diff --git a/CHANGES b/CHANGES index 5bb2003..6158e36 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,151 @@ +CHANGES FROM ACTION 1.51 to ACTION 1.52 + +o Support for friendly-fire in teamplay added. IF YOU ARE NOT ALREADY +USING THE "NO FRIENDLY FIRE" FLAG IN YOUR DMFLAGS, YOU NEED TO ADD IT NOW +IF YOU DON'T WANT TO USE FRIENDLY FIRE. The value for "no friendly fire" +is 256. For a normal AQ teamplay game, you should set your dmflags to 256 +in your server.cfg ("set dmflags 256"). I believe the only other dmflag +that would have a worthwhile effect on AQ teamplay is the "spawn farthest" +flag (512), no others should be set unless you want the spawn farthest +behavior. So...if you want FF in teamplay, set your dmflags to 0. If you +don't, set it to 256. No friendly fire is the "normal" style of play, so +the default/standard dmflags for AQ teamplay should be considered to be +256. When FF is in effect, notification of it will be on the standard +MOTD. Temporary banning for people who kill too many team members is +available. The "maxteamkills" server cvar can be used to control the +maximum number of teamkills that a player can make on a given map before +he is kicked and temporarily banned, the default is 0 which means never +temporarily ban. The "tkbanrounds" and "twbanrounds" variables affect ban +time, see the README file for complete info. Players lose one frag for +killing a teammate. Thanks go to Eek and Azerov for providing the patch +which we used as a basis for this feature. + +o Shot placement on crouching players is much improved and should +basically be consistent with standing players now. Also, the view height +of crouching players has been modified somewhat so it's more consistent +with the actual amount of height loss that occurs. (thanks go to Mikael +Lindh from AQ:Gangsters for lots of help in this area.) + +o The shotgun and handcannon now inform you who you're hitting, when you +hit someone, much like all the other weapons already did. + +o New server variable "limchasecam" now available for clan match usage. +See info in the README. When the chase cam limits are in effect, a line +will be added to the MOTD stating it. (thanks to Black Cross for +submitting the patch for this feature.) + +o Breakable glass now added. Two new server variables for this: +"breakableglass" and "glassfragmentlimit". This is not recommended for +Internet games (much like the ejecting shells, blood splats, and bullet +holes). This was contributed by William van der Sterren of the CGF +project. It requires maps that are designed with breakable glass. For +more info on how to make breakable glass in maps, see +http://www.botepidemic.com/aid/cgf/exp_glass.shtml. For more info on the +server variables, see the README. + +o Door kicking added. You can now jump-kick a door open when it opens in +the opposite direction. If there is a player sitting on the other side of +the door, that player will be kicked along with the door. This was +contributed by AQDT (written by Hal, sent to us by Homer). + +o Vertical component of kicking has returned. You can kick people upwards +or downwards by looking in that direction before kicking them. + +o New commands "use throwing combat knife" and "use slashing combat knife" +allow you to pull out the knife in whichever mode you want, instead of +pulling out your knife then switching modes. + +o A new item has been added to the secondary teamplay scoreboard: damage +dealt. This number is the amount of damage the given player has inflicted +upon other players on the current map. Note this is total damage, not +just damage required to kill someone. For example, a point blank +handcannon blast can count for a lot more than 100 health, even though it +only takes 100 to kill someone. This isn't in the scoreboard if you have +turned on the "noscore" option. + +o New "sv nextmap" command allows a server operator to skip to the next +map in the rotation (thanks go to Black Cross for submitting the patch for +this command). + +o The "spawn farthest" dmflag now works on teamplay games. The second +team will spawn as far away from the first team as possible (unlike the +normal behavior, where the farthest 1-4 spawn candidates are considered, +depending on the total number of candidates). + +o The "needpass" variable is now provided. This allows things like +GameSpy to prompt you for a password when you try to connect to a +passworded server. + +o Players can no longer use say_team when not on a team, or say_partner +when they have no partner. + +o Delayed-death attribution bug fixed (you wouldn't get credit for a kill +when someone bled for a while). + +o Various reloading changes: switching from a reloading weapon to a new +weapon will not cause the new weapon to be reloaded any longer. The +reloading "queue" will immediately empty now whenever a player tries to +shoot (assuming he has any ammo left in the gun to shoot) or bandage. Your +pistol will no longer begin to reload after you get a sniper rifle or +shotgun kicked from your hands while reloading it. The "reload" command +no longer does anything during the "lights, camera, action" countdown in a +teamplay game. You can no longer reload the pistols, M4's, or MP5's when +you've already got a full magazine (to be consistent with the way the +other weapons work). + +o Switching between slashing and throwing for knives now gives a message. + +o Increased knife slashing damage. + +o Repeating "Out of ammo" message bug fixed. + +o Lasersight now falls in the correct place if you're using one of the +"classic" hand modes. + +o Problem with sniper rifle's "weapon" command/bolt animation fixed (you +could fire faster than normal using the right aliases). + +o Dropped grenades now do the same amount of damage as thrown grenades. + +o Green flash when players enter the game in a teamplay game removed (for +real this time), as well as the red flash when a player exits the game in +a teamplay game. + +o The through-eyes chasecam now properly tracks weapon kick (ie with the +M4), and also now gives you IR vision if the person you're viewing through +the eyes of is using IR. + +o A few bugs fixed in the way bodies fly after death. Floating bodies and +bouncing bodies should be gone. + +o Spectators can no longer drop a knife upon entering a teamplay game. + +o Dead bodies no longer appear red when using IR vision. + +o %-vars (ie %W, %H, and the others) are no longer parsed within players' +names (ie a player named %W won't appear as his weapon when he says +something). + +o Minor bug in bleeding from grenades fixed. + +o Bug where sniper rifle icon would be stuck on your screen if you were +zoomed in when the other team all quit and you got "Not enough players to +play!" is fixed. + +o Adjusted spacing in MOTD so certain combinations don't cause lines to go +off the edge of the screen. + +o Shotgun shell ejection animation (when using "shelloff 0") improved. + +o Fixed crash that occurred when a server ran a huge amount of maps in the +rotation (around 100 or more). + +o ReadConfigFile() now closes its file and SelectFarTeamplaySpawnPoint() +frees its memory. + +o Updated Action web page URL in MOTD. + + CHANGES FROM ACTION 1.5 TO ACTION 1.51 o Now remembers how many times reload key was pressed. diff --git a/LICENSE.TXT b/LICENSE.TXT index 6d93dc2..06d4c1d 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -1,91 +1,91 @@ - LIMITED PROGRAM SOURCE CODE LICENSE - - This Limited Program Source Code License (the "Agreement") is between Id Software, Inc., a Texas corporation, (hereinafter "Id Software") and Licensee (as defined below) and is made effective beginning on the date you, the Licensee, download the Code, as defined below, (the "Effective Date"). BY DOWNLOADING THE CODE, AS DEFINED BELOW, YOU, THE LICENSEE, AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU SHOULD READ THIS AGREEMENT CAREFULLY BEFORE DOWNLOADING THE CODE. EVERY PERSON IN POSSESSION OF AN AUTHORIZED COPY, AS DEFINED BELOW, OF THE CODE SHALL BE SUBJECT TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. - - R E C I T A L S - -WHEREAS, Id Software is the owner and developer of the computer software program source code accompanied by this Agreement (the "Code"); - -WHEREAS, Id Software desires to license certain limited non-exclusive rights regarding the Code to Licensee; and - -WHEREAS, Licensee desires to receive a limited license for such rights. - - T E R M S A N D C O N D I T I O N S - - NOW, THEREFORE, for and in consideration of the mutual premises contained herein and for other good and valuable consideration, the receipt and sufficiency of which is hereby acknowledged, the undersigned parties do hereby agree as follows: - -1. Definitions. The parties hereto agree the following definitions shall apply to this Agreement: - - a. "Authorized Copy" shall mean a copy of the Code obtained by Authorized Means, as defined below. A copy of the Code is not an "Authorized Copy" unless it is accompanied by a copy of this Agreement and obtained by Authorized Means. A Modified Copy, as defined below, is not an Authorized Copy; - - b. "Authorized Means" shall mean obtaining an Authorized Copy only by downloading the Authorized Copy from Id Software's Internet web site or from another web site authorized or approved by Id Software for such purposes or by obtaining an Authorized Copy by electronic means via the Internet; - - c. "Code" shall mean the computer software program source code which accompanies this Agreement and includes Code included within any Modified Copy and which is the code that constitutes the Authorized Copy; - - d. "Game" shall mean QUAKE II; - - e. "Licensee" shall mean you, the person who is in possession of an Authorized Copy by Authorized Means; and - - f. "Modified Copy" shall mean a copy of the Code first obtained by Authorized Means which is subsequently modified by Licensee, as provided in paragraph 2. below. - -2. Grant of Rights. Subject to the terms and provisions of this Agreement, Id Software hereby grants to Licensee and Licensee hereby accepts, a limited, world-wide (except as otherwise provided herein), non-exclusive, non-transferable, and non-assignable license to: (i) use the Authorized Copy and the Modified Copy, as defined above, for the development by Licensee of extra levels operable with the Game (the "Extra Levels"); (ii) incorporate all or a portion of the Authorized Copy and the Modified Copy within the Extra Levels; (iii) distribute by way of a sublicense limited by the terms of this Agreement, free of charge and at no cost, the Authorized Copy and the Modified Copy to the extent such Modified Copy and such Authorized Copy, or a portion thereof, is included within the Extra Levels; (iv) distribute by way of a sublicense limited by the terms of this Agreement, free of charge and at no cost, by electronic transmission via the Internet only the Authorized Copy without any alteration or modification along with a copy of this Agreement which must always accompany the Authorized Copy; (v) modify the Authorized Copy in order to create a Modified Copy, as defined above; and (vi) distribute the Modified Copy by way of a sublicense limited by the terms of this Agreement, free of charge and at no cost, by electronic transmission via the Internet only. Each person or entity who/which receives a copy of the Code shall be subject to the terms of this Agreement but, no rights are granted to any person or entity who/which obtains, receives, or is in possession of any copy of the Code by other than Authorized Means. - -3. Reservation of Rights and Prohibitions. Id Software expressly reserves all rights not granted herein. Licensee shall not make any use of the trademarks relating to the Game or Id Software (the "Trademarks"). Any use by Licensee of the Authorized Copy or the Modified Copy not expressly permitted in paragraph 2. above is expressly prohibited and any such unauthorized use shall constitute a material breach of this Agreement by Licensee. Any use of the Code, whether included within a Modified Copy or otherwise, and/or the Authorized Copy not permitted in this Agreement shall constitute an infringement or violation of Id Software's copyright in the Code. Licensee shall not copy, reproduce, manufacture or distribute (free of charge or otherwise) the Authorized Copy or the Modified Copy in any tangible media, including, without limitation, a CD ROM. Licensee shall not commercially exploit by sale, lease, rental or otherwise the Authorized Copy or the Modified Copy whether included within Extra Levels or otherwise. Licensee shall not commercially exploit by sale, lease, rental or otherwise any Extra Levels developed by the use of the Code, whether in whole or in part. Licensee is not receiving any rights hereunder regarding the Game, the Trademarks or any audio-visual elements, artwork, sound, music, images, characters, or other element of the Game. Licensee may modify the Authorized Copy in order to create a Modified Copy, as noted above, but all sublicensees who receive the Modified Copy shall not receive any rights to commercially exploit or to make any other use of the Code included therein except the right to use such Code for such sublicensee's personal entertainment. By way of example and not exclusion, a sublicensee for the Modified Copy shall not further modify the Code within the Modified Copy. Only the Licensee who obtains the Code by Authorized Means shall be permitted to modify such Code on the terms as described in this Agreement. - -4. Additional Obligations. In addition to the obligations of Licensee otherwise set forth in this Agreement, during the Term, and thereafter where specified, Licensee agrees that: - - a. Licensee will not attack or challenge the ownership by Id Software of the Code, the Authorized Copy, the Game, the Trademarks, or any copyright, patent or trademark or other intellectual property right related thereto and Licensee will not attack or challenge the validity of the license granted hereunder during the Term or thereafter; and - - b. Licensee will promptly inform Id Software of any unauthorized use of the Code, the Authorized Copy, the Trademarks, or the Game, or any portions thereof, and will reasonably assist Id Software in the enforcement of all rights Id Software may have against such unauthorized users. - -5. Ownership. Title to and all ownership rights in and to the Code, whether included within the Modified Copy, the Authorized Copy or otherwise, the Game, the Authorized Copy, and the Trademarks and the copyrights, trade secrets, trademarks, patents and all other intellectual property rights related thereto shall remain with Id Software which shall have the exclusive right to protect the same by copyright or otherwise. Licensee shall have no ownership rights in or to the Game, the Code, the Authorized Copy or the Trademarks. Licensee acknowledges that Licensee, by this Agreement, is only receiving a limited license to use the Authorized Copy, as specified in paragraph 2. of this Agreement. - -6. Compliance with Applicable Laws. In exercising Licensee's limited rights hereunder, Licensee shall comply with all applicable laws, [including, without limitation, 22 U.S.C., section 2778 and 22 U.S.C. C.F.R. Parts 120-130 (1995)] regulations, ordinances and statutes, including, but not limited to, the import/export laws and regulations of the United States and its governmental and regulatory agencies (including, without limitation, the Bureau of Export Administration and the U.S. Department of Commerce) and all applicable international treaties and laws. - -7. Term and Termination. - - a. The term of this Agreement and the license granted herein begins on the Effective Date and shall expire, without notice, on a date one (1) calendar year from the Effective Date (the "Term"). - - b. Either party may terminate this Agreement, for any reason or no reason, on thirty (30) days written notice to the other party. Termination will be effective on the thirtieth (30th) day following delivery of the notice of termination. Notwithstanding anything to the contrary herein, this Agreement shall immediately terminate, without the requirement of any notice from Id Software to Licensee, upon the occurrence of any of the following "Terminating Events": (i) if Licensee files a petition in bankruptcy; (ii) if Licensee makes an assignment for the benefit of creditors; (iii) if any bankruptcy proceeding or assignment for benefit of creditors is commenced against Licensee and not dismissed within sixty (60) days after the date of its commencement; (iv) the insolvency of Licensee; or (v) a breach, whether material or otherwise, of this Agreement by Licensee. Upon the occurrence of a Terminating Event, this Agreement and any and all rights hereunder shall terminate without prejudice to any rights or claims Id Software may have, and all rights granted hereunder shall revert, without notice, to and be vested in Id Software. - - c. Termination or expiration of this Agreement shall not create any liability against Id Software and shall not relieve Licensee from any liability which arises prior to termination or expiration. Upon expiration or earlier termination of this Agreement, Licensee shall have no further right to exercise the rights licensed hereunder or otherwise acquired in relation to this Agreement. - -8. Licensee's Warranties. Licensee warrants and represents that: (i) Licensee has full legal rights and authority to enter into and become bound by the terms of this Agreement; (ii) Licensee has full legal rights and authority to perform Licensee?s obligations hereunder; (iii) Licensee will comply, at all times during the Term, with all applicable laws, as set forth hereinabove; (iv) all modifications which Licensee performs on the Code in order to create the Modified Copy and all non-Id Software property included within Extra Levels shall not infringe against or misappropriate any third party rights, including, without limitation, copyrights and trade secrets; and (v) the use or non-use of all modifications which Licensee performs on the Code in order to create the Modified Copy and all non-Id Software property included within Extra Levels shall not infringe against or misappropriate any third party rights, including, without limitation, copyrights and trade secrets. - -9. Indemnification. Licensee hereby agrees to indemnify, hold harmless and defend Id Software and Id Software's predecessors, successors, assigns, officers, directors, shareholders, employees, agents, representatives, licensees (but not including Licensee), sublicensees, distributors, attorneys and accountants (collectively, the "Id Related Parties") from and against any and all "Claims", which shall mean all damages, claims, losses, causes of action, liabilities, lawsuits, judgments and expenses (including, without limitation, reasonable attorneys' fees and expenses) arising from, relating to or in connection with (i) a breach of this Agreement by Licensee and/or (ii) Licensee's use or non-use of the Code, whether the Authorized Copy or whether a portion of the Code as may be included within the Modified Copy or within Extra Levels. Id Software agrees to notify Licensee of any such Claims within a reasonable time after Id Software learns of same. Licensee, at its own expense, shall defend Id Software and the Id Related Parties from and against any and all Claims. Id Software and the Id Related Parties reserve the right to participate in any defense of the Claims with counsel of their choice, and at their own expense. In the event Licensee fails to provide a defense, then Licensee shall be responsible for paying the attorneys' fees and expenses incurred by Id Software and the Id Related Parties regarding the defense of the Claims. Id Software and the Id Related Parties, as applicable, agree to reasonably assist in the defense of the Claims. No settlement by Licensee of any Claims shall be valid unless Licensee receives the prior written consent of Id Software and the Id Related Parties, as applicable, to any such settlement, with consent may be withheld in Id Software's and the Id Related Parties' sole discretion. - -10. Limitation of Liability. UNDER NO CIRCUMSTANCES SHALL ID SOFTWARE BE LIABLE TO LICENSEE FOR ACTUAL, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES OR ANY OTHER DAMAGES, WHETHER OR NOT ID SOFTWARE RECEIVES NOTICE OF ANY SUCH DAMAGES. - -11. Disclaimer of Warranties. ID SOFTWARE EXPRESSLY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH REGARD TO THE CODE, THE AUTHORIZED COPY AND OTHERWISE. - -12. Goodwill. Licensee recognizes the great value of the goodwill associated with the Game and the Trademarks, and acknowledges that such goodwill, now existing and hereafter created, exclusively belongs to Id Software and that the Trademarks have acquired a secondary meaning in the mind of the public. - -13. Remedies. In the event of a breach of this Agreement by Id Software, Licensee's sole remedy shall be to terminate this Agreement by delivering written notice of termination to Id Software. In the event of a breach by Licensee of this Agreement, Id Software may pursue the remedies to which Id Software is entitled under applicable law and this Agreement. Licensee agrees that Licensee's unauthorized use of the Authorized Copy would immediately and irreparably damage Id Software, and in the event of such threatened or actual unauthorized use, Id Software shall be entitled to an injunctive order appropriately restraining and/or prohibiting such unauthorized use without the necessity of Id Software posting bond or other security. Pursuit of any remedy by Id Software shall not constitute a waiver of any other right or remedy of Id Software under this Agreement or under applicable law. - -14. Choice of Law, Venue and Service of Process. This Agreement shall be construed in accordance with the laws of the State of Texas and applicable United States federal law and all claims and/or lawsuits in connection with this Agreement must be brought in Dallas County, Texas where exclusive venue shall lie. Licensee hereby agrees that service of process by certified mail to the address set forth below, with return receipt requested, shall constitute valid service of process upon Licensee. If for any reason Licensee has moved or cannot be validly served, then Licensee appoints the Secretary of State of the state of Texas to accept service of process on Licensee's behalf. - -15. Delivery of Notices. Unless otherwise directed in writing by the parties, all notices given hereunder shall be sent to the last known address of addressee. All notices, requests, consents and other communications under this Agreement shall be in writing and shall be deemed to have been delivered on the date personally delivered or on the date deposited in the United States Postal Service, postage prepaid, by certified mail, return receipt requested, or telegraphed and confirmed, or delivered by electronic facsimile and confirmed. Any notice to Id Software shall also be sent to its counsel: D. Wade Cloud, Jr., Hiersche, Martens, Hayward, Drakeley & Urbach, P.C., 15303 Dallas Parkway, Suite 700, LB 17, Dallas, Texas 75248. - -16. No Partnership, Etc. This Agreement does not constitute and shall not be construed as constituting a partnership or joint venture between Id Software and Licensee. Neither party shall have any right to obligate or bind the other party in any manner whatsoever, and nothing herein contained shall give, or is intended to give, any rights of any kind to any third persons. - -17. Entire agreement. This Agreement constitutes the entire understanding between Licensee and Id Software regarding the subject matter hereof. Each and every clause of this Agreement is severable from the whole and shall survive unless the entire Agreement is declared unenforceable. No prior or present agreements or representations between the parties hereto regarding the subject matter hereof shall be binding upon the parties hereto unless incorporated in this Agreement. No modification or change in this Agreement shall be valid or binding upon the parties hereto unless in writing and executed by the parties to be bound thereby. - -18. Assignment. This Agreement shall bind and inure to the benefit of Id Software, its successors and assigns, and Id Software may assign its rights hereunder, in Id Software's sole discretion. This Agreement is personal to Licensee, and Licensee shall not assign, transfer, convey nor franchise its rights granted hereunder. As provided above, Licensee may sublicense Licensee's limited rights herein by transferring the Authorized Copy by Authorized Means. As noted, each sublicensee in possession of a copy of the Authorized Copy shall be subject to the terms and conditions of this Agreement. - -19. Survival. The following provisions shall survive the expiration or earlier termination of this Agreement: paragraphs 5., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 19., 20.a. and 20.b. - -20. Miscellaneous. - - a. All captions in this Agreement are intended solely for the convenience of the parties, and none shall effect the meaning or construction of any provision. - - b. The terms and conditions of this Agreement have been negotiated fully and freely among the parties. Accordingly, the preparation of this Agreement by counsel for a given party will not be material to the construction hereof, and the terms of this Agreement shall not be strictly construed against such party. - -BY DOWNLOADING THE CODE, AS DEFINED ABOVE, YOU, THE LICENSEE, AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT. - - -February 12, 1998 - - -LIMITED PROGRAM SOURCE CODE LICENSE PAGE 1 - - + LIMITED PROGRAM SOURCE CODE LICENSE + + This Limited Program Source Code License (the "Agreement") is between Id Software, Inc., a Texas corporation, (hereinafter "Id Software") and Licensee (as defined below) and is made effective beginning on the date you, the Licensee, download the Code, as defined below, (the "Effective Date"). BY DOWNLOADING THE CODE, AS DEFINED BELOW, YOU, THE LICENSEE, AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU SHOULD READ THIS AGREEMENT CAREFULLY BEFORE DOWNLOADING THE CODE. EVERY PERSON IN POSSESSION OF AN AUTHORIZED COPY, AS DEFINED BELOW, OF THE CODE SHALL BE SUBJECT TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. + + R E C I T A L S + +WHEREAS, Id Software is the owner and developer of the computer software program source code accompanied by this Agreement (the "Code"); + +WHEREAS, Id Software desires to license certain limited non-exclusive rights regarding the Code to Licensee; and + +WHEREAS, Licensee desires to receive a limited license for such rights. + + T E R M S A N D C O N D I T I O N S + + NOW, THEREFORE, for and in consideration of the mutual premises contained herein and for other good and valuable consideration, the receipt and sufficiency of which is hereby acknowledged, the undersigned parties do hereby agree as follows: + +1. Definitions. The parties hereto agree the following definitions shall apply to this Agreement: + + a. "Authorized Copy" shall mean a copy of the Code obtained by Authorized Means, as defined below. A copy of the Code is not an "Authorized Copy" unless it is accompanied by a copy of this Agreement and obtained by Authorized Means. A Modified Copy, as defined below, is not an Authorized Copy; + + b. "Authorized Means" shall mean obtaining an Authorized Copy only by downloading the Authorized Copy from Id Software's Internet web site or from another web site authorized or approved by Id Software for such purposes or by obtaining an Authorized Copy by electronic means via the Internet; + + c. "Code" shall mean the computer software program source code which accompanies this Agreement and includes Code included within any Modified Copy and which is the code that constitutes the Authorized Copy; + + d. "Game" shall mean QUAKE II; + + e. "Licensee" shall mean you, the person who is in possession of an Authorized Copy by Authorized Means; and + + f. "Modified Copy" shall mean a copy of the Code first obtained by Authorized Means which is subsequently modified by Licensee, as provided in paragraph 2. below. + +2. Grant of Rights. Subject to the terms and provisions of this Agreement, Id Software hereby grants to Licensee and Licensee hereby accepts, a limited, world-wide (except as otherwise provided herein), non-exclusive, non-transferable, and non-assignable license to: (i) use the Authorized Copy and the Modified Copy, as defined above, for the development by Licensee of extra levels operable with the Game (the "Extra Levels"); (ii) incorporate all or a portion of the Authorized Copy and the Modified Copy within the Extra Levels; (iii) distribute by way of a sublicense limited by the terms of this Agreement, free of charge and at no cost, the Authorized Copy and the Modified Copy to the extent such Modified Copy and such Authorized Copy, or a portion thereof, is included within the Extra Levels; (iv) distribute by way of a sublicense limited by the terms of this Agreement, free of charge and at no cost, by electronic transmission via the Internet only the Authorized Copy without any alteration or modification along with a copy of this Agreement which must always accompany the Authorized Copy; (v) modify the Authorized Copy in order to create a Modified Copy, as defined above; and (vi) distribute the Modified Copy by way of a sublicense limited by the terms of this Agreement, free of charge and at no cost, by electronic transmission via the Internet only. Each person or entity who/which receives a copy of the Code shall be subject to the terms of this Agreement but, no rights are granted to any person or entity who/which obtains, receives, or is in possession of any copy of the Code by other than Authorized Means. + +3. Reservation of Rights and Prohibitions. Id Software expressly reserves all rights not granted herein. Licensee shall not make any use of the trademarks relating to the Game or Id Software (the "Trademarks"). Any use by Licensee of the Authorized Copy or the Modified Copy not expressly permitted in paragraph 2. above is expressly prohibited and any such unauthorized use shall constitute a material breach of this Agreement by Licensee. Any use of the Code, whether included within a Modified Copy or otherwise, and/or the Authorized Copy not permitted in this Agreement shall constitute an infringement or violation of Id Software's copyright in the Code. Licensee shall not copy, reproduce, manufacture or distribute (free of charge or otherwise) the Authorized Copy or the Modified Copy in any tangible media, including, without limitation, a CD ROM. Licensee shall not commercially exploit by sale, lease, rental or otherwise the Authorized Copy or the Modified Copy whether included within Extra Levels or otherwise. Licensee shall not commercially exploit by sale, lease, rental or otherwise any Extra Levels developed by the use of the Code, whether in whole or in part. Licensee is not receiving any rights hereunder regarding the Game, the Trademarks or any audio-visual elements, artwork, sound, music, images, characters, or other element of the Game. Licensee may modify the Authorized Copy in order to create a Modified Copy, as noted above, but all sublicensees who receive the Modified Copy shall not receive any rights to commercially exploit or to make any other use of the Code included therein except the right to use such Code for such sublicensee's personal entertainment. By way of example and not exclusion, a sublicensee for the Modified Copy shall not further modify the Code within the Modified Copy. Only the Licensee who obtains the Code by Authorized Means shall be permitted to modify such Code on the terms as described in this Agreement. + +4. Additional Obligations. In addition to the obligations of Licensee otherwise set forth in this Agreement, during the Term, and thereafter where specified, Licensee agrees that: + + a. Licensee will not attack or challenge the ownership by Id Software of the Code, the Authorized Copy, the Game, the Trademarks, or any copyright, patent or trademark or other intellectual property right related thereto and Licensee will not attack or challenge the validity of the license granted hereunder during the Term or thereafter; and + + b. Licensee will promptly inform Id Software of any unauthorized use of the Code, the Authorized Copy, the Trademarks, or the Game, or any portions thereof, and will reasonably assist Id Software in the enforcement of all rights Id Software may have against such unauthorized users. + +5. Ownership. Title to and all ownership rights in and to the Code, whether included within the Modified Copy, the Authorized Copy or otherwise, the Game, the Authorized Copy, and the Trademarks and the copyrights, trade secrets, trademarks, patents and all other intellectual property rights related thereto shall remain with Id Software which shall have the exclusive right to protect the same by copyright or otherwise. Licensee shall have no ownership rights in or to the Game, the Code, the Authorized Copy or the Trademarks. Licensee acknowledges that Licensee, by this Agreement, is only receiving a limited license to use the Authorized Copy, as specified in paragraph 2. of this Agreement. + +6. Compliance with Applicable Laws. In exercising Licensee's limited rights hereunder, Licensee shall comply with all applicable laws, [including, without limitation, 22 U.S.C., section 2778 and 22 U.S.C. C.F.R. Parts 120-130 (1995)] regulations, ordinances and statutes, including, but not limited to, the import/export laws and regulations of the United States and its governmental and regulatory agencies (including, without limitation, the Bureau of Export Administration and the U.S. Department of Commerce) and all applicable international treaties and laws. + +7. Term and Termination. + + a. The term of this Agreement and the license granted herein begins on the Effective Date and shall expire, without notice, on a date one (1) calendar year from the Effective Date (the "Term"). + + b. Either party may terminate this Agreement, for any reason or no reason, on thirty (30) days written notice to the other party. Termination will be effective on the thirtieth (30th) day following delivery of the notice of termination. Notwithstanding anything to the contrary herein, this Agreement shall immediately terminate, without the requirement of any notice from Id Software to Licensee, upon the occurrence of any of the following "Terminating Events": (i) if Licensee files a petition in bankruptcy; (ii) if Licensee makes an assignment for the benefit of creditors; (iii) if any bankruptcy proceeding or assignment for benefit of creditors is commenced against Licensee and not dismissed within sixty (60) days after the date of its commencement; (iv) the insolvency of Licensee; or (v) a breach, whether material or otherwise, of this Agreement by Licensee. Upon the occurrence of a Terminating Event, this Agreement and any and all rights hereunder shall terminate without prejudice to any rights or claims Id Software may have, and all rights granted hereunder shall revert, without notice, to and be vested in Id Software. + + c. Termination or expiration of this Agreement shall not create any liability against Id Software and shall not relieve Licensee from any liability which arises prior to termination or expiration. Upon expiration or earlier termination of this Agreement, Licensee shall have no further right to exercise the rights licensed hereunder or otherwise acquired in relation to this Agreement. + +8. Licensee's Warranties. Licensee warrants and represents that: (i) Licensee has full legal rights and authority to enter into and become bound by the terms of this Agreement; (ii) Licensee has full legal rights and authority to perform Licensee?s obligations hereunder; (iii) Licensee will comply, at all times during the Term, with all applicable laws, as set forth hereinabove; (iv) all modifications which Licensee performs on the Code in order to create the Modified Copy and all non-Id Software property included within Extra Levels shall not infringe against or misappropriate any third party rights, including, without limitation, copyrights and trade secrets; and (v) the use or non-use of all modifications which Licensee performs on the Code in order to create the Modified Copy and all non-Id Software property included within Extra Levels shall not infringe against or misappropriate any third party rights, including, without limitation, copyrights and trade secrets. + +9. Indemnification. Licensee hereby agrees to indemnify, hold harmless and defend Id Software and Id Software's predecessors, successors, assigns, officers, directors, shareholders, employees, agents, representatives, licensees (but not including Licensee), sublicensees, distributors, attorneys and accountants (collectively, the "Id Related Parties") from and against any and all "Claims", which shall mean all damages, claims, losses, causes of action, liabilities, lawsuits, judgments and expenses (including, without limitation, reasonable attorneys' fees and expenses) arising from, relating to or in connection with (i) a breach of this Agreement by Licensee and/or (ii) Licensee's use or non-use of the Code, whether the Authorized Copy or whether a portion of the Code as may be included within the Modified Copy or within Extra Levels. Id Software agrees to notify Licensee of any such Claims within a reasonable time after Id Software learns of same. Licensee, at its own expense, shall defend Id Software and the Id Related Parties from and against any and all Claims. Id Software and the Id Related Parties reserve the right to participate in any defense of the Claims with counsel of their choice, and at their own expense. In the event Licensee fails to provide a defense, then Licensee shall be responsible for paying the attorneys' fees and expenses incurred by Id Software and the Id Related Parties regarding the defense of the Claims. Id Software and the Id Related Parties, as applicable, agree to reasonably assist in the defense of the Claims. No settlement by Licensee of any Claims shall be valid unless Licensee receives the prior written consent of Id Software and the Id Related Parties, as applicable, to any such settlement, with consent may be withheld in Id Software's and the Id Related Parties' sole discretion. + +10. Limitation of Liability. UNDER NO CIRCUMSTANCES SHALL ID SOFTWARE BE LIABLE TO LICENSEE FOR ACTUAL, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES OR ANY OTHER DAMAGES, WHETHER OR NOT ID SOFTWARE RECEIVES NOTICE OF ANY SUCH DAMAGES. + +11. Disclaimer of Warranties. ID SOFTWARE EXPRESSLY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH REGARD TO THE CODE, THE AUTHORIZED COPY AND OTHERWISE. + +12. Goodwill. Licensee recognizes the great value of the goodwill associated with the Game and the Trademarks, and acknowledges that such goodwill, now existing and hereafter created, exclusively belongs to Id Software and that the Trademarks have acquired a secondary meaning in the mind of the public. + +13. Remedies. In the event of a breach of this Agreement by Id Software, Licensee's sole remedy shall be to terminate this Agreement by delivering written notice of termination to Id Software. In the event of a breach by Licensee of this Agreement, Id Software may pursue the remedies to which Id Software is entitled under applicable law and this Agreement. Licensee agrees that Licensee's unauthorized use of the Authorized Copy would immediately and irreparably damage Id Software, and in the event of such threatened or actual unauthorized use, Id Software shall be entitled to an injunctive order appropriately restraining and/or prohibiting such unauthorized use without the necessity of Id Software posting bond or other security. Pursuit of any remedy by Id Software shall not constitute a waiver of any other right or remedy of Id Software under this Agreement or under applicable law. + +14. Choice of Law, Venue and Service of Process. This Agreement shall be construed in accordance with the laws of the State of Texas and applicable United States federal law and all claims and/or lawsuits in connection with this Agreement must be brought in Dallas County, Texas where exclusive venue shall lie. Licensee hereby agrees that service of process by certified mail to the address set forth below, with return receipt requested, shall constitute valid service of process upon Licensee. If for any reason Licensee has moved or cannot be validly served, then Licensee appoints the Secretary of State of the state of Texas to accept service of process on Licensee's behalf. + +15. Delivery of Notices. Unless otherwise directed in writing by the parties, all notices given hereunder shall be sent to the last known address of addressee. All notices, requests, consents and other communications under this Agreement shall be in writing and shall be deemed to have been delivered on the date personally delivered or on the date deposited in the United States Postal Service, postage prepaid, by certified mail, return receipt requested, or telegraphed and confirmed, or delivered by electronic facsimile and confirmed. Any notice to Id Software shall also be sent to its counsel: D. Wade Cloud, Jr., Hiersche, Martens, Hayward, Drakeley & Urbach, P.C., 15303 Dallas Parkway, Suite 700, LB 17, Dallas, Texas 75248. + +16. No Partnership, Etc. This Agreement does not constitute and shall not be construed as constituting a partnership or joint venture between Id Software and Licensee. Neither party shall have any right to obligate or bind the other party in any manner whatsoever, and nothing herein contained shall give, or is intended to give, any rights of any kind to any third persons. + +17. Entire agreement. This Agreement constitutes the entire understanding between Licensee and Id Software regarding the subject matter hereof. Each and every clause of this Agreement is severable from the whole and shall survive unless the entire Agreement is declared unenforceable. No prior or present agreements or representations between the parties hereto regarding the subject matter hereof shall be binding upon the parties hereto unless incorporated in this Agreement. No modification or change in this Agreement shall be valid or binding upon the parties hereto unless in writing and executed by the parties to be bound thereby. + +18. Assignment. This Agreement shall bind and inure to the benefit of Id Software, its successors and assigns, and Id Software may assign its rights hereunder, in Id Software's sole discretion. This Agreement is personal to Licensee, and Licensee shall not assign, transfer, convey nor franchise its rights granted hereunder. As provided above, Licensee may sublicense Licensee's limited rights herein by transferring the Authorized Copy by Authorized Means. As noted, each sublicensee in possession of a copy of the Authorized Copy shall be subject to the terms and conditions of this Agreement. + +19. Survival. The following provisions shall survive the expiration or earlier termination of this Agreement: paragraphs 5., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 19., 20.a. and 20.b. + +20. Miscellaneous. + + a. All captions in this Agreement are intended solely for the convenience of the parties, and none shall effect the meaning or construction of any provision. + + b. The terms and conditions of this Agreement have been negotiated fully and freely among the parties. Accordingly, the preparation of this Agreement by counsel for a given party will not be material to the construction hereof, and the terms of this Agreement shall not be strictly construed against such party. + +BY DOWNLOADING THE CODE, AS DEFINED ABOVE, YOU, THE LICENSEE, AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT. + + +February 12, 1998 + + +LIMITED PROGRAM SOURCE CODE LICENSE PAGE 1 + + diff --git a/Makefile b/Makefile index 71891bc..cb11607 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,8 @@ GAME_OBJS = \ g_monster.o g_phys.o g_save.o g_spawn.o g_svcmds.o \ g_target.o g_trigger.o g_turret.o g_utils.o g_weapon.o g_chase.o \ p_client.o p_hud.o p_trail.o p_view.o p_weapon.o q_shared.o \ - m_move.o a_team.o a_game.o a_items.o a_cmds.o a_radio.o a_menu.o + m_move.o a_team.o a_game.o a_items.o a_cmds.o a_radio.o a_menu.o \ + cgf_sfx_glass.o a_doorkick.o game$(ARCH).$(SHLIBEXT) : $(GAME_OBJS) $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS) @@ -63,7 +64,7 @@ g_ai.o: g_ai.c g_local.h q_shared.h game.h a_team.h a_game.h a_menu.h \ g_cmds.o: g_cmds.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h m_player.h g_combat.o: g_combat.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h g_func.o: g_func.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h g_items.o: g_items.c g_local.h q_shared.h game.h a_team.h a_game.h \ @@ -71,13 +72,13 @@ g_items.o: g_items.c g_local.h q_shared.h game.h a_team.h a_game.h \ g_main.o: g_main.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h g_misc.o: g_misc.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h g_monster.o: g_monster.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h g_phys.o: g_phys.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h g_save.o: g_save.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h g_spawn.o: g_spawn.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h g_svcmds.o: g_svcmds.c g_local.h q_shared.h game.h a_team.h a_game.h \ @@ -91,11 +92,11 @@ g_turret.o: g_turret.c g_local.h q_shared.h game.h a_team.h a_game.h \ g_utils.o: g_utils.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h g_weapon.o: g_weapon.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h g_chase.o: g_chase.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h p_client.o: p_client.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h m_player.h + a_menu.h a_radio.h m_player.h cgf_sfx_glass.h p_hud.o: p_hud.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h p_trail.o: p_trail.c g_local.h q_shared.h game.h a_team.h a_game.h \ @@ -108,9 +109,9 @@ q_shared.o: q_shared.c q_shared.h m_move.o: m_move.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h a_team.o: a_team.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h a_game.o: a_game.c g_local.h q_shared.h game.h a_team.h a_game.h \ - a_menu.h a_radio.h + a_menu.h a_radio.h cgf_sfx_glass.h a_items.o: a_items.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h a_cmds.o: a_cmds.c g_local.h q_shared.h game.h a_team.h a_game.h \ @@ -119,3 +120,7 @@ a_radio.o: a_radio.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h a_menu.o: a_menu.c g_local.h q_shared.h game.h a_team.h a_game.h \ a_menu.h a_radio.h +cgf_sfx_glass.o: cgf_sfx_glass.c g_local.h q_shared.h game.h a_team.h \ + a_game.h a_menu.h a_radio.h cgf_sfx_glass.h +a_doorkick.o: a_doorkick.c g_local.h q_shared.h game.h a_team.h \ + a_game.h a_menu.h a_radio.h diff --git a/README b/README index 654bbf0..694abb1 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -SERVER/PLAYER DOCUMENTATION FOR ACTION 1.51 +SERVER/PLAYER DOCUMENTATION FOR ACTION 1.52 ACTION-SPECIFIC SERVER VARIABLES @@ -56,8 +56,9 @@ allitem: gives all the items to each player in teamplay/DM tgren: sets the number of grenades that come with the bandolier in teamplay (default = 0). -noscore: if set to 1, individual scores are not in effect for teamplay, -the only scores visible will be team wins and total frags (default = 0). +noscore: if set to 1, individual scores (and damage stats) are not in +effect for teamplay, the only scores visible will be team wins and total +frags (default = 0). nohud: if set to 1, the standard HUD (health, ammo, etc) is disabled for all players. This can allow you to record better-looking demos of staged @@ -69,6 +70,33 @@ game directory. This should be used on the Quake2 commandline, ie: quake2 +set game action +set ininame alternate.ini +set dedicated 1 ... (default = "action.ini"). +limchasecam: if set to 1, will prevent all players on teams from free +floating, or chase-camming enemy players. If set to 2, will prevent all +players on teams from using the normal chase cam as well (only the +through-eyes cam on teammates will be allowed). This variable should be +set to 2 for clan matches, etc (default = 0). + +shelloff: turns off the dropping of expended shells from your gun +(default = 1 [turn off shells, for a faster Internet game]). + +breakableglass: turns on breakable glass. Not recommended for Internet +games (default = 0). + +glassfragmentlimit: controls the maximum number of glass fragments present +on the map at any time (default = 30). + +maxteamkills: the maximum number of teammates a player can kill in one map +before he is temporarily banned from the server. Only applies during +friendly-fire enabled teamplay games. Players will also be banned for +wounding teammates, if they wound 4*maxteamkills teammates. Setting this +to zero disables the feature (default = 0). + +tkbanrounds: the number of maps a player will be banned for when he is +banned for killing teammates (default = 2). + +twbanrounds: the number of maps a player will be banned for when he is +banned for wounding teammates (default = 2). + ACTION-SPECIFIC PLAYER COMMANDS @@ -158,7 +186,7 @@ unpartner: breaks your current partnership. WEAPON/ITEM NAMES These are the exact names of all the weapons/items in the game, for use with -commands like "drop", "choose", "give", etc: +commands like "use", "drop", "choose", "give", etc: MK23 Pistol M3 Super 90 Assault Shotgun @@ -167,7 +195,8 @@ Handcannon Sniper Rifle M4 Assault Rifle Dual MK23 Pistols -Combat Knife +Combat Knife (also aliases "throwing combat knife" and + "slashing combat knife" for the "use" command) Pistol Clip 12 Gauge Shells @@ -205,8 +234,11 @@ Flood protection is supported using the standard Q2 3.20 variables, "flood_msgs" (default 4), "flood_persecond" (default 4), and "flood_waitdelay" (default 10). -DM teams-by-model, teams-by-skin, friendly fire, etc are supported using the -standard Q2 "dmflags" values. +DM teams-by-model, teams-by-skin, friendly fire, etc are supported using +the standard Q2 "dmflags" values. A server should use dmflags 256 for +"normal" play (including no friendly fire), 0 for friendly fire. Some of +the other regular dmflags are also available, such as "spawn farthest" +(512). action/action.ini is the configuration file for map rotation and teamplay team name/model/skin setup. See the example for information on the format. @@ -234,10 +266,16 @@ default. IP addresses in the filter list will only be prohibited from connecting if the "filterban" variable is set to 1 (this is the default). +OTHER SERVER COMMANDS + +sv reloadmotd: reloads the MOTD file from disk. + +sv nextmap: immediately skips to the next map in the rotation. + + REPORTING BUGS OR MAKING COMMENTS -As of this writing, you can contact the authors of the Action 1.5 server -code (Zucchini and Fireblade) at spikard@u.washington.edu and -ucs_brf@shsu.edu, respectively. The Action Quake 2 website is at -http://action.telefragged.com/ and has a message board where discussions -about Action Quake 2 take place. +The maintainers of the Action server code, Zucchini and Fireblade, can be +contacted at spikard@u.washington.edu and ucs_brf@shsu.edu, respectively. +The Action Quake 2 website is at http://aq2.action-web.net/ and has a +message board where discussions about Action Quake 2 take place. diff --git a/a_cmds.c b/a_cmds.c index f2f872c..61805c9 100644 --- a/a_cmds.c +++ b/a_cmds.c @@ -108,11 +108,12 @@ void LaserSightThink (edict_t *self) { vec3_t start,end,endp,offset; vec3_t forward,right,up; - vec3_t angles; + vec3_t angles; trace_t tr; + int height = 0; - // zucc compensate for weapon ride up - VectorAdd (self->owner->client->v_angle, self->owner->client->kick_angles, angles); + // zucc compensate for weapon ride up + VectorAdd (self->owner->client->v_angle, self->owner->client->kick_angles, angles); AngleVectors (/*self->owner->client->v_angle*/angles, forward, right, up); @@ -121,13 +122,18 @@ void LaserSightThink (edict_t *self) self->think = G_FreeEdict; } + if (self->owner->client->pers.firing_style == ACTION_FIRING_CLASSIC) + height = 8; - VectorSet(offset,24 , 0, self->owner->viewheight); + + VectorSet(offset,24 , 8, self->owner->viewheight- height); P_ProjectSource (self->owner->client, self->owner->s.origin, offset, forward, right, start); VectorMA(start,8192,forward,end); - tr = do_trace (start,NULL,NULL, end,self->owner,CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); + PRETRACE(); + tr = gi.trace (start,NULL,NULL, end,self->owner,CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); + POSTTRACE(); if (tr.fraction != 1) { VectorMA(tr.endpos,-4,forward,endp); @@ -144,7 +150,12 @@ void LaserSightThink (edict_t *self) void Cmd_New_Reload_f( edict_t *ent ) { - ent->client->reload_attempts++; +//FB 6/1/99 - refuse to reload during LCA + if ((int)teamplay->value && lights_camera_action) + return; +//FB 6/1/99 + + ent->client->reload_attempts++; } @@ -168,15 +179,15 @@ void Cmd_Reload_f (edict_t *ent) || ent->client->bandage_stopped == 1 || ent->client->weaponstate == WEAPON_ACTIVATING || ent->client->weaponstate == WEAPON_DROPPING - || ent->client->weaponstate == WEAPON_FIRING ) + || ent->client->weaponstate == WEAPON_FIRING ) { return; } - - if (!ent->client->fast_reload) - ent->client->reload_attempts--; + + if (!ent->client->fast_reload) + ent->client->reload_attempts--; if ( ent->client->reload_attempts < 0 ) - ent->client->reload_attempts = 0; + ent->client->reload_attempts = 0; //First, grab the current magazine max count... if ( ( ent->client->curr_weap == MK23_NUM ) @@ -198,12 +209,7 @@ void Cmd_Reload_f (edict_t *ent) if(ent->client->pers.inventory[ent->client->ammo_index]) { - /*if((ent->client->weaponstate != WEAPON_END_MAG) && (ent->client->pers.inventory[ent->client->ammo_index] < rds_left)) - { - gi.centerprintf(ent,"Buy a clue-\nYou're on your last magazine!\n"); - } - else*/ - //Set the weaponstate... + //Set the weaponstate... if ( ent->client->curr_weap == M3_NUM ) { if (ent->client->shot_rds >= ent->client->shot_max) @@ -213,17 +219,26 @@ void Cmd_Reload_f (edict_t *ent) // already in the process of reloading! if ( ent->client->weaponstate == WEAPON_RELOADING && (ent->client->shot_rds < (ent->client->shot_max -1)) && !(ent->client->fast_reload) && ((ent->client->pers.inventory[ent->client->ammo_index] -1) > 0 )) { - ent->client->fast_reload = 1; - (ent->client->pers.inventory[ent->client->ammo_index])--; + // don't let them start fast reloading until far enough into the firing sequence + // this gives them a chance to break off from reloading to fire the weapon - zucc + if ( ent->client->ps.gunframe >= 48 ) + { + ent->client->fast_reload = 1; + (ent->client->pers.inventory[ent->client->ammo_index])--; + } + else + { + ent->client->reload_attempts++; + + } } } if ( ent->client->curr_weap == HC_NUM ) { - if (ent->client->cannon_rds >= ent->client->cannon_max) - { - return; - } - + if (ent->client->cannon_rds >= ent->client->cannon_max) + { + return; + } if (!(ent->client->pers.inventory[ent->client->ammo_index] >= 2)) return; @@ -235,22 +250,48 @@ void Cmd_Reload_f (edict_t *ent) return; } // already in the process of reloading! - if ( ent->client->weaponstate == WEAPON_RELOADING && (ent->client->sniper_rds < (ent->client->sniper_max -1)) && !(ent->client->fast_reload) && ((ent->client->pers.inventory[ent->client->ammo_index] -1) > 0 )) + if ( ent->client->weaponstate == WEAPON_RELOADING && (ent->client->sniper_rds < (ent->client->sniper_max -1)) && !(ent->client->fast_reload) && ((ent->client->pers.inventory[ent->client->ammo_index] -1) > 0 ) ) { - ent->client->fast_reload = 1; - (ent->client->pers.inventory[ent->client->ammo_index])--; + // don't let them start fast reloading until far enough into the firing sequence + // this gives them a chance to break off from reloading to fire the weapon - zucc + if ( ent->client->ps.gunframe >= 72 ) + { + ent->client->fast_reload = 1; + (ent->client->pers.inventory[ent->client->ammo_index])--; + } + else + { + ent->client->reload_attempts++; + } } ent->client->ps.fov = 90; if ( ent->client->pers.weapon ) ent->client->ps.gunindex = gi.modelindex( ent->client->pers.weapon->view_model ); - } if ( ent->client->curr_weap == DUAL_NUM ) { if (!(ent->client->pers.inventory[ent->client->ammo_index] >= 2)) return; +//FIREBLADE 7/11/1999 - stop reloading when weapon already full + if (ent->client->dual_rds == ent->client->dual_max) + return; } - + if (ent->client->curr_weap == MP5_NUM) + { + if (ent->client->mp5_rds == ent->client->mp5_max) + return; + } + if (ent->client->curr_weap == M4_NUM) + { + if (ent->client->m4_rds == ent->client->m4_max) + return; + } + if (ent->client->curr_weap == MK23_NUM) + { + if (ent->client->mk23_rds == ent->client->mk23_max) + return; + } +//FIREBLADE ent->client->weaponstate = WEAPON_RELOADING; //(ent->client->pers.inventory[ent->client->ammo_index])--; @@ -261,6 +302,14 @@ void Cmd_Reload_f (edict_t *ent) } //+BD END CODE BLOCK +void Cmd_New_Weapon_f( edict_t *ent ) +{ + ent->client->weapon_attempts++; + if ( ent->client->weapon_attempts == 1 ) + Cmd_Weapon_f(ent); +} + + // function to change the firing mode of weapons (when appropriate) void Cmd_Weapon_f ( edict_t *ent ) @@ -269,13 +318,26 @@ void Cmd_Weapon_f ( edict_t *ent ) dead = (ent->solid == SOLID_NOT || ent->deadflag == DEAD_DEAD); - if ( ent->client->bandaging || ent->client->bandage_stopped ) + ent->client->weapon_attempts--; + if ( ent->client->weapon_attempts < 0 ) + ent->client->weapon_attempts = 0; + + if ( ent->client->bandaging || ent->client->bandage_stopped ) { - gi.cprintf(ent, PRINT_HIGH, "You can't mess with your weapon while bandaging!\n"); - return; + + gi.cprintf(ent, PRINT_HIGH, "You'll get to your weapon when your done bandaging!\n"); + ent->client->weapon_attempts++; + return; } - if ( ent->client->curr_weap == MK23_NUM ) + if ( ent->client->weaponstate == WEAPON_FIRING || ent->client->weaponstate == WEAPON_BUSY ) + { + //gi.cprintf(ent, PRINT_HIGH, "Try again when you aren't using your weapon.\n"); + ent->client->weapon_attempts++; + return; + } + + if ( ent->client->curr_weap == MK23_NUM ) { if (!dead) gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/click.wav"), 1, ATTN_NORM, 0); @@ -309,15 +371,18 @@ void Cmd_Weapon_f ( edict_t *ent ) if ( ent->client->curr_weap == SNIPER_NUM ) { if (dead) - return; + return; if ( ent->client->resp.sniper_mode == SNIPER_1X ) { - gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/lensflik.wav"), 1, ATTN_NORM, 0); - ent->client->weaponstate = WEAPON_BUSY; + gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/lensflik.wav"), 1, ATTN_NORM, 0); ent->client->resp.sniper_mode = SNIPER_2X; ent->client->desired_fov = 45; - ent->client->idle_weapon = 6; // 6 frames of idleness - ent->client->ps.gunframe = 22; + if ( ent->client->weaponstate != WEAPON_RELOADING ) + { + ent->client->idle_weapon = 6; // 6 frames of idleness + ent->client->ps.gunframe = 22; + ent->client->weaponstate = WEAPON_BUSY; + } } else if ( ent->client->resp.sniper_mode == SNIPER_2X ) { @@ -350,9 +415,15 @@ void Cmd_Weapon_f ( edict_t *ent ) ent->client->resp.knife_mode = !(ent->client->resp.knife_mode); ent->client->weaponstate = WEAPON_ACTIVATING; if ( ent->client->resp.knife_mode ) - ent->client->ps.gunframe = 0; + { + gi.cprintf(ent, PRINT_HIGH, "Switching to throwing\n"); + ent->client->ps.gunframe = 0; + } else - ent->client->ps.gunframe = 106; + { + gi.cprintf(ent, PRINT_HIGH, "Switching to slashing\n"); + ent->client->ps.gunframe = 106; + } } } @@ -390,8 +461,15 @@ void Cmd_OpenDoor_f (edict_t *ent ) void Cmd_Bandage_f ( edict_t *ent ) { - gitem_t *item; - if ( (ent->client->weaponstate == WEAPON_READY || ent->client->weaponstate == WEAPON_END_MAG ) + gitem_t *item; + + + if ( (ent->client->bleeding != 0 || ent->client->leg_damage != 0) && ent->client->bandaging != 1 ) + ent->client->reload_attempts = 0; // prevent any further reloading + + + + if ( (ent->client->weaponstate == WEAPON_READY || ent->client->weaponstate == WEAPON_END_MAG ) && (ent->client->bleeding != 0 || ent->client->leg_damage != 0 ) && ent->client->bandaging != 1 ) { @@ -401,22 +479,22 @@ void Cmd_Bandage_f ( edict_t *ent ) if ( ent->client->curr_weap == GRENADE_NUM && ( ( ent->client->ps.gunframe >= GRENADE_IDLE_FIRST && ent->client->ps.gunframe <= GRENADE_IDLE_LAST ) - || ( ent->client->ps.gunframe >= GRENADE_THROW_FIRST - && ent->client->ps.gunframe <= GRENADE_THROW_LAST ) ) ) + || ( ent->client->ps.gunframe >= GRENADE_THROW_FIRST + && ent->client->ps.gunframe <= GRENADE_THROW_LAST ) ) ) { ent->client->ps.gunframe = 0; fire_grenade2 (ent, ent->s.origin, tv(0,0,0), GRENADE_DAMRAD, 0, 2, GRENADE_DAMRAD*2, false); - item = FindItem(GRENADE_NAME); - ent->client->pers.inventory[ITEM_INDEX(item)]--; - if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 ) - { - ent->client->newweapon = FindItem( MK23_NAME ); + item = FindItem(GRENADE_NAME); + ent->client->pers.inventory[ITEM_INDEX(item)]--; + if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 ) + { + ent->client->newweapon = FindItem( MK23_NAME ); - } - } + } + } ent->client->bandaging = 1; - ent->client->resp.sniper_mode = SNIPER_1X; + ent->client->resp.sniper_mode = SNIPER_1X; ent->client->ps.fov = 90; ent->client->desired_fov = 90; if ( ent->client->pers.weapon ) @@ -438,16 +516,16 @@ void Bandage( edict_t* ent ) { ent->client->leg_noise = 0; ent->client->leg_damage = 0; - ent->client->leghits = 0; - ent->client->bleeding = 0; + ent->client->leghits = 0; + ent->client->bleeding = 0; ent->client->bleed_remain = 0; // ent->client->bleedcount = 0; // ent->client->bleeddelay = 0; - ent->client->bandaging = 0; - ent->client->leg_dam_count = 0; - ent->client->attacker = NULL; - ent->client->bandage_stopped = 1; - ent->client->idle_weapon = BANDAGE_TIME; + ent->client->bandaging = 0; + ent->client->leg_dam_count = 0; + ent->client->attacker = NULL; + ent->client->bandage_stopped = 1; + ent->client->idle_weapon = BANDAGE_TIME; } @@ -503,7 +581,9 @@ qboolean loc_CanSee (edict_t *targ, edict_t *inflictor) viewpoint[2] += inflictor->viewheight; for (i = 0; i < 8; i++) { - trace = do_trace (viewpoint, vec3_origin, vec3_origin, targpoints[i], inflictor, MASK_SOLID); + PRETRACE(); + trace = gi.trace (viewpoint, vec3_origin, vec3_origin, targpoints[i], inflictor, MASK_SOLID); + POSTTRACE(); if (trace.fraction == 1.0) return true; } @@ -521,7 +601,7 @@ void SetIDView(edict_t *ent) //FIREBLADE, suggested by hal[9k] 3/11/1999 float bd = 0.9; //FIREBLADE - float d; + float d; int i; ent->client->ps.stats[STAT_ID_VIEW] = 0; @@ -552,7 +632,9 @@ void SetIDView(edict_t *ent) AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorScale(forward, 8192, forward); VectorAdd(ent->s.origin, forward, forward); - tr = do_trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID); + PRETRACE(); + tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID); + POSTTRACE(); if (tr.fraction < 1 && tr.ent && tr.ent->client) { ent->client->ps.stats[STAT_ID_VIEW] = CS_PLAYERSKINS + (ent - g_edicts - 1); diff --git a/a_doorkick.c b/a_doorkick.c new file mode 100644 index 0000000..8f699f8 --- /dev/null +++ b/a_doorkick.c @@ -0,0 +1,135 @@ +/* a_doorkick.c + * Door kicking code by hal[9k] + * originally for AQ:Espionage (http://aqdt.fear.net/) + * email: hal9000@telefragged.com + * Assembled here by Homer (homer@fear.net) + */ + +#include "g_local.h" + +#define STATE_TOP 0 +#define STATE_BOTTOM 1 +#define STATE_UP 2 +#define STATE_DOWN 3 + +#define DOOR_START_OPEN 1 +#define DOOR_REVERSE 2 + +extern void door_use (edict_t *self, edict_t *other, edict_t *activator); + +// needed for KickDoor +void VectorRotate(vec3_t in, vec3_t angles, vec3_t out) +{ + float cv, sv, angle, tv; + + VectorCopy(in, out); + + angle = (-angles[PITCH]) * M_PI / 180; + cv = cos(angle); + sv = sin(angle); + tv = (out[0] * cv) - (out[2] * sv); + + out[2] = (out[2] * cv) + (out[0] * sv); + out[0] = tv; + + angle = (angles[YAW]) * M_PI / 180; + + cv = cos(angle); + sv = sin(angle); + tv = (out[0] * cv) - (out[1] * sv); + + out[1] = (out[1] * cv) + (out[0] * sv); + out[0] = tv; + angle = (angles[ROLL]) * M_PI / 180; + + cv = cos(angle); + sv = sin(angle); + tv = (out[1] * cv) - (out[2] * sv); + out[2] = (out[2] * cv) + (out[1] * sv); + out[1] = tv; +} + +int KickDoor( trace_t *tr_old, edict_t *ent, vec3_t forward ) +{ + trace_t tr; + + vec3_t d_forward, right, end; + float d; + if ( !Q_strcasecmp( tr_old->ent->classname, "func_door_rotating" ) ) + { + // Make that the door is closed + + tr = *tr_old; +#if 1 + if ( (!(tr.ent->spawnflags & DOOR_START_OPEN) && + !(tr.ent->moveinfo.state == STATE_TOP)) || + ( (tr.ent->spawnflags & DOOR_START_OPEN) && + !(tr.ent->moveinfo.state == STATE_BOTTOM)) ) +#else + if ( (!(tr.ent->spawnflags & DOOR_START_OPEN) && + ((tr.ent->moveinfo.state == STATE_BOTTOM) || + (tr.ent->moveinfo.state == STATE_DOWN))) || + ((tr.ent->spawnflags & DOOR_START_OPEN) && + ((tr.ent->moveinfo.state == STATE_TOP) || + (tr.ent->moveinfo.state == STATE_UP))) ) +#endif + { + //gi.dprintf( "Kicking a closed door\n" ); + + // Find out if we are on the "outside" + +#if 0 + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_RAILTRAIL); + gi.WritePosition (tr.ent->s.origin); + gi.WritePosition (tr.endpos); + gi.multicast (tr.ent->s.origin, MULTICAST_PHS); +#endif + VectorSubtract( tr.endpos, tr.ent->s.origin, d_forward ); + + forward[2] = 0; + d_forward[2] = 0; + VectorNormalize( forward ); + VectorNormalize( d_forward ); + VectorSet( right, 0, 90, 0 ); + VectorRotate( d_forward, right, d_forward ); + + d = DotProduct( forward, d_forward ); + if ( tr.ent->spawnflags & DOOR_REVERSE ) + d = -d; + // d = sin( acos( d ) ); + if ( d > 0.0 ) + { + // gi.dprintf( "we think we are on the outside\n" ); + //if ( tr.ent->spawnflags & DOOR_REVERSE ) + // gi.dprintf( "but DOOR_REVERSE is set\n" ); + // Only use the door if it's not already opening + if ( (!( tr.ent->spawnflags & DOOR_START_OPEN ) && + !( tr.ent->moveinfo.state == STATE_UP )) || + ((tr.ent->spawnflags & DOOR_START_OPEN ) && + (tr.ent->moveinfo.state == STATE_DOWN) ) ) + door_use( tr.ent, ent, ent ); + // Find out if someone else is on the other side + VectorMA( tr.endpos, 25, forward, end ); + PRETRACE(); + tr = gi.trace (tr.endpos, NULL, NULL, end, tr.ent, MASK_SHOT); + POSTTRACE(); + if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) + { + if (tr.fraction < 1.0) + { + if (tr.ent->client) + { + //gi.dprintf("we found a client on the other side\n"); + *tr_old = tr; + return( 1 ); + } + } + } + } + + } + } + + return( 0 ); +} diff --git a/a_game.c b/a_game.c index c9b7426..c88ebf2 100644 --- a/a_game.c +++ b/a_game.c @@ -7,6 +7,7 @@ */ #include "g_local.h" +#include "cgf_sfx_glass.h" #define MAX_MAP_ROTATION 1000 // just in case... #define MAX_STR_LEN 1000 @@ -34,15 +35,15 @@ void ReadConfigFile() FILE *config_file; char buf[MAX_STR_LEN]; char reading_section[MAX_STR_LEN]; - char inipath[MAX_STR_LEN]; + char inipath[MAX_STR_LEN]; int lines_into_section = -1; - cvar_t *ininame; + cvar_t *ininame; - ininame = gi.cvar("ininame", "action.ini", 0); - if (ininame->string && *(ininame->string)) - sprintf(inipath, "%s/%s", GAMEVERSION, ininame->string); - else - sprintf(inipath, "%s/%s", GAMEVERSION, "action.ini"); + ininame = gi.cvar("ininame", "action.ini", 0); + if (ininame->string && *(ininame->string)) + sprintf(inipath, "%s/%s", GAMEVERSION, ininame->string); + else + sprintf(inipath, "%s/%s", GAMEVERSION, "action.ini"); config_file = fopen(inipath, "r"); if (config_file == NULL) @@ -119,6 +120,8 @@ void ReadConfigFile() sprintf(team1_skin_index, "../players/%s_i", team1_skin); sprintf(team2_skin_index, "../players/%s_i", team2_skin); cur_map = 0; + + fclose(config_file); } void ReadMOTDFile() @@ -167,147 +170,165 @@ void PrintMOTD(edict_t *ent) // 3 lines for version number & website (third blank) strcpy(msg_buf, "Welcome to Action Quake v" ACTION_VERSION "\n" - "http://action.telefragged.com/\n" - "\n"); + "http://aq2.action-web.net/\n" + "\n"); lines = 3; - if (!skipmotd->value) - { - // Line for server hostname, if set - if (hostname->string && strlen(hostname->string) && strcmp(hostname->string, "unnamed")) - { - //strcat(msg_buf, "Running at: "); - strcat(msg_buf, hostname->string); - strcat(msg_buf, "\n"); - lines++; - } + if (!skipmotd->value) + { + // Line for server hostname, if set + if (hostname->string && strlen(hostname->string) && strcmp(hostname->string, "unnamed")) + { + //strcat(msg_buf, "Running at: "); + strcat(msg_buf, hostname->string); + strcat(msg_buf, "\n"); + lines++; + } - /* - * Server-specific settings information - */ - - // Line for game type - if (teamplay->value) - { - server_type = "teamplay"; - } - else - { - if ((int)dmflags->value & DF_MODELTEAMS) - server_type = "deathmatch (teams by model)"; - else if ((int)dmflags->value & DF_SKINTEAMS) - server_type = "deathmatch (teams by skin)"; - else - server_type = "deathmatch (no teams)"; - } - sprintf(msg_buf + strlen(msg_buf), - "Game type: %s\n", - server_type); - lines++; - - // Line for frag and time limit - if ((int)fraglimit->value) - sprintf(msg_buf + strlen(msg_buf), "Frag limit: %d", (int)fraglimit->value); - else - strcat(msg_buf, "Frag limit: none"); - if ((int)timelimit->value) - sprintf(msg_buf + strlen(msg_buf), " Time limit: %d\n", (int)timelimit->value); - else - strcat(msg_buf, " Time limit: none\n"); - lines++; + /* + * Server-specific settings information + */ + + // Line for game type + if (teamplay->value) + { + server_type = "teamplay"; + } + else + { + if ((int)dmflags->value & DF_MODELTEAMS) + server_type = "deathmatch (teams by model)"; + else if ((int)dmflags->value & DF_SKINTEAMS) + server_type = "deathmatch (teams by skin)"; + else + server_type = "deathmatch (no teams)"; + } + sprintf(msg_buf + strlen(msg_buf), + "Game type: %s\n", + server_type); + lines++; + + // Line for frag and time limit + if ((int)fraglimit->value) + sprintf(msg_buf + strlen(msg_buf), "Frag limit: %d", (int)fraglimit->value); + else + strcat(msg_buf, "Frag limit: none"); + if ((int)timelimit->value) + sprintf(msg_buf + strlen(msg_buf), " Time limit: %d\n", (int)timelimit->value); + else + strcat(msg_buf, " Time limit: none\n"); + lines++; - // Teamplay: 3 lines for round limit/round time limit, and menu instructions - if (teamplay->value) - { - if ((int)roundlimit->value) - sprintf(msg_buf + strlen(msg_buf), "Round limit: %d", (int)roundlimit->value); - else - strcat(msg_buf, "Round limit: none"); - if ((int)roundtimelimit->value) - sprintf(msg_buf + strlen(msg_buf), " Round time limit: %d\n", (int)roundtimelimit->value); - else - strcat(msg_buf, " Round time limit: none\n"); - lines++; - } - - // Check for weird unique items/weapons settings, and inform with a line - if ((int)unique_weapons->value != 1 || - (int)unique_items->value != 1) - { - sprintf(msg_buf + strlen(msg_buf), "Max carry of special weaps: %d items: %d\n", - (int)unique_weapons->value, (int)unique_items->value); - lines++; - } - if (tgren->value > 0 || !(ir->value)) - { - char grenade_num[32]; - if (tgren->value > 0) - sprintf(grenade_num, "%d grenade%s", - (int)tgren->value, - (int)tgren->value == 1 ? "" : "s"); - sprintf(msg_buf + strlen(msg_buf), "Bandolier w/ %s%s%s\n", - !(ir->value) ? "no IR" : "", - (tgren->value > 0 && !(ir->value)) ? " & " : "", - tgren->value > 0 ? grenade_num : ""); - lines++; - } - if (allitem->value || allweapon->value) - { - sprintf(msg_buf + strlen(msg_buf), "Players receive %s%s%s\n", - allweapon->value ? "all weapons" : "", - (allweapon->value && allitem->value) ? " & " : "", - allitem->value ? "all items" : ""); - lines++; - } - - // Teamplay: 2 lines (one blank) for menu instructions - if (teamplay->value) - { - strcat(msg_buf, "\nHit tab to open the team selection menu\n"); - lines += 2; - } - - /* - * If actionmaps, put a blank line then the maps list - */ - - // hopefully no one will run enough maps to exceed the line limit, if so, oh well... -FB - if (actionmaps->value && num_maps > 0) - { - int chars_on_line, len_mr; - - strcat(msg_buf, "\nRunning the following maps in order:\n"); - lines += 2; - - chars_on_line = 0; - for (mapnum = 0; mapnum < num_maps; mapnum++) - { - len_mr = strlen(*(map_rotation + mapnum)); - if ((chars_on_line + len_mr + 2) > 39) - { - strcat(msg_buf, "\n"); - lines++; - chars_on_line = 0; - } - strcat(msg_buf, *(map_rotation + mapnum)); - chars_on_line += len_mr; - if (mapnum < (num_maps - 1)) - { - strcat(msg_buf, ", "); - chars_on_line += 2; - } - } - - strcat(msg_buf, "\n"); - lines++; - } - - if (motd_num_lines) + // Teamplay: 3 lines for round limit/round time limit, and menu instructions + if (teamplay->value) + { + if ((int)roundlimit->value) + sprintf(msg_buf + strlen(msg_buf), "Round limit: %d", (int)roundlimit->value); + else + strcat(msg_buf, "Round limit: none"); + if ((int)roundtimelimit->value) + sprintf(msg_buf + strlen(msg_buf), " Round time limit: %d\n", (int)roundtimelimit->value); + else + strcat(msg_buf, " Round time limit: none\n"); + lines++; + } + + // Check for weird unique items/weapons settings, and inform with a line + if ((int)unique_weapons->value != 1 || + (int)unique_items->value != 1) + { + sprintf(msg_buf + strlen(msg_buf), "Max carry of special weaps: %d items: %d\n", + (int)unique_weapons->value, (int)unique_items->value); + lines++; + } + if (tgren->value > 0 || !(ir->value)) + { + char grenade_num[32]; + if (tgren->value > 0) + sprintf(grenade_num, "%d grenade%s", + (int)tgren->value, + (int)tgren->value == 1 ? "" : "s"); + sprintf(msg_buf + strlen(msg_buf), "Bandolier w/ %s%s%s\n", + !(ir->value) ? "no IR" : "", + (tgren->value > 0 && !(ir->value)) ? " & " : "", + tgren->value > 0 ? grenade_num : ""); + lines++; + } + if (allitem->value || allweapon->value) + { + sprintf(msg_buf + strlen(msg_buf), "Players receive %s%s%s\n", + allweapon->value ? "all weapons" : "", + (allweapon->value && allitem->value) ? " & " : "", + allitem->value ? "all items" : ""); + lines++; + } + if (limchasecam->value) { - strcat(msg_buf, "\n"); - lines++; + if ((int)limchasecam->value == 2) + sprintf(msg_buf + strlen(msg_buf), "Chase cam disallowed\n"); + else + sprintf(msg_buf + strlen(msg_buf), "Chase cam restricted\n"); + lines++; } - } + if (teamplay->value && !((int)dmflags->value & DF_NO_FRIENDLY_FIRE)) + { + sprintf(msg_buf + strlen(msg_buf), "Friendly fire enabled\n"); + lines++; + } + + // Teamplay: 2 lines (one blank) for menu instructions + if (teamplay->value) + { + strcat(msg_buf, "\nHit tab to open the team selection menu\n"); + lines += 2; + } + + /* + * If actionmaps, put a blank line then the maps list + */ + + // hopefully no one will run enough maps to exceed the line limit, if so, oh well... -FB + if (actionmaps->value && num_maps > 0) + { + int chars_on_line, len_mr; + + strcat(msg_buf, "\nRunning the following maps in order:\n"); + lines += 2; + + chars_on_line = 0; + for (mapnum = 0; mapnum < num_maps; mapnum++) + { + len_mr = strlen(*(map_rotation + mapnum)); + if ((chars_on_line + len_mr + 2) > 39) + { + strcat(msg_buf, "\n"); + lines++; + if (lines >= max_lines) + break; + chars_on_line = 0; + } + strcat(msg_buf, *(map_rotation + mapnum)); + chars_on_line += len_mr; + if (mapnum < (num_maps - 1)) + { + strcat(msg_buf, ", "); + chars_on_line += 2; + } + } + + if (lines < max_lines) + { + strcat(msg_buf, "\n"); + lines++; + } + } + + if (motd_num_lines && lines < max_lines) + { + strcat(msg_buf, "\n"); + lines++; + } + } /* * Insert action/motd.txt contents (whole MOTD gets truncated after 30 lines) @@ -357,49 +378,49 @@ int splats = 0; void BlooderDie(edict_t *self) { - G_FreeEdict(self); + G_FreeEdict(self); } void BlooderTouch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { - G_FreeEdict(self); + G_FreeEdict(self); } - + void EjectBlooder (edict_t *self, vec3_t start, vec3_t veloc ) { - edict_t *blooder; - vec3_t forward; - int spd = 0; + edict_t *blooder; + vec3_t forward; + int spd = 0; - blooder = G_Spawn(); + blooder = G_Spawn(); - VectorCopy (veloc, forward); + VectorCopy (veloc, forward); - VectorCopy (start , blooder->s.origin); + VectorCopy (start , blooder->s.origin); - spd = 0; - + spd = 0; + - VectorScale (forward, spd, blooder->velocity); - - blooder->solid = SOLID_NOT; - blooder->movetype = MOVETYPE_TOSS; + VectorScale (forward, spd, blooder->velocity); + + blooder->solid = SOLID_NOT; + blooder->movetype = MOVETYPE_TOSS; - blooder->s.modelindex = gi.modelindex("sprites/null.sp2"); - blooder->s.effects |= EF_GIB; + blooder->s.modelindex = gi.modelindex("sprites/null.sp2"); + blooder->s.effects |= EF_GIB; - blooder->owner = self; - blooder->touch = BlooderTouch; - blooder->nextthink = level.time + 3.2; - blooder->think = BlooderDie; - blooder->classname = "blooder"; + blooder->owner = self; + blooder->touch = BlooderTouch; + blooder->nextthink = level.time + 3.2; + blooder->think = BlooderDie; + blooder->classname = "blooder"; - gi.linkentity (blooder); + gi.linkentity (blooder); } @@ -411,19 +432,19 @@ void EjectBlooder (edict_t *self, vec3_t start, vec3_t veloc ) void ShellTouch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { - if (self->owner->client->curr_weap == M3_NUM) - gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/shellhit1.wav"), 1, ATTN_STATIC, 0); - else if (random() < 0.5) - gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_STATIC, 0); - else - gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink2.wav"), 1, ATTN_STATIC, 0); + if (self->owner->client->curr_weap == M3_NUM) + gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/shellhit1.wav"), 1, ATTN_STATIC, 0); + else if (random() < 0.5) + gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_STATIC, 0); + else + gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink2.wav"), 1, ATTN_STATIC, 0); } void ShellDie(edict_t *self) { - G_FreeEdict(self); - - --shells; + G_FreeEdict(self); + + --shells; } @@ -434,236 +455,236 @@ void ShellDie(edict_t *self) void EjectShell (edict_t *self, vec3_t start, int toggle ) { - edict_t *shell; - vec3_t forward, right, up; - float r; - float fix = 1.0; - int left = 0; + edict_t *shell; + vec3_t forward, right, up; + float r; + float fix = 1.0; + int left = 0; - if (sv_shelloff->value) - return; - - shell = G_Spawn(); - ++shells; + if (sv_shelloff->value) + return; + + shell = G_Spawn(); + ++shells; - AngleVectors (self->client->v_angle, forward, right, up); + AngleVectors (self->client->v_angle, forward, right, up); - if (self->client->pers.hand == LEFT_HANDED) - { - left = 1; - fix = -1.0; - } - else if ( self->client->pers.hand == CENTER_HANDED) - fix = 0; + if (self->client->pers.hand == LEFT_HANDED) + { + left = 1; + fix = -1.0; + } + else if ( self->client->pers.hand == CENTER_HANDED) + fix = 0; - // zucc spent a fair amount of time hacking these until they look ok, - // several of them could be improved however. + // zucc spent a fair amount of time hacking these until they look ok, + // several of them could be improved however. - if (self->client->curr_weap == MK23_NUM ) - { - VectorMA (start, left?-7:.4, right, start); - VectorMA (start, left?5:2, forward, start); - VectorMA (start, left?-10:-8 , up, start); - } - else if (self->client->curr_weap == M4_NUM) - { - VectorMA (start, left?-10:5, right, start); - VectorMA (start, left?6:12, forward, start); - VectorMA (start, left?-9:-11, up, start); - } - else if (self->client->curr_weap == MP5_NUM) - { - VectorMA (start, left?-10:6, right, start); - VectorMA (start, left?6:8, forward, start); - VectorMA (start, left?-9:-10, up, start); - } - else if (self->client->curr_weap == SNIPER_NUM) - { - VectorMA (start, fix*11, right, start); - VectorMA (start, 2, forward, start); - VectorMA (start, -11, up, start); - - } - else if (self->client->curr_weap == M3_NUM) - { - VectorMA(start, left?-9:3, right, start); - VectorMA(start, left?4:4, forward, start); - VectorMA(start, left?-1:-1, up, start); - } - - else if (self->client->curr_weap == DUAL_NUM) - { - if (self->client->pers.hand == LEFT_HANDED) - VectorMA (start, ((toggle==1)?8:-8), right, start); - else - VectorMA (start, ((toggle==1)?-4:4), right, start); - VectorMA (start, 6, forward, start); - VectorMA (start, -9, up, start); - - } + if (self->client->curr_weap == MK23_NUM ) + { + VectorMA (start, left?-7:.4, right, start); + VectorMA (start, left?5:2, forward, start); + VectorMA (start, left?-10:-8 , up, start); + } + else if (self->client->curr_weap == M4_NUM) + { + VectorMA (start, left?-10:5, right, start); + VectorMA (start, left?6:12, forward, start); + VectorMA (start, left?-9:-11, up, start); + } + else if (self->client->curr_weap == MP5_NUM) + { + VectorMA (start, left?-10:6, right, start); + VectorMA (start, left?6:8, forward, start); + VectorMA (start, left?-9:-10, up, start); + } + else if (self->client->curr_weap == SNIPER_NUM) + { + VectorMA (start, fix*11, right, start); + VectorMA (start, 2, forward, start); + VectorMA (start, -11, up, start); + + } + else if (self->client->curr_weap == M3_NUM) + { + VectorMA(start, left?-9:3, right, start); + VectorMA(start, left?4:4, forward, start); + VectorMA(start, left?-1:-1, up, start); + } + + else if (self->client->curr_weap == DUAL_NUM) + { + if (self->client->pers.hand == LEFT_HANDED) + VectorMA (start, ((toggle==1)?8:-8), right, start); + else + VectorMA (start, ((toggle==1)?-4:4), right, start); + VectorMA (start, 6, forward, start); + VectorMA (start, -9, up, start); + + } - if ( (forward[2] >= -1) && (forward[2] < -0.99) ) { - VectorMA (start, 5, forward, start); - VectorMA (start, -0.5, up, start); } + if ( (forward[2] >= -1) && (forward[2] < -0.99) ) { + VectorMA (start, 5, forward, start); + VectorMA (start, -0.5, up, start); } - else if ( (forward[2] >= -0.99) && (forward[2] < -0.98) ) { - VectorMA (start, 5, forward, start); - VectorMA (start, -.1, up, start); } - else if ( (forward[2] >= -0.98) && (forward[2] < -0.97) ) { - VectorMA (start, 5.1, forward, start); - VectorMA (start, 0.3, up, start); } - else if ( (forward[2] >= -0.97) && (forward[2] < -0.96) ) { - VectorMA (start, 5.2, forward, start); - VectorMA (start, 0.7, up, start); } - else if ( (forward[2] >= -0.96) && (forward[2] < -0.95) ) { - VectorMA (start, 5.2, forward, start); - VectorMA (start, 1.1, up, start); } - else if ( (forward[2] >= -0.95) && (forward[2] < -0.94) ) { - VectorMA (start, 5.3, forward, start); - VectorMA (start, 1.5, up, start); } - else if ( (forward[2] >= -0.94) && (forward[2] < -0.93) ) { - VectorMA (start, 5.4, forward, start); - VectorMA (start, 1.9, up, start); } - else if ( (forward[2] >= -0.93) && (forward[2] < -0.92) ) { - VectorMA (start, 5.5, forward, start); - VectorMA (start, 2.3, up, start); } - else if ( (forward[2] >= -0.92) && (forward[2] < -0.91) ) { - VectorMA (start, 5.6, forward, start); - VectorMA (start, 2.7, up, start); } - else if ( (forward[2] >= -0.91) && (forward[2] < -0.9) ) { - VectorMA (start, 5.7, forward, start); - VectorMA (start, 3.1, up, start); } + else if ( (forward[2] >= -0.99) && (forward[2] < -0.98) ) { + VectorMA (start, 5, forward, start); + VectorMA (start, -.1, up, start); } + else if ( (forward[2] >= -0.98) && (forward[2] < -0.97) ) { + VectorMA (start, 5.1, forward, start); + VectorMA (start, 0.3, up, start); } + else if ( (forward[2] >= -0.97) && (forward[2] < -0.96) ) { + VectorMA (start, 5.2, forward, start); + VectorMA (start, 0.7, up, start); } + else if ( (forward[2] >= -0.96) && (forward[2] < -0.95) ) { + VectorMA (start, 5.2, forward, start); + VectorMA (start, 1.1, up, start); } + else if ( (forward[2] >= -0.95) && (forward[2] < -0.94) ) { + VectorMA (start, 5.3, forward, start); + VectorMA (start, 1.5, up, start); } + else if ( (forward[2] >= -0.94) && (forward[2] < -0.93) ) { + VectorMA (start, 5.4, forward, start); + VectorMA (start, 1.9, up, start); } + else if ( (forward[2] >= -0.93) && (forward[2] < -0.92) ) { + VectorMA (start, 5.5, forward, start); + VectorMA (start, 2.3, up, start); } + else if ( (forward[2] >= -0.92) && (forward[2] < -0.91) ) { + VectorMA (start, 5.6, forward, start); + VectorMA (start, 2.7, up, start); } + else if ( (forward[2] >= -0.91) && (forward[2] < -0.9) ) { + VectorMA (start, 5.7, forward, start); + VectorMA (start, 3.1, up, start); } - else if ( (forward[2] >= -0.9) && (forward[2] < -0.85) ) { - VectorMA (start, 5.8, forward, start); - VectorMA (start, 3.5, up, start); } - else if ( (forward[2] >= -0.85) && (forward[2] < -0.8) ) { - VectorMA (start, 6, forward, start); - VectorMA (start, 4, up, start); } - else if ( (forward[2] >= -0.8) && (forward[2] < -0.6) ) { - VectorMA (start, 6.5, forward, start); - VectorMA (start, 4.5, up , start); } - else if ( (forward[2] >= -0.6) && (forward[2] < -0.4) ) { - VectorMA (start, 8, forward, start); - VectorMA (start, 5.5, up , start); } - else if ( (forward[2] >= -0.4) && (forward[2] < -0.2) ) { - VectorMA (start, 9.5, forward, start); - VectorMA (start, 6, up , start); } - else if ( (forward[2] >= -0.2) && (forward[2] < 0) ) { - VectorMA (start, 11, forward, start); - VectorMA (start, 6.5, up , start); } - else if ( (forward[2] >= 0) && (forward[2] < 0.2) ) { - VectorMA (start, 12, forward, start); - VectorMA (start, 7, up, start); } - else if ( (forward[2] >= 0.2) && (forward[2] < 0.4) ) { - VectorMA (start, 14, forward, start); - VectorMA (start, 6.5, up, start); } - else if ( (forward[2] >= 0.4) && (forward[2] < 0.6) ) { - VectorMA (start, 16, forward, start); - VectorMA (start, 6, up, start); } - else if ( (forward[2] >= 0.6) && (forward[2] < 0.8) ) { - VectorMA (start, 18, forward, start); - VectorMA (start, 5, up, start); } - else if ( (forward[2] >= 0.8) && (forward[2] < 0.85) ) { - VectorMA (start, 18, forward, start); - VectorMA (start, 4, up, start); } - else if ( (forward[2] >= 0.85) && (forward[2] < 0.9) ) { - VectorMA (start, 18, forward, start); - VectorMA (start, 2.5, up, start); } + else if ( (forward[2] >= -0.9) && (forward[2] < -0.85) ) { + VectorMA (start, 5.8, forward, start); + VectorMA (start, 3.5, up, start); } + else if ( (forward[2] >= -0.85) && (forward[2] < -0.8) ) { + VectorMA (start, 6, forward, start); + VectorMA (start, 4, up, start); } + else if ( (forward[2] >= -0.8) && (forward[2] < -0.6) ) { + VectorMA (start, 6.5, forward, start); + VectorMA (start, 4.5, up , start); } + else if ( (forward[2] >= -0.6) && (forward[2] < -0.4) ) { + VectorMA (start, 8, forward, start); + VectorMA (start, 5.5, up , start); } + else if ( (forward[2] >= -0.4) && (forward[2] < -0.2) ) { + VectorMA (start, 9.5, forward, start); + VectorMA (start, 6, up , start); } + else if ( (forward[2] >= -0.2) && (forward[2] < 0) ) { + VectorMA (start, 11, forward, start); + VectorMA (start, 6.5, up , start); } + else if ( (forward[2] >= 0) && (forward[2] < 0.2) ) { + VectorMA (start, 12, forward, start); + VectorMA (start, 7, up, start); } + else if ( (forward[2] >= 0.2) && (forward[2] < 0.4) ) { + VectorMA (start, 14, forward, start); + VectorMA (start, 6.5, up, start); } + else if ( (forward[2] >= 0.4) && (forward[2] < 0.6) ) { + VectorMA (start, 16, forward, start); + VectorMA (start, 6, up, start); } + else if ( (forward[2] >= 0.6) && (forward[2] < 0.8) ) { + VectorMA (start, 18, forward, start); + VectorMA (start, 5, up, start); } + else if ( (forward[2] >= 0.8) && (forward[2] < 0.85) ) { + VectorMA (start, 18, forward, start); + VectorMA (start, 4, up, start); } + else if ( (forward[2] >= 0.85) && (forward[2] < 0.9) ) { + VectorMA (start, 18, forward, start); + VectorMA (start, 2.5, up, start); } - else if ( (forward[2] >= 0.9) && (forward[2] < 0.91) ) { - VectorMA (start, 18.2, forward, start); - VectorMA (start, 2.2, up, start); } - else if ( (forward[2] >= 0.91) && (forward[2] < 0.92) ) { - VectorMA (start, 18.4, forward, start); - VectorMA (start, 1.9, up, start); } - else if ( (forward[2] >= 0.92) && (forward[2] < 0.93) ) { - VectorMA (start, 18.6, forward, start); - VectorMA (start, 1.6, up, start); } - else if ( (forward[2] >= 0.93) && (forward[2] < 0.94) ) { - VectorMA (start, 18.8, forward, start); - VectorMA (start, 1.3, up, start); } - else if ( (forward[2] >= 0.94) && (forward[2] < 0.95) ) { - VectorMA (start, 19, forward, start); - VectorMA (start, 1, up, start); } - else if ( (forward[2] >= 0.95) && (forward[2] < 0.96) ) { - VectorMA (start, 19.2, forward, start); - VectorMA (start, 0.7, up, start); } - else if ( (forward[2] >= 0.96) && (forward[2] < 0.97) ) { - VectorMA (start, 19.4, forward, start); - VectorMA (start, 0.4, up, start); } - else if ( (forward[2] >= 0.97) && (forward[2] < 0.98) ) { - VectorMA (start, 19.6, forward, start); - VectorMA (start, -0.2, up, start); } - else if ( (forward[2] >= 0.98) && (forward[2] < 0.99) ) { - VectorMA (start, 19.8, forward, start); - VectorMA (start, -0.6, up, start); } + else if ( (forward[2] >= 0.9) && (forward[2] < 0.91) ) { + VectorMA (start, 18.2, forward, start); + VectorMA (start, 2.2, up, start); } + else if ( (forward[2] >= 0.91) && (forward[2] < 0.92) ) { + VectorMA (start, 18.4, forward, start); + VectorMA (start, 1.9, up, start); } + else if ( (forward[2] >= 0.92) && (forward[2] < 0.93) ) { + VectorMA (start, 18.6, forward, start); + VectorMA (start, 1.6, up, start); } + else if ( (forward[2] >= 0.93) && (forward[2] < 0.94) ) { + VectorMA (start, 18.8, forward, start); + VectorMA (start, 1.3, up, start); } + else if ( (forward[2] >= 0.94) && (forward[2] < 0.95) ) { + VectorMA (start, 19, forward, start); + VectorMA (start, 1, up, start); } + else if ( (forward[2] >= 0.95) && (forward[2] < 0.96) ) { + VectorMA (start, 19.2, forward, start); + VectorMA (start, 0.7, up, start); } + else if ( (forward[2] >= 0.96) && (forward[2] < 0.97) ) { + VectorMA (start, 19.4, forward, start); + VectorMA (start, 0.4, up, start); } + else if ( (forward[2] >= 0.97) && (forward[2] < 0.98) ) { + VectorMA (start, 19.6, forward, start); + VectorMA (start, -0.2, up, start); } + else if ( (forward[2] >= 0.98) && (forward[2] < 0.99) ) { + VectorMA (start, 19.8, forward, start); + VectorMA (start, -0.6, up, start); } - else if ( (forward[2] >= 0.99) && (forward[2] <= 1) ) { - VectorMA (start, 20, forward, start); - VectorMA (start, -1, up , start); } + else if ( (forward[2] >= 0.99) && (forward[2] <= 1) ) { + VectorMA (start, 20, forward, start); + VectorMA (start, -1, up , start); } - VectorCopy (start , shell->s.origin); - if (fix == 0) // we want some velocity on those center handed ones - fix = 1; - if (self->client->curr_weap == SNIPER_NUM) - VectorMA (shell->velocity, fix*(-35 + random() * -60), right, shell->velocity); - else if ( self->client->curr_weap == DUAL_NUM) - { - if (self->client->pers.hand == LEFT_HANDED) - VectorMA (shell->velocity, (toggle==1?1:-1)*(35 + random() * 60), right, shell->velocity); - else - VectorMA (shell->velocity, (toggle==1?-1:1)*(35 + random() * 60), right, shell->velocity); - } - else - VectorMA (shell->velocity, fix*(35 + random() * 60), right, shell->velocity); - VectorMA (shell->avelocity, 500, right, shell->avelocity); - if (self->client->curr_weap == SNIPER_NUM) - VectorMA (shell->velocity, 60 + 40, up, shell->velocity); - else - VectorMA (shell->velocity, 60 + random() * 90, up, shell->velocity); + VectorCopy (start , shell->s.origin); + if (fix == 0) // we want some velocity on those center handed ones + fix = 1; + if (self->client->curr_weap == SNIPER_NUM) + VectorMA (shell->velocity, fix*(-35 + random() * -60), right, shell->velocity); + else if ( self->client->curr_weap == DUAL_NUM) + { + if (self->client->pers.hand == LEFT_HANDED) + VectorMA (shell->velocity, (toggle==1?1:-1)*(35 + random() * 60), right, shell->velocity); + else + VectorMA (shell->velocity, (toggle==1?-1:1)*(35 + random() * 60), right, shell->velocity); + } + else + VectorMA (shell->velocity, fix*(35 + random() * 60), right, shell->velocity); + VectorMA (shell->avelocity, 500, right, shell->avelocity); + if (self->client->curr_weap == SNIPER_NUM) + VectorMA (shell->velocity, 60 + 40, up, shell->velocity); + else + VectorMA (shell->velocity, 60 + random() * 90, up, shell->velocity); - shell->movetype = MOVETYPE_BOUNCE; - shell->solid = SOLID_BBOX; + shell->movetype = MOVETYPE_BOUNCE; + shell->solid = SOLID_BBOX; - if (self->client->curr_weap == M3_NUM) - shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris2.md2"); - else if (self->client->curr_weap == SNIPER_NUM) - shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris3.md2"); - else - shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris.md2"); + if (self->client->curr_weap == M3_NUM) + shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris2.md2"); + else if (self->client->curr_weap == SNIPER_NUM) + shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris3.md2"); + else + shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris.md2"); - r = random(); - if (r < 0.1) - shell->s.frame = 0; - else if (r < 0.2) - shell->s.frame = 1; - else if (r < 0.3) - shell->s.frame = 2; - else if (r < 0.5) - shell->s.frame = 3; - else if (r < 0.6) - shell->s.frame = 4; - else if (r < 0.7) - shell->s.frame = 5; - else if (r < 0.8) - shell->s.frame = 6; - else if (r < 0.9) - shell->s.frame = 7; - else - shell->s.frame = 8; - - shell->owner = self; - shell->touch = ShellTouch; - shell->nextthink = level.time + 1.2 - (shells * .05); - shell->think = ShellDie; - shell->classname = "shell"; + r = random(); + if (r < 0.1) + shell->s.frame = 0; + else if (r < 0.2) + shell->s.frame = 1; + else if (r < 0.3) + shell->s.frame = 2; + else if (r < 0.5) + shell->s.frame = 3; + else if (r < 0.6) + shell->s.frame = 4; + else if (r < 0.7) + shell->s.frame = 5; + else if (r < 0.8) + shell->s.frame = 6; + else if (r < 0.9) + shell->s.frame = 7; + else + shell->s.frame = 8; + + shell->owner = self; + shell->touch = ShellTouch; + shell->nextthink = level.time + 1.2 - (shells * .05); + shell->think = ShellDie; + shell->classname = "shell"; - gi.linkentity (shell); + gi.linkentity (shell); } @@ -675,26 +696,26 @@ FindEdictByClassnum */ edict_t *FindEdictByClassnum (char *classname, int classnum) { - int i; - edict_t *it; + int i; + edict_t *it; - for (i=0 ; iclassname) - continue; - if (!it->classnum) - continue; - if (Q_stricmp(it->classname, classname) == 0) - { - if (it->classnum == classnum) - return it; - } + for (i=0 ; iclassname) + continue; + if (!it->classnum) + continue; + if (Q_stricmp(it->classname, classname) == 0) + { + if (it->classnum == classnum) + return it; + } - } + } - return NULL; + return NULL; } @@ -707,323 +728,333 @@ edict_t *FindEdictByClassnum (char *classname, int classnum) void DecalDie(edict_t *self) { - G_FreeEdict(self); + G_FreeEdict(self); } -void AddDecal (edict_t *self, vec3_t point, vec3_t direct) +void AddDecal (edict_t *self, trace_t* tr) { - edict_t *decal; - edict_t *dec; + edict_t *decal; + edict_t *dec; - if (bholelimit->value < 1) - return; + if (bholelimit->value < 1) + return; - decal = G_Spawn(); - ++decals; + decal = G_Spawn(); + ++decals; - if (decals > bholelimit->value) - decals = 1; - - dec = FindEdictByClassnum ("decal", decals); + if (decals > bholelimit->value) + decals = 1; + + dec = FindEdictByClassnum ("decal", decals); - if (dec) - { + if (dec) + { // gi.bprintf (PRINT_HIGH, "Decal->classnum == %i\n", dec->classnum); - dec->nextthink = level.time + .1; - } - + dec->nextthink = level.time + .1; + } + - decal->solid = SOLID_NOT; - decal->movetype = MOVETYPE_NONE; + decal->solid = SOLID_NOT; + decal->movetype = MOVETYPE_NONE; - decal->s.modelindex = gi.modelindex("models/objects/holes/hole1/hole.md2"); + decal->s.modelindex = gi.modelindex("models/objects/holes/hole1/hole.md2"); - VectorCopy (point, decal->s.origin); + VectorCopy (tr->endpos, decal->s.origin); - vectoangles (direct, decal->s.angles); + vectoangles (tr->plane.normal, decal->s.angles); - decal->owner = self; - decal->classnum = decals; - decal->touch = NULL; - decal->nextthink = level.time + 20; - decal->think = DecalDie; - decal->classname = "decal"; - - gi.linkentity (decal); + decal->owner = self; + decal->classnum = decals; + decal->touch = NULL; + decal->nextthink = level.time + 20; + decal->think = DecalDie; + decal->classname = "decal"; + gi.linkentity (decal); +//glass + if ((tr->ent) && (0 == Q_stricmp("func_explosive", tr->ent->classname))) + { + CGF_SFX_AttachDecalToGlass(tr->ent, decal); + } +//--- } void SplatDie(edict_t *self) { - G_FreeEdict(self); + G_FreeEdict(self); } -void AddSplat (edict_t *self, vec3_t point, vec3_t direct) +void AddSplat (edict_t *self, vec3_t point, trace_t* tr) { - edict_t *splat; - edict_t *spt; - float r; + edict_t *splat; + edict_t *spt; + float r; - if (splatlimit->value < 1) - return; + if (splatlimit->value < 1) + return; - splat = G_Spawn(); - ++splats; + splat = G_Spawn(); + ++splats; - if (splats > splatlimit->value) - splats = 1; + if (splats > splatlimit->value) + splats = 1; - spt = FindEdictByClassnum ("splat", splats); + spt = FindEdictByClassnum ("splat", splats); - if (spt) - { + if (spt) + { // gi.bprintf (PRINT_HIGH, "Decal->classnum == %i\n", dec->classnum); - spt->nextthink = level.time + .1; - } + spt->nextthink = level.time + .1; + } - splat->solid = SOLID_NOT; - splat->movetype = MOVETYPE_NONE; + splat->solid = SOLID_NOT; + splat->movetype = MOVETYPE_NONE; - r = random(); - if ( r > .67 ) - splat->s.modelindex = gi.modelindex("models/objects/splats/splat1/splat.md2"); - else if ( r > .33 ) - splat->s.modelindex = gi.modelindex("models/objects/splats/splat2/splat.md2"); - else - splat->s.modelindex = gi.modelindex("models/objects/splats/splat3/splat.md2"); + r = random(); + if ( r > .67 ) + splat->s.modelindex = gi.modelindex("models/objects/splats/splat1/splat.md2"); + else if ( r > .33 ) + splat->s.modelindex = gi.modelindex("models/objects/splats/splat2/splat.md2"); + else + splat->s.modelindex = gi.modelindex("models/objects/splats/splat3/splat.md2"); - VectorCopy (point, splat->s.origin); + VectorCopy (point, splat->s.origin); - vectoangles (direct, splat->s.angles); + vectoangles (tr->plane.normal, splat->s.angles); - splat->owner = self; - splat->touch = NULL; - splat->nextthink = level.time + 25;// - (splats * .05); - splat->think = SplatDie; - splat->classname = "splat"; - splat->classnum = splats; - - gi.linkentity (splat); + splat->owner = self; + splat->touch = NULL; + splat->nextthink = level.time + 25;// - (splats * .05); + splat->think = SplatDie; + splat->classname = "splat"; + splat->classnum = splats; + gi.linkentity (splat); +//glass + if ((tr->ent) && (0 == Q_stricmp("func_explosive", tr->ent->classname))) + { + CGF_SFX_AttachDecalToGlass(tr->ent, splat); + } +//--- } /* %-variables for chat msgs */ void GetWeaponName(edict_t *ent, char *buf) { - if (ent->client->pers.weapon) - { - strcpy(buf, ent->client->pers.weapon->pickup_name); - return; - } + if (ent->client->pers.weapon) + { + strcpy(buf, ent->client->pers.weapon->pickup_name); + return; + } - strcpy(buf, "No Weapon"); + strcpy(buf, "No Weapon"); } void GetItemName(edict_t *ent, char *buf) { - gitem_t *spec; + gitem_t *spec; int i; i = 0; while (tnames[i]) - { + { if ((spec = FindItemByClassname(tnames[i])) != NULL && ent->client->pers.inventory[ITEM_INDEX(spec)]) - { - strcpy(buf, spec->pickup_name); - return; + { + strcpy(buf, spec->pickup_name); + return; } i++; - } + } - strcpy(buf, "No Item"); + strcpy(buf, "No Item"); } void GetHealth(edict_t *ent, char *buf) { - sprintf(buf, "%d", ent->health); + sprintf(buf, "%d", ent->health); } void GetAmmo(edict_t *ent, char *buf) { - int ammo; + int ammo; - if (ent->client->pers.weapon) - { - switch (ent->client->curr_weap) - { - case MK23_NUM: - sprintf(buf, "%d rounds (%d extra clips)", - ent->client->mk23_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + if (ent->client->pers.weapon) + { + switch (ent->client->curr_weap) + { + case MK23_NUM: + sprintf(buf, "%d rounds (%d extra clips)", + ent->client->mk23_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case MP5_NUM: - sprintf(buf, "%d rounds (%d extra clips)", - ent->client->mp5_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + sprintf(buf, "%d rounds (%d extra clips)", + ent->client->mp5_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case M4_NUM: - sprintf(buf, "%d rounds (%d extra clips)", - ent->client->m4_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + sprintf(buf, "%d rounds (%d extra clips)", + ent->client->m4_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case M3_NUM: - sprintf(buf, "%d shells (%d extra shells)", - ent->client->shot_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + sprintf(buf, "%d shells (%d extra shells)", + ent->client->shot_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case HC_NUM: - sprintf(buf, "%d shells (%d extra shells)", - ent->client->cannon_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + sprintf(buf, "%d shells (%d extra shells)", + ent->client->cannon_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case SNIPER_NUM: - sprintf(buf, "%d rounds (%d extra rounds)", - ent->client->sniper_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + sprintf(buf, "%d rounds (%d extra rounds)", + ent->client->sniper_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case DUAL_NUM: - sprintf(buf, "%d rounds (%d extra clips)", - ent->client->dual_rds, - ent->client->pers.inventory[ent->client->ammo_index]); + sprintf(buf, "%d rounds (%d extra clips)", + ent->client->dual_rds, + ent->client->pers.inventory[ent->client->ammo_index]); return; case KNIFE_NUM: - ammo = ent->client->pers.inventory[ITEM_INDEX(FindItem(KNIFE_NAME))]; - sprintf(buf, "%d kni%s", ammo, (ammo == 1) ? "fe" : "ves"); + ammo = ent->client->pers.inventory[ITEM_INDEX(FindItem(KNIFE_NAME))]; + sprintf(buf, "%d kni%s", ammo, (ammo == 1) ? "fe" : "ves"); return; case GRENADE_NUM: - ammo = ent->client->pers.inventory[ITEM_INDEX(FindItem(GRENADE_NAME))]; - sprintf(buf, "%d grenade%s", ammo, (ammo == 1) ? "" : "s"); + ammo = ent->client->pers.inventory[ITEM_INDEX(FindItem(GRENADE_NAME))]; + sprintf(buf, "%d grenade%s", ammo, (ammo == 1) ? "" : "s"); return; - } - } + } + } - strcpy(buf, "no ammo"); + strcpy(buf, "no ammo"); } void GetNearbyTeammates(edict_t *self, char *buf) { - unsigned char nearby_teammates[10][16]; - int nearby_teammates_num, l; - edict_t *ent = NULL; + unsigned char nearby_teammates[10][16]; + int nearby_teammates_num, l; + edict_t *ent = NULL; - nearby_teammates_num = 0; + nearby_teammates_num = 0; while ((ent = findradius(ent, self->s.origin, 1500)) != NULL) { if (ent == self || !ent->client || !CanDamage(ent, self) || - !OnSameTeam(ent, self)) + !OnSameTeam(ent, self)) continue; - strncpy(nearby_teammates[nearby_teammates_num], ent->client->pers.netname, 15); - nearby_teammates[nearby_teammates_num][15] = 0; // in case their name is 15 chars... - nearby_teammates_num++; - if (nearby_teammates_num >= 10) - break; + strncpy(nearby_teammates[nearby_teammates_num], ent->client->pers.netname, 15); + nearby_teammates[nearby_teammates_num][15] = 0; // in case their name is 15 chars... + nearby_teammates_num++; + if (nearby_teammates_num >= 10) + break; + } + + if (nearby_teammates_num == 0) + { + strcpy(buf, "nobody"); + return; } - - if (nearby_teammates_num == 0) - { - strcpy(buf, "nobody"); - return; - } - for (l = 0; l < nearby_teammates_num; l++) - { - if (l == 0) - { - strcpy(buf, nearby_teammates[l]); - } - else - { - if (nearby_teammates_num == 2) - { - strcat(buf, " and "); - strcat(buf, nearby_teammates[l]); - } - else - { - if (l == (nearby_teammates_num - 1)) - { - strcat(buf, ", and "); - strcat(buf, nearby_teammates[l]); - } - else - { - strcat(buf, ", "); - strcat(buf, nearby_teammates[l]); - } - } - } - - } + for (l = 0; l < nearby_teammates_num; l++) + { + if (l == 0) + { + strcpy(buf, nearby_teammates[l]); + } + else + { + if (nearby_teammates_num == 2) + { + strcat(buf, " and "); + strcat(buf, nearby_teammates[l]); + } + else + { + if (l == (nearby_teammates_num - 1)) + { + strcat(buf, ", and "); + strcat(buf, nearby_teammates[l]); + } + else + { + strcat(buf, ", "); + strcat(buf, nearby_teammates[l]); + } + } + } + + } } char *SeekBufEnd(char *buf) { - while (*buf != 0) - buf++; - return buf; -} + while (*buf != 0) + buf++; + return buf; +} void ParseSayText(edict_t *ent, char *text) { - static unsigned char buf[10240], infobuf[10240]; - char *p, *pbuf; + static unsigned char buf[10240], infobuf[10240]; + char *p, *pbuf; - p = text; - pbuf = buf; - *pbuf = 0; + p = text; + pbuf = buf; + *pbuf = 0; - while (*p != 0) - { - if (((ptrdiff_t)pbuf - (ptrdiff_t)buf) > 300) - { - break; - } - if (*p == '%') - { - switch (*(p+1)) - { - case 'H': - GetHealth(ent, infobuf); - strcpy(pbuf, infobuf); - pbuf = SeekBufEnd(pbuf); - p += 2; - continue; - case 'A': - GetAmmo(ent, infobuf); - strcpy(pbuf, infobuf); - pbuf = SeekBufEnd(pbuf); - p += 2; - continue; - case 'W': - GetWeaponName(ent, infobuf); - strcpy(pbuf, infobuf); - pbuf = SeekBufEnd(pbuf); - p += 2; - continue; - case 'I': - GetItemName(ent, infobuf); - strcpy(pbuf, infobuf); - pbuf = SeekBufEnd(pbuf); - p += 2; - continue; - case 'T': - GetNearbyTeammates(ent, infobuf); - strcpy(pbuf, infobuf); - pbuf = SeekBufEnd(pbuf); - p += 2; - continue; - } - } - *pbuf++ = *p++; - } + while (*p != 0) + { + if (((ptrdiff_t)pbuf - (ptrdiff_t)buf) > 300) + { + break; + } + if (*p == '%') + { + switch (*(p+1)) + { + case 'H': + GetHealth(ent, infobuf); + strcpy(pbuf, infobuf); + pbuf = SeekBufEnd(pbuf); + p += 2; + continue; + case 'A': + GetAmmo(ent, infobuf); + strcpy(pbuf, infobuf); + pbuf = SeekBufEnd(pbuf); + p += 2; + continue; + case 'W': + GetWeaponName(ent, infobuf); + strcpy(pbuf, infobuf); + pbuf = SeekBufEnd(pbuf); + p += 2; + continue; + case 'I': + GetItemName(ent, infobuf); + strcpy(pbuf, infobuf); + pbuf = SeekBufEnd(pbuf); + p += 2; + continue; + case 'T': + GetNearbyTeammates(ent, infobuf); + strcpy(pbuf, infobuf); + pbuf = SeekBufEnd(pbuf); + p += 2; + continue; + } + } + *pbuf++ = *p++; + } - *pbuf = 0; + *pbuf = 0; - strncpy(text, buf, 300); - text[300] = 0; // in case it's 300 + strncpy(text, buf, 300); + text[300] = 0; // in case it's 300 } diff --git a/a_game.h b/a_game.h index 325d480..69be54e 100644 --- a/a_game.h +++ b/a_game.h @@ -2,7 +2,7 @@ * Include for base Action game-related things */ -#define ACTION_VERSION "1.51" +#define ACTION_VERSION "1.52" extern char team1_name[]; extern char team2_name[]; @@ -13,11 +13,13 @@ extern char team2_skin_index[]; extern char *map_rotation[]; extern int num_maps, cur_map; extern char *tnames[]; +extern int *took_damage; void ReadConfigFile(); void ReadMOTDFile(); void PrintMOTD(edict_t *); void stuffcmd(edict_t *, char *); +int KickDoor( trace_t *tr_old, edict_t *ent, vec3_t forward ); // Prototypes of base Q2 functions that weren't included in any Q2 header qboolean loc_CanSee(edict_t *, edict_t *); @@ -26,6 +28,12 @@ qboolean IsFemale(edict_t *); void ParseSayText(edict_t *, char *); // Firing styles (where shots originate from) -#define ACTION_FIRING_CENTER 0 -#define ACTION_FIRING_CLASSIC 1 -#define ACTION_FIRING_CLASSIC_HIGH 2 +#define ACTION_FIRING_CENTER 0 +#define ACTION_FIRING_CLASSIC 1 +#define ACTION_FIRING_CLASSIC_HIGH 2 + +// maxs[2] of a player when crouching (we modify it from the normal 4) +// ...also the modified viewheight -FB 7/18/99 +#define CROUCHING_MAXS2 16 +#define CROUCHING_VIEWHEIGHT 8 + diff --git a/a_items.c b/a_items.c index 4250726..9005c87 100644 --- a/a_items.c +++ b/a_items.c @@ -33,7 +33,9 @@ static edict_t *FindSpecSpawn(void) if (spot == NULL) { +/* FB 6/24/99 gi.dprintf("Warning: failed to find special item spawn point!\n"); + */ } return spot; diff --git a/a_radio.c b/a_radio.c index 3bfeb52..0f3dba4 100644 --- a/a_radio.c +++ b/a_radio.c @@ -181,7 +181,7 @@ void RadioThink(edict_t *ent) { char snd_play_cmd[512]; edict_t *from; - int check; + int check; from = ent->client->resp.radio_queue[0].from_player; @@ -199,14 +199,14 @@ void RadioThink(edict_t *ent) ent->client->resp.radio_queue[0].length = RADIO_DEATH_MALE_LEN; } - for (check = 1; check < ent->client->resp.radio_queue_size; check++) - { - if (ent->client->resp.radio_queue[check].from_player == from) - { - DeleteRadioQueueEntry(ent, check); - check--; - } - } + for (check = 1; check < ent->client->resp.radio_queue_size; check++) + { + if (ent->client->resp.radio_queue[check].from_player == from) + { + DeleteRadioQueueEntry(ent, check); + check--; + } + } } sprintf(snd_play_cmd, "play %s", ent->client->resp.radio_queue[0].soundfile); @@ -488,13 +488,15 @@ edict_t *DetermineViewedTeammate(edict_t *ent) //FIREBLADE, suggested by hal[9k] 3/11/1999 float bd = 0.9; //FIREBLADE - float d; + float d; int i; AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorScale(forward, 8192, forward); VectorAdd(ent->s.origin, forward, forward); - tr = do_trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID); + PRETRACE(); + tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID); + POSTTRACE(); if (tr.fraction < 1 && tr.ent && tr.ent->client) { return NULL; } diff --git a/a_team.c b/a_team.c index ef8b409..bb59896 100644 --- a/a_team.c +++ b/a_team.c @@ -5,6 +5,7 @@ */ #include "g_local.h" +#include "cgf_sfx_glass.h" qboolean team_game_going = 0; // is a team game going right now? qboolean team_round_going = 0; // is an actual round of a team game going right now? @@ -36,95 +37,95 @@ void CreditsMenu(edict_t *ent, pmenu_t *p); void InitTransparentList() { - if (transparent_list != NULL) - { - transparent_list_t *p, *q; + if (transparent_list != NULL) + { + transparent_list_t *p, *q; - p = transparent_list; - while (p != NULL) - { - q = p->next; - gi.TagFree(p); - p = q; - } + p = transparent_list; + while (p != NULL) + { + q = p->next; + gi.TagFree(p); + p = q; + } - transparent_list = NULL; - } + transparent_list = NULL; + } } void AddToTransparentList(edict_t *ent) { - transparent_list_t *p, *n; + transparent_list_t *p, *n; - n = (transparent_list_t *)gi.TagMalloc(sizeof(transparent_list_t), TAG_GAME); - if (n == NULL) - { - gi.dprintf("Out of memory\n"); - exit(1); - } - n->ent = ent; - n->next = NULL; + n = (transparent_list_t *)gi.TagMalloc(sizeof(transparent_list_t), TAG_GAME); + if (n == NULL) + { + gi.dprintf("Out of memory\n"); + exit(1); + } + n->ent = ent; + n->next = NULL; - if (transparent_list == NULL) - { - transparent_list = n; - } - else - { - p = transparent_list; - while (p->next != NULL) - { - p = p->next; - } - p->next = n; - } + if (transparent_list == NULL) + { + transparent_list = n; + } + else + { + p = transparent_list; + while (p->next != NULL) + { + p = p->next; + } + p->next = n; + } } void RemoveFromTransparentList(edict_t *ent) { - transparent_list_t *p, *q, *r; + transparent_list_t *p, *q, *r; - if (transparent_list != NULL) - { - if (transparent_list->ent == ent) - { - q = transparent_list->next; - gi.TagFree(transparent_list); - transparent_list = q; - return; - } - else - { - p = transparent_list; - q = p->next; - while (q != NULL) - { - if (q->ent == ent) - { - r = q->next; - gi.TagFree(q); - p->next = r; - return; - } - p = p->next; - q = p->next; - } - } - } + if (transparent_list != NULL) + { + if (transparent_list->ent == ent) + { + q = transparent_list->next; + gi.TagFree(transparent_list); + transparent_list = q; + return; + } + else + { + p = transparent_list; + q = p->next; + while (q != NULL) + { + if (q->ent == ent) + { + r = q->next; + gi.TagFree(q); + p->next = r; + return; + } + p = p->next; + q = p->next; + } + } + } - gi.dprintf("Warning: attempt to RemoveFromTransparentList when not in it\n"); -} + gi.dprintf("Warning: attempt to RemoveFromTransparentList when not in it\n"); +} void TransparentListSet(solid_t solid_type) { - transparent_list_t *p = transparent_list; + transparent_list_t *p = transparent_list; - while (p != NULL) - { - p->ent->solid = solid_type; - gi.linkentity(p->ent); - p = p->next; - } + while (p != NULL) + { + p->ent->solid = solid_type; + gi.linkentity(p->ent); + p = p->next; + } } void ReprintMOTD(edict_t *ent, pmenu_t *p) @@ -582,10 +583,10 @@ int UpdateJoinMenu(edict_t *ent) else if (num2 > num1) return TEAM1; else if (team1_score > team2_score) - return TEAM2; - else if (team2_score > team1_score) - return TEAM1; - else + return TEAM2; + else if (team2_score > team1_score) + return TEAM1; + else return TEAM1; } @@ -635,8 +636,8 @@ void CleanLevel() "item_band", "item_lasersight", "item_vest", - "thrown_knife", - "hgrenade", + "thrown_knife", + "hgrenade", }; int i; @@ -659,6 +660,9 @@ void CleanLevel() } CleanBodies(); + + // fix glass + CGF_SFX_RebuildAllBrokenGlass(); } qboolean StartClient(edict_t *ent) @@ -718,7 +722,7 @@ int CheckForWinner() int onteam1 = 0, onteam2 = 0, i; edict_t *ent; - for (i = 0; i < game.maxclients; i++) + for (i = 0; i < game.maxclients; i++) { ent = &g_edicts[1+i]; if (!ent->inuse) @@ -788,18 +792,18 @@ void SpawnPlayers() { int i; edict_t *ent; - + GetSpawnPoints(); SetupTeamSpawnPoints(); - InitTransparentList(); + InitTransparentList(); for (i = 0; i < game.maxclients; i++) { ent = &g_edicts[1+i]; if (ent->inuse && ent->client->resp.team != NOTEAM) - { + { PutClientInServer(ent); - AddToTransparentList(ent); - } + AddToTransparentList(ent); + } } } @@ -812,6 +816,7 @@ void StartRound() void StartLCA() { CleanLevel(); + CenterPrintAll("LIGHTS...\n"); gi.sound(&g_edicts[0], CHAN_VOICE | CHAN_NO_PHS_ADD, gi.soundindex("atl/lights.wav"), 1.0, ATTN_NONE, 0.0); @@ -857,10 +862,10 @@ void ContinueLCA() } else if (lights_camera_action == 1) { - CenterPrintAll("ACTION!\n"); - gi.sound(&g_edicts[0], CHAN_VOICE | CHAN_NO_PHS_ADD, - gi.soundindex("atl/action.wav"), 1.0, ATTN_NONE, 0.0); - StartRound(); + CenterPrintAll("ACTION!\n"); + gi.sound(&g_edicts[0], CHAN_VOICE | CHAN_NO_PHS_ADD, + gi.soundindex("atl/action.wav"), 1.0, ATTN_NONE, 0.0); + StartRound(); } lights_camera_action--; } @@ -997,7 +1002,7 @@ void CheckTeamRules() if (BothTeamsHavePlayers()) { CenterPrintAll("The round will begin in 20 seconds!\n"); - team_round_countdown = 201; + team_round_countdown = 201; } } } @@ -1071,12 +1076,11 @@ void A_Scoreboard(edict_t *ent) void A_ScoreboardMessage (edict_t *ent, edict_t *killer) { - char string[1400]; + char string[1400], damage[50]; gclient_t *cl; edict_t *cl_ent; int maxsize = 1000, i, j, k; - if (ent->client->scoreboardnum == 1) { int team, len, deadview; @@ -1087,9 +1091,9 @@ void A_ScoreboardMessage (edict_t *ent, edict_t *killer) int stoppedat[TEAM_TOP]; int name_pos[TEAM_TOP]; - deadview = (ent->solid == SOLID_NOT || - ent->deadflag == DEAD_DEAD || - !team_round_going); + deadview = (ent->solid == SOLID_NOT || + ent->deadflag == DEAD_DEAD || + !team_round_going); ent->client->ps.stats[STAT_TEAM_HEADER] = gi.imageindex ("tag3"); @@ -1108,29 +1112,29 @@ void A_ScoreboardMessage (edict_t *ent, edict_t *killer) team = game.clients[i].resp.team; score = game.clients[i].resp.score; - if (noscore->value) - { - j = total[team]; - } - else - { - for (j = 0; j < total[team]; j++) - { - if (score > sortedscores[team][j]) - break; - } - for (k=total[team] ; k>j ; k--) - { - sorted[team][k] = sorted[team][k-1]; - sortedscores[team][k] = sortedscores[team][k-1]; - } - } + if (noscore->value) + { + j = total[team]; + } + else + { + for (j = 0; j < total[team]; j++) + { + if (score > sortedscores[team][j]) + break; + } + for (k=total[team] ; k>j ; k--) + { + sorted[team][k] = sorted[team][k-1]; + sortedscores[team][k] = sortedscores[team][k-1]; + } + } sorted[team][j] = i; sortedscores[team][j] = score; totalscore[team] += score; total[team]++; if (cl_ent->solid != SOLID_NOT && - cl_ent->deadflag != DEAD_DEAD) + cl_ent->deadflag != DEAD_DEAD) totalalive[team]++; } @@ -1193,7 +1197,7 @@ void A_ScoreboardMessage (edict_t *ent, edict_t *killer) cl = &game.clients[sorted[TEAM1][i]]; cl_ent = g_edicts + 1 + sorted[TEAM1][i]; if (cl_ent->solid != SOLID_NOT && - cl_ent->deadflag != DEAD_DEAD) + cl_ent->deadflag != DEAD_DEAD) totalaliveprinted[TEAM1]++; // AQ truncates names at 12, not sure why, except maybe to conserve scoreboard @@ -1211,7 +1215,7 @@ void A_ScoreboardMessage (edict_t *ent, edict_t *killer) cl = &game.clients[sorted[TEAM2][i]]; cl_ent = g_edicts + 1 + sorted[TEAM2][i]; if (cl_ent->solid != SOLID_NOT && - cl_ent->deadflag != DEAD_DEAD) + cl_ent->deadflag != DEAD_DEAD) totalaliveprinted[TEAM2]++; // AQ truncates names at 12, not sure why, except maybe to conserve scoreboard @@ -1277,63 +1281,67 @@ void A_ScoreboardMessage (edict_t *ent, edict_t *killer) continue; score = game.clients[i].resp.score; - if (noscore->value) - { - j = total; - } - else - { - for (j = 0; j < total; j++) - { - if (score > sortedscores[j]) - break; - } - for (k = total ; k > j ; k--) - { - sorted[k] = sorted[k-1]; - sortedscores[k] = sortedscores[k-1]; - } - } - sorted[j] = i; - sortedscores[j] = score; + if (noscore->value) + { + j = total; + } + else + { + for (j = 0; j < total; j++) + { + if (score > sortedscores[j]) + break; + } + for (k = total ; k > j ; k--) + { + sorted[k] = sorted[k-1]; + sortedscores[k] = sortedscores[k-1]; + } + } + sorted[j] = i; + sortedscores[j] = score; total++; } - if (noscore->value) - { - strcpy(string, "xv 0 yv 32 string2 \"Player Time Ping\" " - "xv 0 yv 40 string2 \"--------------- ---- ----\" "); - } - else - { - strcpy(string, "xv 0 yv 32 string2 \"Frags Player Time Ping\" " - "xv 0 yv 40 string2 \"----- --------------- ---- ----\" "); - } + if (noscore->value) + { + strcpy(string, "xv 0 yv 32 string2 \"Player Time Ping\" " + "xv 0 yv 40 string2 \"--------------- ---- ----\" "); + } + else + { + strcpy(string, "xv 0 yv 32 string2 \"Frags Player Time Ping Damage\" " + "xv 0 yv 40 string2 \"----- --------------- ---- ---- ------\" "); + } for (i = 0; i < total; i++) { - ping = game.clients[sorted[i]].ping; - if (ping > 999) - ping = 999; - if (noscore->value) - { - sprintf(string + strlen(string), - "xv 0 yv %d string \"%-15s %4d %4d\" ", - 48 + i * 8, - game.clients[sorted[i]].pers.netname, - (level.framenum - game.clients[sorted[i]].resp.enterframe)/600, - ping); - } - else - { - sprintf(string + strlen(string), - "xv 0 yv %d string \"%5d %-15s %4d %4d\" ", - 48 + i * 8, - sortedscores[i], - game.clients[sorted[i]].pers.netname, - (level.framenum - game.clients[sorted[i]].resp.enterframe)/600, - ping); - } + ping = game.clients[sorted[i]].ping; + if (ping > 999) + ping = 999; + if (noscore->value) + { + sprintf(string + strlen(string), + "xv 0 yv %d string \"%-15s %4d %4d\" ", + 48 + i * 8, + game.clients[sorted[i]].pers.netname, + (level.framenum - game.clients[sorted[i]].resp.enterframe)/600, + ping); + } + else + { + if (game.clients[sorted[i]].resp.damage_dealt < 1000000) + sprintf(damage, "%d", game.clients[sorted[i]].resp.damage_dealt); + else + strcpy(damage, "******"); + sprintf(string + strlen(string), + "xv 0 yv %d string \"%5d %-15s %4d %4d %6s\" ", + 48 + i * 8, + sortedscores[i], + game.clients[sorted[i]].pers.netname, + (level.framenum - game.clients[sorted[i]].resp.enterframe)/600, + ping, damage); + } if (strlen(string) > (maxsize - 100) && i < (total - 2)) @@ -1404,11 +1412,11 @@ void GetSpawnPoints() { potential_spawns[num_potential_spawns] = spot; num_potential_spawns++; - if (num_potential_spawns >= MAX_SPAWNS) - { - gi.dprintf("Warning: MAX_SPAWNS exceeded\n"); - return; - } + if (num_potential_spawns >= MAX_SPAWNS) + { + gi.dprintf("Warning: MAX_SPAWNS exceeded\n"); + return; + } } } @@ -1485,10 +1493,24 @@ void SelectFarTeamplaySpawnPoint(int team, qboolean teams_assigned[]) else preferred_spawn_points = 3; +//FB 6/1/99 - make DF_SPAWN_FARTHEST force far spawn points in TP + if ((int)dmflags->value & DF_SPAWN_FARTHEST) + preferred_spawn_points = 1; +//FB 6/1/99 + spawn_to_use = newrand(preferred_spawn_points); - teams_assigned[team] = true; - teamplay_spawns[team] = spawn_distances[num_potential_spawns - spawn_to_use - 1].s; + if (team < 0 || team >= MAX_TEAMS) + { + gi.dprintf("Out-of-range teams value in SelectFarTeamplaySpawnPoint, skipping...\n"); + } + else + { + teams_assigned[team] = true; + teamplay_spawns[team] = spawn_distances[num_potential_spawns - spawn_to_use - 1].s; + } + + gi.TagFree(spawn_distances); } // SetupTeamSpawnPoints: @@ -1510,10 +1532,10 @@ void SetupTeamSpawnPoints() firstassignment = 1; do { - if (l < 0 || l >= MAX_TEAMS) - { - gi.dprintf("Warning: attempt to setup spawns for out-of-range team (%d)\n", l); - } + if (l < 0 || l >= MAX_TEAMS) + { + gi.dprintf("Warning: attempt to setup spawns for out-of-range team (%d)\n", l); + } if (firstassignment) { diff --git a/a_team.h b/a_team.h index d18d846..1665539 100644 --- a/a_team.h +++ b/a_team.h @@ -14,20 +14,22 @@ #define WINNER_TEAM2 2 #define WINNER_TIE 3 -// Override normal trace for our teamplay anti-stick stuff. If there are +// Pre- and post-trace code for our teamplay anti-stick stuff. If there are // still "transparent" (SOLID_TRIGGER) players, they need to be set to // SOLID_BBOX before a trace is performed, then changed back again -// afterwards. do_trace() should be used instead of gi.trace() in all -// areas where "transparent" players should be detected. -#define do_trace(a, b, c, d, e, f) \ - (((int)teamplay->value && transparent_list && !lights_camera_action) ? \ - (TransparentListSet(SOLID_BBOX), \ - (trace_t_temp = gi.trace(a, b, c, d, e, f)), \ - TransparentListSet(SOLID_TRIGGER), \ - trace_t_temp) \ - : \ - gi.trace(a, b, c, d, e, f)) -trace_t our_trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passent, int contentmask); +// afterwards. PRETRACE() and POSTTRACE() should be called before and after +// traces in all places where combat is taking place (ie "transparent" players +// should be detected), ie shots being traced etc. +// FB 6/1/99: Now crouching players will have their bounding box adjusted here +// too, for better shot areas. (there has to be a better way to do this?) + +#define PRETRACE() \ + if (transparent_list && (int)teamplay->value && !lights_camera_action) \ + TransparentListSet(SOLID_BBOX) + +#define POSTTRACE() \ + if (transparent_list && (int)teamplay->value && !lights_camera_action) \ + TransparentListSet(SOLID_TRIGGER) edict_t *SelectTeamplaySpawnPoint(edict_t *); qboolean FallingDamageAmnesty(edict_t *targ); @@ -61,8 +63,8 @@ typedef struct spawn_distances_s typedef struct transparent_list_s { - edict_t *ent; - struct transparent_list_s *next; + edict_t *ent; + struct transparent_list_s *next; } transparent_list_t; diff --git a/cgf_sfx_glass.c b/cgf_sfx_glass.c new file mode 100644 index 0000000..61cda20 --- /dev/null +++ b/cgf_sfx_glass.c @@ -0,0 +1,704 @@ +/****************************************************************************/ +/* */ +/* project : CGF (c) 1999 William van der Sterren */ +/* parts (c) 1998 id software */ +/* */ +/* file : cgf_sfx_glass.cpp "special effects for glass entities" */ +/* author(s): William van der Sterren */ +/* version : 0.5 */ +/* */ +/* date (last revision): Jun 12, 99 */ +/* date (creation) : Jun 04, 99 */ +/* */ +/* */ +/* revision history */ +/* -- date ---- | -- revision ---------------------- | -- revisor -- */ +/* Jun 12, 1999 | fixed knife slash breaks glass | William */ +/* Jun 08, 1999 | improved fragment limit | William */ +/* */ +/******* http://www.botepidemic.com/aid/cgf for CGF for Action Quake2 *******/ + +#ifdef __cplusplus + // VC++, for CGF + #include // prevent problems between C and STL + extern "C" + { + #include "g_local.h" + #include "cgf_sfx_glass.h" + } +#else + // C, for other AQ2 variants + #include "g_local.h" + #include "cgf_sfx_glass.h" +#endif + + +// cvar for breaking glass +static cvar_t *breakableglass = 0; + +// cvar for max glass fragment count +static cvar_t *glassfragmentlimit = 0; + +static int glassfragmentcount = 0; + + +// additional functions - Q2 expects C calling convention +#ifdef __cplusplus +extern "C" +{ +#endif + +void CGF_SFX_TouchGlass(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf); +// called whenever an entity hits the trigger spawned for the glass + +void CGF_SFX_EmitGlass (edict_t* aGlassPane, edict_t* anInflictor, vec3_t aPoint); +// emits glass fragments from aPoint, to show effects of firing thru window + +void CGF_SFX_BreakGlass(edict_t* aGlassPane, edict_t* anOther, edict_t* anAttacker, + int aDamage, vec3_t aPoint, vec_t aPaneDestructDelay + ); +// breaks glass + +void CGF_SFX_InstallBreakableGlass(edict_t* aGlassPane); +// when working on a glass pane for the first time, just install trigger +// when working on a glass pane again (after a game ended), move +// glass back to original location + +void CGF_SFX_HideBreakableGlass(edict_t* aGlassPane); +// after being broken, the pane cannot be removed as it is needed in +// subsequent missions/games, so hide it at about z = -1000 + +void CGF_SFX_ApplyGlassFragmentLimit(const char* aClassName); +// updates glassfragmentcount and removes oldest glass fragement if +// necessary to meet limit + +void CGF_SFX_MiscGlassUse(edict_t *self, edict_t *other, edict_t *activator); +// catches use from unforeseen objects (weapons, debris, +// etc. touching the window) + +void CGF_SFX_MiscGlassDie(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); +// catches die calls caused by unforeseen objects (weapons, debris, +// etc. damaging the window) + +void CGF_SFX_GlassThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin); +// variant of id software's ThrowDebris, now numbering the entity (for later removal) + +extern // from a_game.c +edict_t *FindEdictByClassnum (char *classname, int classnum); + +// declaration from g_misc.c +extern // from g_misc.c +void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); + +#ifdef __cplusplus +} +#endif + + + +void CGF_SFX_InstallGlassSupport() +{ + breakableglass = gi.cvar("breakableglass", "0", 0); + glassfragmentlimit = gi.cvar("glassfragmentlimit", "30", 0); +} + + +int CGF_SFX_IsBreakableGlassEnabled() +{ + // returns whether breakable glass is enabled (cvar) and allowed (dm mode) + return breakableglass->value; +} + + + +void CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think(edict_t* aPossibleGlassEntity) +{ + // at level.time == 0.1 the entity has been introduced in the game, + // and we can use gi.pointcontents and gi.trace to check the entity + vec3_t origin; + int breakingglass; + trace_t trace; + + // test for cvar + if (!CGF_SFX_IsBreakableGlassEnabled()) + { + G_FreeEdict(aPossibleGlassEntity); + return; + } + + VectorAdd(aPossibleGlassEntity->absmax, aPossibleGlassEntity->absmin, origin); + VectorScale(origin, 0.5, origin); + + // detect glass (does not work for complex shapes, + // for example, the glass window near the satellite + // dish at Q2 base3 + breakingglass = (gi.pointcontents(origin) & CONTENTS_TRANSLUCENT); + + if (!breakingglass) + { + // test for complex brushes that happen to be + // hollow in their origin (for instance, the + // window at Q2 base3, near the satellite dish + trace = gi.trace(origin, vec3_origin, vec3_origin, aPossibleGlassEntity->absmax, 0, + MASK_PLAYERSOLID + ); + breakingglass = ((trace.ent == aPossibleGlassEntity) && + (trace.contents & CONTENTS_TRANSLUCENT) + ); + trace = gi.trace(origin, vec3_origin, vec3_origin, aPossibleGlassEntity->absmin, 0, + MASK_PLAYERSOLID + ); + breakingglass = ((breakingglass) || + ((trace.ent == aPossibleGlassEntity) && + (trace.contents & CONTENTS_TRANSLUCENT) + ) + ); + } + + if (!breakingglass) + { + // do remove other func_explosives + G_FreeEdict (aPossibleGlassEntity); + return; + } + + // discovered some glass - now make store the origin + // we need that after hiding the glass + VectorCopy(aPossibleGlassEntity->s.origin, aPossibleGlassEntity->pos1); // IMPORTANT! + + // make a backup of the health in light_level + aPossibleGlassEntity->light_level = aPossibleGlassEntity->health; + + // install the glass + CGF_SFX_InstallBreakableGlass(aPossibleGlassEntity); +} + + + +void CGF_SFX_InstallBreakableGlass(edict_t* aGlassPane) +{ + // when working on a glass pane for the first time, just install trigger + // when working on a glass pane again (after a game ended), move + // glass back to original location + edict_t* trigger; + vec3_t maxs; + vec3_t mins; + + // reset origin based on aGlassPane->pos1 + VectorCopy(aGlassPane->pos1, aGlassPane->s.origin); + + // reset health based on aGlassPane->light_level + aGlassPane->health = aGlassPane->light_level; + + // replace die and use functions by glass specific ones + aGlassPane->die = CGF_SFX_MiscGlassDie; + aGlassPane->use = CGF_SFX_MiscGlassUse; + + // reset some pane attributes + aGlassPane->takedamage = DAMAGE_YES; + aGlassPane->solid = SOLID_BSP; + aGlassPane->movetype = MOVETYPE_FLYMISSILE; + // for other movetypes, cannot move pane to hidden location and back + + // try to establish size + VectorCopy(aGlassPane->maxs, maxs); + VectorCopy(aGlassPane->mins, mins); + + // set up trigger, similar to triggers for doors + // but with a smaller box + mins[0] -= 24; + mins[1] -= 24; + mins[2] -= 24; + maxs[0] += 24; + maxs[1] += 24; + maxs[2] += 24; + + // adjust some settings + trigger = G_Spawn (); + trigger->classname = "breakableglass_trigger"; + VectorCopy (mins, trigger->mins); + VectorCopy (maxs, trigger->maxs); + trigger->owner = aGlassPane; + trigger->solid = SOLID_TRIGGER; + trigger->movetype = MOVETYPE_NONE; + trigger->touch = CGF_SFX_TouchGlass; + gi.linkentity (trigger); +} + + +void CGF_SFX_ShootBreakableGlass(edict_t* aGlassPane, + edict_t* anAttacker, + /*trace_t**/ void* tr, + int mod + ) +{ + // process gunshots thru glass + edict_t* trigger; + int destruct; + + // depending on mod, destroy window or emit fragments + switch (mod) + { + // break for ap, shotgun, handcannon, and kick, destory window + case MOD_M3 : + case MOD_HC : + case MOD_SNIPER : + case MOD_KICK : + case MOD_GRENADE : + case MOD_G_SPLASH: + case MOD_HANDGRENADE: + case MOD_HG_SPLASH: + case MOD_KNIFE : // slash damage + destruct = true; + break; + default : + destruct = (rand() % 3 == 0); + break; + }; + + if (destruct) + { + // break glass (and hurt if doing kick) + CGF_SFX_BreakGlass(aGlassPane, anAttacker, 0, aGlassPane->health, vec3_origin, FRAMETIME); + if (mod == MOD_KICK) + { + vec3_t bloodorigin; + vec3_t dir; + vec3_t normal; + VectorAdd(aGlassPane->absmax, aGlassPane->absmin, bloodorigin); + VectorScale(bloodorigin, 0.5, bloodorigin); + VectorSubtract(bloodorigin, anAttacker->s.origin, dir); + VectorNormalize(dir); + VectorMA(anAttacker->s.origin, 32.0, dir, bloodorigin); + VectorSet(normal, 0, 0, -1); + T_Damage(anAttacker, aGlassPane, anAttacker, dir, bloodorigin, normal, 15.0, 0, 0, MOD_BREAKINGGLASS); + } + + // remove corresponding trigger + trigger = 0; + while (trigger = G_Find(trigger, FOFS(classname), "breakableglass_trigger")) + { + if (trigger->owner == aGlassPane) + { + // remove it + G_FreeEdict(trigger); + // only one to be found + break; + } + } + } + else + { + // add decal (if not grenade) + if ( (mod != MOD_HANDGRENADE) + && (mod != MOD_HG_SPLASH) + && (mod != MOD_GRENADE) + && (mod != MOD_G_SPLASH) + ) + { + AddDecal(anAttacker, (trace_t*) tr); + } + // and emit glass + CGF_SFX_EmitGlass(aGlassPane, anAttacker, ((trace_t*) tr)->endpos); + } +} + + +void CGF_SFX_TouchGlass(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + // called whenever an entity hits the trigger spawned for the glass + + vec3_t origin; + vec3_t normal; + vec3_t spot; + trace_t trace; + edict_t* glass; + vec3_t velocity; + vec_t speed; + vec_t projected_speed; + int is_hgrenade; + int is_knife; + + is_hgrenade = is_knife = false; + + // ignore non-clients-non-grenade-non-knife + if (!other->client) + { + is_knife = (0 == Q_stricmp("weapon_knife", other->classname)); + if (!is_knife) + { + is_hgrenade = (0 == Q_stricmp("hgrenade", other->classname)); + } + + if ((!is_knife) && (!is_hgrenade)) + return; + if (is_knife) + goto knife_and_grenade_handling; + } + + // test whether other really hits the glass - deal with + // the special case that other hits some boundary close to the border of the glass pane + // + // + // ....trigger....... + // +++++++++ +++++++++++ + // .+---glass------+. + // wall .+--------------+.wall + // +++++++++ +++++++++++ + // ----->.................. + // wrong ^ ^ + // | | + // wrong ok + // + glass = self->owner; + // hack - set glass' movetype to MOVETYPE_PUSH as it is not + // moving as long as the trigger is active + glass->movetype = MOVETYPE_PUSH; + + VectorAdd(glass->absmax, glass->absmin, origin); + VectorScale(origin, 0.5, origin); + + // other needs to be able to trace to glass origin + trace = gi.trace(other->s.origin, vec3_origin, vec3_origin, origin, other, + MASK_PLAYERSOLID + ); + if (trace.ent != glass) + return; + + // we can reach the glass origin, so we have the normal of + // the glass plane + VectorCopy(trace.plane.normal, normal); + + // we need to check if client is not running into wall next + // to the glass (the trigger stretches into the wall) + VectorScale(normal, -1000.0, spot); + VectorAdd(spot, other->s.origin, spot); + // line between other->s.origin and spot (perpendicular to glass + // surface should not hit wall but glass instead + trace = gi.trace(other->s.origin, vec3_origin, vec3_origin, spot, other, + MASK_PLAYERSOLID + ); + if (trace.ent != glass) + return; + + // now, we check if the client's speed perpendicular to + // the glass plane, exceeds the required 175 + // (speed should be < -200, as the plane's normal + // points towards the client + VectorCopy(other->velocity, velocity); + speed = VectorNormalize(velocity); + projected_speed = speed * DotProduct(velocity, normal); + + // bump projected speed for grenades - they should break + // the window more easily + if (is_hgrenade) + projected_speed *= 1.5; + + // if hitting the glass with sufficient speed (project < -175), + // being jumpkicked (speed > 700, project < -5) break the window + if (!((projected_speed < -175.0) || + ((projected_speed < -5) && (speed > 700)) + ) + ) + goto knife_and_grenade_handling; + + // break glass + CGF_SFX_BreakGlass(glass, other, other, glass->health, vec3_origin, 3.0 * FRAMETIME); + // glass can take care of itself, but the trigger isn't needed anymore + G_FreeEdict (self); + + /* not needed + // reduce momentum of the client (he just broke the window + // so he should lose speed. in addition, it doesn't feel + // right if he overtakes the glass fragments + // VectorScale(normal, 200.0, velocity); + // VectorAdd(other->velocity, velocity, other->velocity); + */ + + // make sure client takes damage + T_Damage(other, glass, other, normal, other->s.origin, normal, 15.0, 0, 0, MOD_BREAKINGGLASS); + return; + + // goto label + knife_and_grenade_handling: + // if knife or grenade, bounce them + if ((is_knife) || (is_hgrenade)) + { + // change clipmask to bounce of glass + other->clipmask = MASK_SOLID; + } +} + + +void CGF_SFX_BreakGlass(edict_t* aGlassPane, edict_t* anInflictor, edict_t* anAttacker, + int aDamage, vec3_t aPoint, vec_t aPaneDestructDelay + ) +{ + // based on func_explode, but with lotsa subtle differences + vec3_t origin; + vec3_t old_origin; + vec3_t chunkorigin; + vec3_t size; + int count; + int mass; + + // bmodel origins are (0 0 0), we need to adjust that here + VectorCopy(aGlassPane->s.origin, old_origin); + VectorScale (aGlassPane->size, 0.5, size); + VectorAdd (aGlassPane->absmin, size, origin); + VectorCopy (origin, aGlassPane->s.origin); + + aGlassPane->takedamage = DAMAGE_NO; + + VectorSubtract (aGlassPane->s.origin, anInflictor->s.origin, aGlassPane->velocity); + VectorNormalize (aGlassPane->velocity); + // use speed 250 instead of 150 for funkier glass spray + VectorScale (aGlassPane->velocity, 250.0, aGlassPane->velocity); + + // start chunks towards the center + VectorScale (size, 0.75, size); + + mass = aGlassPane->mass; + if (!mass) + mass = 75; + + // big chunks + if (mass >= 100) + { + count = mass / 100; + if (count > 8) + count = 8; + while(count--) + { + CGF_SFX_ApplyGlassFragmentLimit("debris"); + chunkorigin[0] = origin[0] + crandom() * size[0]; + chunkorigin[1] = origin[1] + crandom() * size[1]; + chunkorigin[2] = origin[2] + crandom() * size[2]; + CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris1/tris.md2", 1, chunkorigin); + } + } + + // small chunks + count = mass / 25; + if (count > 16) + count = 16; + while(count--) + { + CGF_SFX_ApplyGlassFragmentLimit("debris"); + chunkorigin[0] = origin[0] + crandom() * size[0]; + chunkorigin[1] = origin[1] + crandom() * size[1]; + chunkorigin[2] = origin[2] + crandom() * size[2]; + CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris2/tris.md2", 2, chunkorigin); + } + + // clear velocity, reset origin (that has been abused in ThrowDebris) + VectorClear(aGlassPane->velocity); + VectorCopy (old_origin, aGlassPane->s.origin); + + if (anAttacker) + { + // jumping thru + G_UseTargets (aGlassPane, anAttacker); + } + else + { + // firing thru - the pane has no direct attacker to hurt, + // but G_UseTargets expects one. So make it a DIY + G_UseTargets (aGlassPane, aGlassPane); + } + + // have glass plane be visible for two more frames, + // and have it self-destruct then + // meanwhile, make sure the player can move thru + aGlassPane->solid = SOLID_NOT; + aGlassPane->think = CGF_SFX_HideBreakableGlass; + aGlassPane->nextthink = level.time + aPaneDestructDelay; +} + + + +void CGF_SFX_EmitGlass (edict_t* aGlassPane, edict_t* anInflictor, vec3_t aPoint) +{ + // based on func_explode, but with lotsa subtle differences + vec3_t old_origin; + vec3_t chunkorigin; + vec3_t size; + int count; + + // bmodel origins are (0 0 0), we need to adjust that here + VectorCopy(aGlassPane->s.origin, old_origin); + VectorCopy (aPoint, aGlassPane->s.origin); + + VectorSubtract (aGlassPane->s.origin, anInflictor->s.origin, aGlassPane->velocity); + VectorNormalize (aGlassPane->velocity); + // use speed 250 instead of 150 for funkier glass spray + VectorScale (aGlassPane->velocity, 250.0, aGlassPane->velocity); + + // start chunks towards the center + VectorScale (aGlassPane->size, 0.25, size); + + count = 4; + while(count--) + { + CGF_SFX_ApplyGlassFragmentLimit("debris"); + chunkorigin[0] = aPoint[0] + crandom() * size[0]; + chunkorigin[1] = aPoint[1] + crandom() * size[1]; + chunkorigin[2] = aPoint[2] + crandom() * size[2]; + CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris2/tris.md2", 2, chunkorigin); + } + + // clear velocity, reset origin (that has been abused in ThrowDebris) + VectorClear(aGlassPane->velocity); + VectorCopy (old_origin, aGlassPane->s.origin); + + // firing thru - the pane has no direct attacker to hurt, + // but G_UseTargets expects one. So make it a DIY + G_UseTargets (aGlassPane, aGlassPane); +} + + + +void CGF_SFX_HideBreakableGlass(edict_t* aGlassPane) +{ + // remove all attached decals + edict_t* decal; + decal = 0; + while (decal = G_Find(decal, FOFS(classname), "decal")) + { + if (decal->owner == aGlassPane) + { + // make it goaway in the next frame + decal->nextthink = level.time + FRAMETIME; + } + } + + while (decal = G_Find(decal, FOFS(classname), "splat")) + { + if (decal->owner == aGlassPane) + { + // make it goaway in the next frame + decal->nextthink = level.time + FRAMETIME; + } + } + + // after being broken, the pane cannot be freed as it is needed in + // subsequent missions/games, so hide it at about z = -1000 lower + aGlassPane->movetype = MOVETYPE_FLYMISSILE; + VectorCopy(aGlassPane->s.origin, aGlassPane->pos1); + aGlassPane->s.origin[2] -=1000.0; +} + + + +void CGF_SFX_AttachDecalToGlass(edict_t* aGlassPane, edict_t* aDecal) +{ + // just set aDecal's owner to be the glass pane + aDecal->owner = aGlassPane; +} + + + +void CGF_SFX_RebuildAllBrokenGlass() +{ + // iterate over all func_explosives + edict_t* glass; + glass = 0; + while (glass = G_Find(glass, FOFS(classname), "func_explosive")) + { + // glass is broken if solid != SOLID_BSP + if (glass->solid != SOLID_BSP) + { + CGF_SFX_InstallBreakableGlass(glass); + } + } +} + + + +void CGF_SFX_ApplyGlassFragmentLimit(const char* aClassName) +{ + edict_t* oldfragment; + + glassfragmentcount++; + if (glassfragmentcount > glassfragmentlimit->value) + glassfragmentcount = 1; + + // remove fragment with corresponding number if any + oldfragment = FindEdictByClassnum((char*) aClassName, glassfragmentcount); + + if (oldfragment) + { + // oldfragment->nextthink = level.time + FRAMETIME; + G_FreeEdict(oldfragment); + } +} + + + +void CGF_SFX_MiscGlassUse(edict_t *self, edict_t *other, edict_t *activator) +{ + #ifdef _DEBUG + const char* classname; + classname = other->classname; + #endif +} + + + +void CGF_SFX_MiscGlassDie(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) +{ + #ifdef _DEBUG + const char* classname; + classname = inflictor->classname; + #endif +} + + + +static vec_t previous_throw_time = 0; +static int this_throw_count = 0; + +void CGF_SFX_GlassThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin) +{ + // based on ThrowDebris from id software - now returns debris created + edict_t *chunk; + vec3_t v; + + if (level.time != previous_throw_time) + { + previous_throw_time = level.time; + this_throw_count = 0; + } + else + { + this_throw_count++; + if (this_throw_count > glassfragmentlimit->value) + return; + } + + chunk = G_Spawn(); + VectorCopy (origin, chunk->s.origin); + gi.setmodel (chunk, modelname); + v[0] = 100 * crandom(); + v[1] = 100 * crandom(); + v[2] = 100 + 100 * crandom(); + VectorMA (self->velocity, speed, v, chunk->velocity); + chunk->movetype = MOVETYPE_BOUNCE; + chunk->solid = SOLID_NOT; + chunk->avelocity[0] = random()*600; + chunk->avelocity[1] = random()*600; + chunk->avelocity[2] = random()*600; + chunk->think = G_FreeEdict; + chunk->nextthink = level.time + 5 + random()*5; + chunk->s.frame = 0; + chunk->flags = 0; + chunk->classname = "debris"; + chunk->takedamage = DAMAGE_YES; + chunk->die = debris_die; + gi.linkentity (chunk); + + // number chunk + chunk->classnum = glassfragmentcount; +} + + diff --git a/cgf_sfx_glass.h b/cgf_sfx_glass.h new file mode 100644 index 0000000..8acf0d0 --- /dev/null +++ b/cgf_sfx_glass.h @@ -0,0 +1,281 @@ +/****************************************************************************/ +/* */ +/* project : CGF (c) 1999 William van der Sterren */ +/* parts (c) 1998 id software */ +/* */ +/* file : cgf_sfx_glass.h "special effects for glass entities" */ +/* author(s): William van der Sterren */ +/* version : 0.5 */ +/* */ +/* date (last revision): Jun 12, 99 */ +/* date (creation) : Jun 04, 99 */ +/* */ +/* */ +/* revision history */ +/* -- date ---- | -- revision ---------------------- | -- revisor -- */ +/* Jun 12, 1999 | fixed knife slash breaks glass | William */ +/* */ +/******* http://www.botepidemic.com/aid/cgf for CGF for Action Quake2 *******/ + + +#ifndef __CGF_SFX_GLASS_H_ +#define __CGF_SFX_GLASS_H_ + + +// defines (should be consistent with other weapon defs in g_local.h) +#define MOD_BREAKINGGLASS 46 + +/* +// forward definitions +typedef struct edict_s edict_t; +*/ + + +// export a number of functions to g_func.c: +// +// + +void CGF_SFX_InstallGlassSupport(); +// registers cvar breakableglass (default 0) +// registers cvar glassfragmentlimit (default 30) + + +void CGF_SFX_RebuildAllBrokenGlass(); +// upon starting a new team play game, reconstruct any +// broken glass because we like to break it again + + +int CGF_SFX_IsBreakableGlassEnabled(); +// returns whether breakable glass is enabled (cvar breakableglass) + + +void CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think(edict_t* aPossibleGlassEntity); +// initial think function for all func_explosives +// because we cannot verify the contents of the entity unless the entity +// has been spawned, we need to first introduce the entity, and remove +// it later (level.time == 0.1) if required + + +void CGF_SFX_ShootBreakableGlass(edict_t* aGlassPane, + edict_t* anAttacker, + /*trace_t*/ void* tr, + int mod + ); +// shoot thru glass - depending on bullet types and +// random effects, glass just emits fragments, or breaks + + +void CGF_SFX_AttachDecalToGlass(edict_t* aGlassPane, edict_t* aDecal); +// a glass that breaks will remove all decals (blood splashes, bullet +// holes) if they are attached + + +#endif + + +/* + + further documentation: + + cgf_sfx_glass.cpp can be compiled with ordinary c compilers as + well - just change the extension from .cpp to .c + + to install breakable glass in the ActionQuake2 1.51 code base, + do the following: + + @ file a_game.h + + modify + #define ACTION_VERSION "1.51" + to something that tells users breakable glass is supported + + @ file a_team.c + + add #include "cgf_sfx_glass.h" + + in void CleanLevel() + add + CGF_SFX_RebuildAllBrokenGlass(); + as the last statement (thus after CleanBodies();) + + @ file g_misc.c + + add #include "cgf_sfx_glass.h" + + in void SP_func_explosive (edict_t *self) + disable the statements (put them within comments): + if (deathmatch->value) + { // auto-remove for deathmatch + G_FreeEdict (self); + return; + } + + in void SP_func_explosive (edict_t *self) + add + self->think = CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think; + self->nextthink = level.time + FRAMETIME; + as the last statement (thus after gi.linkentity (self);) + + @ file g_save.c + + add #include "cgf_sfx_glass.h" + + in void InitGame (void) + add + CGF_SFX_InstallGlassSupport(); + under (splatlimit = gi.cvar ("splatlimit", "0", 0);) + + @ file g_local.h + + replace + void AddDecal (edict_t *self, vec3_t point, vec3_t direct); + void AddSplat (edict_t *self, vec3_t point, vec3_t direct); + by + void AddDecal (edict_t *self, trace_t* tr); + void AddSplat (edict_t *self, vec3_t point, trace_t* tr); + + @ file a_game.c + + add #include "cgf_sfx_glass.h" + + replace + void AddDecal (edict_t *self, vec3_t point, vec3_t direct) + by + void AddDecal (edict_t *self, trace_t* tr) + + replace + void AddSplat (edict_t *self, vec3_t point, vec3_t direct); + by + void AddSplat (edict_t *self, vec3_t point, trace_t* tr); + + in void AddDecal (edict_t *self, trace_t* tr) + replace each occurrence of 'point' by tr->endpos + + in void AddDecal (edict_t *self, trace_t* tr) + replace each occurrence of 'direct' by tr->plane.normal + + in void AddDecal (edict_t *self, trace_t* tr) + add (as the last line, thus under gi.linkentity (decal);) + if ((tr->ent) && (0 == Q_stricmp("func_explosive", tr->ent->classname))) + { + CGF_SFX_AttachDecalToGlass(tr->ent, decal); + } + + in void AddSplat (edict_t *self, vec3_t point, trace_t* tr) + replace each occurrence of 'direct' by tr->plane.normal + + in void AddSplat (edict_t *self, vec3_t point, trace_t* tr) + add (as the last line, thus under gi.linkentity (decal);) + if ((tr->ent) && (0 == Q_stricmp("func_explosive", tr->ent->classname))) + { + CGF_SFX_AttachDecalToGlass(tr->ent, splat); + } + @ file g_weapon.c + + add #include "cgf_sfx_glass.h" + + in static void fire_lead (edict_t *self, ...) + replace + AddDecal (self, tr.endpos, tr.plane.normal); + by + AddDecal (self, &tr); + + in void fire_lead_ap (edict_t *self, ...) + replace + AddDecal (self, tr.endpos, tr.plane.normal); + by + AddDecal (self, &tr); + + in static void fire_lead (edict_t *self, ...) + add + between + tr = do_trace (start, NULL, NULL, end, self, content_mask); + and + // see if we hit water + the following + // catch case of firing thru one or breakable glasses + while ( (tr.fraction < 1.0) + && (tr.surface->flags & (SURF_TRANS33 | SURF_TRANS66)) + && (tr.ent) + && (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + ) + { + // break glass + CGF_SFX_ShootBreakableGlass(tr.ent, self, &tr, mod); + // continue trace from current endpos to start + tr = do_trace (tr.endpos, NULL, NULL, end, tr.ent, content_mask); + } + + in static void fire_lead_ap (edict_t *self, ...) + add + between + tr = do_trace (start, NULL, NULL, end, self, content_mask); + and + // see if we hit water + the following + // catch case of firing thru one or breakable glasses + while ( (tr.fraction < 1.0) + && (tr.surface->flags & (SURF_TRANS33 | SURF_TRANS66)) + && (tr.ent) + && (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + ) + { + // break glass + CGF_SFX_ShootBreakableGlass(tr.ent, self, &tr, mod); + // continue trace from current endpos to start + tr = do_trace (tr.endpos, NULL, NULL, end, tr.ent, content_mask); + } + + in void knife_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) + replace + if (other->takedamage) + by + if (0 == Q_stricmp(other->classname, "func_explosive")) + { + // ignore it, so it can bounce + return; + } + else + if (other->takedamage) + + in void kick_attack (edict_t * ent ) + between + // zucc stop powerful upwards kicking + forward[2] = 0; + and + T_Damage (tr.ent, ent, ent, forward, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KICK ); + add + if (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + { + CGF_SFX_ShootBreakableGlass(tr.ent, ent, &tr, MOD_KICK); + } + + in int knife_attack ( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) + replace + if (tr.ent->takedamage) + by + if (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + { + CGF_SFX_ShootBreakableGlass(tr.ent, self, &tr, MOD_KNIFE); + } + else + if (tr.ent->takedamage) + + @ file g_phys.c + + add #include "cgf_sfx_glass.h" + + in void SV_Physics_Toss (edict_t *ent) + replace + AddSplat (self, tr.endpos, tr.plane.normal); + by + AddSplat (self, &tr); + + @ file g_combat.c + + add #include "cgf_sfx_glass.h" + + in void T_Damage (edict_t *targ, ...) + replace + if ( (mod == MOD_M3) || (mod == MOD_HC) || (mod == MOD_HELD_GRENADE) || + (mod == MOD_HG_SPLASH) || (mod == MOD_G_SPLASH) ) + by + if ( (mod == MOD_M3) + || (mod == MOD_HC) + || (mod == MOD_HELD_GRENADE) + || (mod == MOD_HG_SPLASH) + || (mod == MOD_G_SPLASH) + || (mod == MOD_BREAKINGGLASS) + ) + + in void T_RadiusDamage (...) + add before if (CanDamage (ent, inflictor)) + if (0 == Q_stricmp(ent->classname, "func_explosive")) + { + CGF_SFX_ShootBreakableGlass(ent, inflictor, 0, mod); + } + else + + @ file p_client.c + + add #include "cgf_sfx_glass.h" + + in void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker) + between + switch (mod) + { + and + case MOD_SUICIDE: + add + case MOD_BREAKINGGLASS: + message = "ate too much glass"; + break; +*/ diff --git a/g_chase.c b/g_chase.c index 74b2e44..47f6855 100644 --- a/g_chase.c +++ b/g_chase.c @@ -19,7 +19,7 @@ int ChaseTargetGone(edict_t *ent) { ent->client->chase_target = NULL; ent->client->desired_fov = 90; - ent->client->ps.fov = 90; + ent->client->ps.fov = 90; ent->client->chase_mode = 0; ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; return 1; @@ -45,7 +45,7 @@ void UpdateChaseCam(edict_t *ent) if (ent->client->chase_mode == 1) { ent->client->desired_fov = 90; - ent->client->ps.fov = 90; + ent->client->ps.fov = 90; if (ent->client->resp.cmd_angles[PITCH] > 89) ent->client->resp.cmd_angles[PITCH] = 89; @@ -69,7 +69,9 @@ void UpdateChaseCam(edict_t *ent) if (!targ->groundentity) o[2] += 16; - trace = do_trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID); + PRETRACE(); + trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID); + POSTTRACE(); VectorCopy(trace.endpos, goal); @@ -78,7 +80,9 @@ void UpdateChaseCam(edict_t *ent) // pad for floors and ceilings VectorCopy(goal, o); o[2] += 6; - trace = do_trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); + PRETRACE(); + trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); + POSTTRACE(); if (trace.fraction < 1) { VectorCopy(trace.endpos, goal); goal[2] -= 6; @@ -86,7 +90,9 @@ void UpdateChaseCam(edict_t *ent) VectorCopy(goal, o); o[2] -= 6; - trace = do_trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); + PRETRACE(); + trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); + POSTTRACE(); if (trace.fraction < 1) { VectorCopy(trace.endpos, goal); goal[2] += 6; @@ -118,7 +124,7 @@ void UpdateChaseCam(edict_t *ent) VectorCopy(o, ent->s.origin); ent->client->ps.fov = targ->client->ps.fov; - ent->client->desired_fov = targ->client->ps.fov; + ent->client->desired_fov = targ->client->ps.fov; for (i=0 ; i<3 ; i++) ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]); @@ -131,8 +137,11 @@ void UpdateChaseCam(edict_t *ent) } else { - VectorCopy(targ->client->v_angle, ent->client->ps.viewangles); - VectorCopy(targ->client->v_angle, ent->client->v_angle); + VectorAdd(targ->client->v_angle, + targ->client->ps.kick_angles, + angles); + VectorCopy(angles, ent->client->ps.viewangles); + VectorCopy(angles, ent->client->v_angle); } } @@ -157,6 +166,12 @@ void ChaseNext(edict_t *ent) e = g_edicts + i; if (!e->inuse) continue; + //Black Cross - Begin + if (teamplay->value && limchasecam->value && + ent->client->resp.team != NOTEAM && + ent->client->resp.team != e->client->resp.team) + continue; + //Black Cross - End if (e->solid != SOLID_NOT || e->deadflag == DEAD_DEAD) break; } while (e != ent->client->chase_target); @@ -180,6 +195,12 @@ void ChasePrev(edict_t *ent) e = g_edicts + i; if (!e->inuse) continue; + //Black Cross - Begin + if (teamplay->value && limchasecam->value && + ent->client->resp.team != NOTEAM && + ent->client->resp.team != e->client->resp.team) + continue; + //Black Cross - End if (e->solid != SOLID_NOT || e->deadflag == DEAD_DEAD) break; } while (e != ent->client->chase_target); @@ -194,30 +215,36 @@ void GetChaseTarget(edict_t *ent) start_target = ent->client->resp.last_chase_target; - if (start_target == NULL) - { - start_target = g_edicts + 1; - } - else - { - if (start_target < (g_edicts + 1) || - start_target > (g_edicts + game.maxclients)) - { - gi.dprintf("Warning: start_target ended up out of range\n"); - } - } + if (start_target == NULL) + { + start_target = g_edicts + 1; + } + else + { + if (start_target < (g_edicts + 1) || + start_target > (g_edicts + game.maxclients)) + { + gi.dprintf("Warning: start_target ended up out of range\n"); + } + } i = (start_target - g_edicts) + 1; found = searched = 0; do - { - searched++; + { + searched++; i--; if (i < 1) i = game.maxclients; e = g_edicts + i; if (!e->inuse) continue; + //Black Cross - Begin + if (teamplay->value && limchasecam->value && + ent->client->resp.team != NOTEAM && + ent->client->resp.team != e->client->resp.team) + continue; + //Black Cross - End if (e->solid != SOLID_NOT || e->deadflag == DEAD_DEAD) { found = 1; @@ -225,14 +252,13 @@ void GetChaseTarget(edict_t *ent) } } while ((e != (start_target + 1)) && searched < 100); - if (searched >= 100) - { - gi.dprintf("Warning: prevented loop in GetChaseTarget\n"); - } + if (searched >= 100) + { + gi.dprintf("Warning: prevented loop in GetChaseTarget\n"); + } if (found) { ent->client->chase_target = e; } } - diff --git a/g_cmds.c b/g_cmds.c index c9163f6..aae80d0 100644 --- a/g_cmds.c +++ b/g_cmds.c @@ -33,8 +33,8 @@ qboolean OnSameTeam (edict_t *ent1, edict_t *ent2) char ent2Team [512]; //FIREBLADE - if (!ent1->client || !ent2->client) - return false; + if (!ent1->client || !ent2->client) + return false; if (teamplay->value) { @@ -168,11 +168,11 @@ void Cmd_Give_f (edict_t *ent) return; } - if (ent->solid == SOLID_NOT) - { - gi.cprintf(ent, PRINT_HIGH, "This command can't be used by spectators.\n"); - return; - } + if (ent->solid == SOLID_NOT) + { + gi.cprintf(ent, PRINT_HIGH, "This command can't be used by spectators.\n"); + return; + } name = gi.args(); @@ -337,9 +337,9 @@ void Cmd_Give_f (edict_t *ent) /* if (gi.argc() == 5) ent->client->pers.inventory[index] = atoi(gi.argv(4)); else if ( (gi.argc() == 4) && !(stricmp(it->pickup_name, "12 Gauge Shells")) ) - ent->client->pers.inventory[index] = atoi(gi.argv(3)); - else */ - ent->client->pers.inventory[index] += it->quantity; + ent->client->pers.inventory[index] = atoi(gi.argv(3)); + else */ + ent->client->pers.inventory[index] += it->quantity; } else if ( it->flags & IT_ITEM) { @@ -490,6 +490,33 @@ void Cmd_Use_f (edict_t *ent) s = SNIPER_NAME; if (!stricmp(s, "bfg10k")) s = KNIFE_NAME; + // zucc - let people pull up a knife ready to be thrown + if (!stricmp(s, "throwing combat knife")) + { + if ( ent->client->curr_weap != KNIFE_NUM ) + { + ent->client->resp.knife_mode = 1; + } + // switch to throwing mode if a knife is already out + else + { + Cmd_New_Weapon_f( ent ); + } + s = KNIFE_NAME; + } + if (!stricmp(s, "slashing combat knife")) + { + if ( ent->client->curr_weap != KNIFE_NUM ) + { + ent->client->resp.knife_mode = 0; + } + // switch to slashing mode if a knife is already out + else + { + Cmd_New_Weapon_f( ent ); + } + s = KNIFE_NAME; + } if (!stricmp(s, "grenade launcher")) s = M4_NAME; if (!stricmp(s, "grenades")) @@ -873,33 +900,33 @@ void Cmd_Players_f (edict_t *ent) count = 0; for (i = 0 ; i < maxclients->value ; i++) - { + { if (game.clients[i].pers.connected) { index[count] = i; count++; } - } + } - if (!teamplay->value || !noscore->value) - { - // sort by frags - qsort (index, count, sizeof(index[0]), PlayerSort); - } + if (!teamplay->value || !noscore->value) + { + // sort by frags + qsort (index, count, sizeof(index[0]), PlayerSort); + } // print information large[0] = 0; for (i = 0 ; i < count ; i++) { - if (!teamplay->value || !noscore->value) - Com_sprintf (small, sizeof(small), "%3i %s\n", - game.clients[index[i]].ps.stats[STAT_FRAGS], - game.clients[index[i]].pers.netname); - else - Com_sprintf (small, sizeof(small), "%s\n", - game.clients[index[i]].pers.netname); - + if (!teamplay->value || !noscore->value) + Com_sprintf (small, sizeof(small), "%3i %s\n", + game.clients[index[i]].ps.stats[STAT_FRAGS], + game.clients[index[i]].pers.netname); + else + Com_sprintf (small, sizeof(small), "%s\n", + game.clients[index[i]].pers.netname); + if (strlen (small) + strlen(large) > sizeof(large) - 100 ) { // can't print all of them in one packet strcat (large, "...\n"); @@ -969,11 +996,11 @@ Cmd_Say_f */ void Cmd_Say_f (edict_t *ent, qboolean team, qboolean arg0, qboolean partner_msg) { - int j, i; + int j, i, offset_of_text; edict_t *other; char *p; char text[2048]; - gclient_t *cl; + gclient_t *cl; if (gi.argc () < 2 && !arg0) return; @@ -987,18 +1014,36 @@ void Cmd_Say_f (edict_t *ent, qboolean team, qboolean arg0, qboolean partner_msg } if (team) + { + if (ent->client->resp.team == NOTEAM) + { + gi.cprintf(ent, PRINT_HIGH, "You're not on a team.\n"); + return; + } Com_sprintf (text, sizeof(text), "%s(%s): ", (teamplay->value && (ent->solid == SOLID_NOT || ent->deadflag == DEAD_DEAD)) ? "[DEAD] " : "", ent->client->pers.netname); + } else if (partner_msg) + { + if (ent->client->resp.radio_partner == NULL) + { + gi.cprintf(ent, PRINT_HIGH, "You don't have a partner.\n"); + return; + } Com_sprintf (text, sizeof(text), "[%sPARTNER] %s: ", (teamplay->value && (ent->solid == SOLID_NOT || ent->deadflag == DEAD_DEAD)) ? "DEAD " : "", ent->client->pers.netname); + } else + { Com_sprintf (text, sizeof(text), "%s%s: ", (teamplay->value && (ent->solid == SOLID_NOT || ent->deadflag == DEAD_DEAD)) ? "[DEAD] " : "", ent->client->pers.netname); + } + offset_of_text = strlen(text); //FB 5/31/99 + if (arg0) { strcat (text, gi.argv(0)); @@ -1022,10 +1067,11 @@ void Cmd_Say_f (edict_t *ent, qboolean team, qboolean arg0, qboolean partner_msg if (strlen(text) > 300) text[300] = 0; - if (ent->solid != SOLID_NOT && ent->deadflag != DEAD_DEAD) - ParseSayText(ent, text); - // this will parse the % variables, - // and again check 300 limit afterwards -FB + if (ent->solid != SOLID_NOT && ent->deadflag != DEAD_DEAD) + ParseSayText(ent, text + offset_of_text); //FB 5/31/99 - offset change + // this will parse the % variables, + // and again check 300 limit afterwards -FB + // (although it checks it without the name in front, oh well) strcat(text, "\n"); @@ -1079,7 +1125,7 @@ void Cmd_Say_f (edict_t *ent, qboolean team, qboolean arg0, qboolean partner_msg if (teamplay->value && team_round_going) { if ((ent->solid == SOLID_NOT || ent->deadflag == DEAD_DEAD) && - (other->solid != SOLID_NOT && other->deadflag != DEAD_DEAD)) + (other->solid != SOLID_NOT && other->deadflag != DEAD_DEAD)) continue; } //FIREBLADE @@ -1100,21 +1146,21 @@ void Cmd_PlayerList_f(edict_t *ent) if (!e2->inuse) continue; - if (!teamplay->value || !noscore->value) - Com_sprintf(st, sizeof(st), "%02d:%02d %4d %3d %s%s\n", - (level.framenum - e2->client->resp.enterframe) / 600, - ((level.framenum - e2->client->resp.enterframe) % 600)/10, - e2->client->ping, - e2->client->resp.score, - e2->client->pers.netname, - (e2->solid == SOLID_NOT && e2->deadflag != DEAD_DEAD) ? " (spectator)" : ""); - else - Com_sprintf(st, sizeof(st), "%02d:%02d %4d %s%s\n", - (level.framenum - e2->client->resp.enterframe) / 600, - ((level.framenum - e2->client->resp.enterframe) % 600)/10, - e2->client->ping, - e2->client->pers.netname, - (e2->solid == SOLID_NOT && e2->deadflag != DEAD_DEAD) ? " (spectator)" : ""); + if (!teamplay->value || !noscore->value) + Com_sprintf(st, sizeof(st), "%02d:%02d %4d %3d %s%s\n", + (level.framenum - e2->client->resp.enterframe) / 600, + ((level.framenum - e2->client->resp.enterframe) % 600)/10, + e2->client->ping, + e2->client->resp.score, + e2->client->pers.netname, + (e2->solid == SOLID_NOT && e2->deadflag != DEAD_DEAD) ? " (spectator)" : ""); + else + Com_sprintf(st, sizeof(st), "%02d:%02d %4d %s%s\n", + (level.framenum - e2->client->resp.enterframe) / 600, + ((level.framenum - e2->client->resp.enterframe) % 600)/10, + e2->client->ping, + e2->client->pers.netname, + (e2->solid == SOLID_NOT && e2->deadflag != DEAD_DEAD) ? " (spectator)" : ""); if (strlen(text) + strlen(st) > sizeof(text) - 100) { sprintf(text+strlen(text), "...\n"); @@ -1218,7 +1264,7 @@ void ClientCommand (edict_t *ent) else if (Q_stricmp (cmd, "reload") == 0) Cmd_New_Reload_f (ent); else if (Q_stricmp (cmd, "weapon") == 0) - Cmd_Weapon_f (ent); + Cmd_New_Weapon_f (ent); else if (Q_stricmp (cmd, "opendoor") == 0) Cmd_OpenDoor_f (ent); else if (Q_stricmp (cmd, "bandage") == 0) diff --git a/g_combat.c b/g_combat.c index c57930b..527fc07 100644 --- a/g_combat.c +++ b/g_combat.c @@ -1,1067 +1,1116 @@ -// g_combat.c - -#include "g_local.h" - -/* -============ -CanDamage - -Returns true if the inflictor can directly damage the target. Used for -explosions and melee attacks. -============ -*/ -qboolean CanDamage (edict_t *targ, edict_t *inflictor) -{ - vec3_t dest; - trace_t trace; - -// bmodels need special checking because their origin is 0,0,0 - if (targ->movetype == MOVETYPE_PUSH) - { - VectorAdd (targ->absmin, targ->absmax, dest); - VectorScale (dest, 0.5, dest); - trace = do_trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - if (trace.ent == targ) - return true; - return false; - } - - trace = do_trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] += 15.0; - dest[1] += 15.0; - trace = do_trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] += 15.0; - dest[1] -= 15.0; - trace = do_trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] -= 15.0; - dest[1] += 15.0; - trace = do_trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] -= 15.0; - dest[1] -= 15.0; - trace = do_trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - - - return false; -} - - -/* -============ -Killed -============ -*/ -void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - if (targ->health < -999) - targ->health = -999; - - if ( targ->client ) - { - targ->client->bleeding = 0; - //targ->client->bleedcount = 0; - targ->client->bleed_remain = 0; - } - - targ->enemy = attacker; - - if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) - { -// targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type - if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) - { - level.killed_monsters++; - if (coop->value && attacker->client) - attacker->client->resp.score++; - // medics won't heal monsters that they kill themselves - if (strcmp(attacker->classname, "monster_medic") == 0) - targ->owner = attacker; - } - } - - if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE) - { // doors, triggers, etc - targ->die (targ, inflictor, attacker, damage, point); - return; - } - - if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) - { - targ->touch = NULL; - monster_death_use (targ); - } - - targ->die (targ, inflictor, attacker, damage, point); -} - - -/* -================ -SpawnDamage -================ -*/ -void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage) -{ - if (damage > 255) - damage = 255; - gi.WriteByte (svc_temp_entity); - gi.WriteByte (type); -// gi.WriteByte (damage); - gi.WritePosition (origin); - gi.WriteDir (normal); - gi.multicast (origin, MULTICAST_PVS); -} - - -/* -============ -T_Damage - -targ entity that is being damaged -inflictor entity that is causing the damage -attacker entity that caused the inflictor to damage targ - example: targ=monster, inflictor=rocket, attacker=player - -dir direction of the attack -point point at which the damage is being inflicted -normal normal vector from that point -damage amount of damage being inflicted -knockback force to be applied against targ as a result of the damage - -dflags these flags are used to control how T_Damage works - DAMAGE_RADIUS damage was indirect (from a nearby explosion) - DAMAGE_NO_ARMOR armor does not protect from this damage - DAMAGE_ENERGY damage is from an energy based weapon - DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles - DAMAGE_BULLET damage is from a bullet (used for ricochets) - DAMAGE_NO_PROTECTION kills godmode, armor, everything -============ -*/ -static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags) -{ - gclient_t *client; - int save; - int power_armor_type; - int index; - int damagePerCell; - int pa_te_type; - int power; - int power_used; - - if (!damage) - return 0; - - client = ent->client; - - if (dflags & DAMAGE_NO_ARMOR) - return 0; - - if (client) - { - power_armor_type = PowerArmorType (ent); - if (power_armor_type != POWER_ARMOR_NONE) - { - index = ITEM_INDEX(FindItem("Cells")); - power = client->pers.inventory[index]; - } - } - else if (ent->svflags & SVF_MONSTER) - { - power_armor_type = ent->monsterinfo.power_armor_type; - power = ent->monsterinfo.power_armor_power; - } - else - return 0; - - if (power_armor_type == POWER_ARMOR_NONE) - return 0; - if (!power) - return 0; - - if (power_armor_type == POWER_ARMOR_SCREEN) - { - vec3_t vec; - float dot; - vec3_t forward; - - // only works if damage point is in front - AngleVectors (ent->s.angles, forward, NULL, NULL); - VectorSubtract (point, ent->s.origin, vec); - VectorNormalize (vec); - dot = DotProduct (vec, forward); - if (dot <= 0.3) - return 0; - - damagePerCell = 1; - pa_te_type = TE_SCREEN_SPARKS; - damage = damage / 3; - } - else - { - damagePerCell = 2; - pa_te_type = TE_SHIELD_SPARKS; - damage = (2 * damage) / 3; - } - - save = power * damagePerCell; - if (!save) - return 0; - if (save > damage) - save = damage; - - SpawnDamage (pa_te_type, point, normal, save); - ent->powerarmor_time = level.time + 0.2; - - power_used = save / damagePerCell; - - if (client) - client->pers.inventory[index] -= power_used; - else - ent->monsterinfo.power_armor_power -= power_used; - return save; -} - -static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags) -{ - gclient_t *client; - int save; - int index; - gitem_t *armor; - - if (!damage) - return 0; - - client = ent->client; - - if (!client) - return 0; - - if (dflags & DAMAGE_NO_ARMOR) - return 0; - - index = ArmorIndex (ent); - if (!index) - return 0; - - armor = GetItemByIndex (index); - - if (dflags & DAMAGE_ENERGY) - save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage); - else - save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage); - if (save >= client->pers.inventory[index]) - save = client->pers.inventory[index]; - - if (!save) - return 0; - - client->pers.inventory[index] -= save; - SpawnDamage (te_sparks, point, normal, save); - - return save; -} - -void M_ReactToDamage (edict_t *targ, edict_t *attacker) -{ - if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) - return; - - if (attacker == targ || attacker == targ->enemy) - return; - - // if we are a good guy monster and our attacker is a player - // or another good guy, do not get mad at them - if (targ->monsterinfo.aiflags & AI_GOOD_GUY) - { - if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) - return; - } - - // we now know that we are not both good guys - - // if attacker is a client, get mad at them because he's good and we're not - if (attacker->client) - { - // this can only happen in coop (both new and old enemies are clients) - // only switch if can't see the current enemy - if (targ->enemy && targ->enemy->client) - { - if (visible(targ, targ->enemy)) - { - targ->oldenemy = attacker; - return; - } - targ->oldenemy = targ->enemy; - } - targ->enemy = attacker; - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - FoundTarget (targ); - return; - } - - // it's the same base (walk/swim/fly) type and a different classname and it's not a tank - // (they spray too much), get mad at them - if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && - (strcmp (targ->classname, attacker->classname) != 0) && - (strcmp(attacker->classname, "monster_tank") != 0) && - (strcmp(attacker->classname, "monster_supertank") != 0) && - (strcmp(attacker->classname, "monster_makron") != 0) && - (strcmp(attacker->classname, "monster_jorg") != 0) ) - { - if (targ->enemy) - if (targ->enemy->client) - targ->oldenemy = targ->enemy; - targ->enemy = attacker; - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - FoundTarget (targ); - } - else - // otherwise get mad at whoever they are mad at (help our buddy) - { - if (targ->enemy) - if (targ->enemy->client) - targ->oldenemy = targ->enemy; - targ->enemy = attacker->enemy; - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - FoundTarget (targ); - } -} - -qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker) -{ - //FIXME make the next line real and uncomment this block - // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team)) - return false; -} - - - -void BloodSprayThink (edict_t *self) -{ - - - /* if ( self->dmg > 0 ) - { - self->dmg -= 10; -// SpawnDamage (TE_BLOOD, self->s.origin, self->movedir, self->dmg); - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_SPLASH); - gi.WriteByte (6); - gi.WritePosition (self->s.origin); - gi.WriteDir (self->movedir); - gi.WriteByte (6); //blood - gi.multicast (self->s.origin, MULTICAST_PVS); - - } - else - { - self->think = G_FreeEdict; - } - - self->nextthink = level.time + 0.1; - gi.linkentity (self); - */ - - G_FreeEdict(self); - -} - -void blood_spray_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (other == ent->owner) - return; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 0.1; -} - - - -void spray_blood (edict_t *self, vec3_t start, vec3_t dir, int damage, int mod ) -{ - edict_t *blood; - int speed; - - switch(mod) - { - case MOD_MK23: - speed = 1800; - break; - case MOD_MP5: - speed = 1500; - break; - case MOD_M4: - speed = 2400; - break; - case MOD_KNIFE: - speed = 0; - break; - case MOD_KNIFE_THROWN: - speed = 0; - break; - case MOD_DUAL: - speed = 1800; - break; - case MOD_SNIPER: - speed = 4000; - break; - default: - speed = 1800; - } - - - - - blood = G_Spawn(); - VectorNormalize(dir); - VectorCopy (start, blood->s.origin); - VectorCopy (dir, blood->movedir); - vectoangles (dir, blood->s.angles); - VectorScale (dir, speed, blood->velocity); - blood->movetype = MOVETYPE_BLOOD; - blood->clipmask = MASK_SHOT; - blood->solid = SOLID_BBOX; - blood->s.effects |= EF_GIB; - VectorClear (blood->mins); - VectorClear (blood->maxs); - blood->s.modelindex = gi.modelindex ("sprites/null.sp2"); - blood->owner = self; - blood->nextthink = level.time + speed/1000;//3.2; - blood->touch = blood_spray_touch; - blood->think = BloodSprayThink; - blood->dmg = damage; - blood->classname = "blood_spray"; - - gi.linkentity (blood); -} - - -// zucc based on some code in Action Quake -void spray_sniper_blood( edict_t *self, vec3_t start, vec3_t dir ) -{ - vec3_t forward; - int mod = MOD_SNIPER; - - VectorCopy (dir, forward); - - forward[2] += .03; - - spray_blood(self, start, forward, 0, mod); - - - VectorCopy (dir, forward); - forward[2] -= .03; - spray_blood(self, start, forward, 0, mod); - - - VectorCopy (dir, forward); - if ( (forward[0] > 0) && (forward[1] > 0) ) - { - forward[0] -= .03; - forward[1] += .03; - } - if ( (forward[0] > 0) && (forward[1] < 0) ) - { - forward[0] += .03; - forward[1] += .03; - } - if ( (forward[0] < 0) && (forward[1] > 0) ) - { - forward[0] -= .03; - forward[1] -= .03; - } - if ( (forward[0] < 0) && (forward[1] < 0) ) - { - forward[0] += .03; - forward[1] -= .03; - } - spray_blood(self, start, forward, 0, mod); - - - VectorCopy (dir, forward); - if ( (forward[0] > 0) && (forward[1] > 0) ) - { - forward[0] += .03; - forward[1] -= .03; - } - if ( (forward[0] > 0) && (forward[1] < 0) ) - { - forward[0] -= .03; - forward[1] -= .03; - } - if ( (forward[0] < 0) && (forward[1] > 0) ) - { - forward[0] += .03; - forward[1] += .03; - } - if ( (forward[0] < 0) && (forward[1] < 0) ) - { - forward[0] -= .03; - forward[1] += .03; - } - spray_blood(self, start, forward, 0, mod); - - VectorCopy (dir, forward); - spray_blood(self, start, forward, 0, mod); - -} - - -void VerifyHeadShot( vec3_t point, vec3_t dir, float height, vec3_t newpoint) -{ - vec3_t normdir; - vec3_t normdir2; - - - VectorNormalize2(dir, normdir); - VectorScale( normdir, height, normdir2 ); - VectorAdd( point, normdir2, newpoint ); -} - - - -// zucc adding location hit code -// location hit code based off ideas by pyromage and shockman - -#define LEG_DAMAGE (height/2.2) - abs(targ->mins[2]) - 3 -#define STOMACH_DAMAGE (height/1.8) - abs(targ->mins[2]) -#define CHEST_DAMAGE (height/1.4) - abs(targ->mins[2]) - -#define HEAD_HEIGHT 12.0 -qboolean IsFemale (edict_t *ent); - - -void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) -{ - gclient_t *client; - int take; - int save; - int asave; - int psave; - int te_sparks; - int do_sparks = 0; - int damage_type = 0; // used for MOD later - int bleeding = 0; // damage causes bleeding - int head_success = 0; - int instant_dam = 1; - - float z_rel; - int height; - - gitem_t* item; - - float from_top; - - vec3_t line; - vec_t dist; - - // do this before teamplay check - if (!targ->takedamage) - return; - - //FIREBLADE - if (teamplay->value && mod != MOD_TELEFRAG) - { - if (lights_camera_action) - return; - - if (targ != attacker && targ->client && attacker->client && - targ->client->resp.team == attacker->client->resp.team) - return; - } - //FIREBLADE - - - // damage reduction for shotgun - // if far away, reduce it to original action levels - if ( mod == MOD_M3 ) - { - VectorSubtract(targ->s.origin, inflictor->s.origin, line ); - dist = VectorLength( line ); - if ( dist > 450.0 ) - { - damage = damage - 2; - } - - } - - - - - - item = FindItem(KEV_NAME); - - height = abs(targ->mins[2]) + targ->maxs[2]; - - // locational damage code - // base damage is head shot damage, so all the scaling is downwards - if (targ->client) - { - if (!((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && OnSameTeam (targ, attacker))) - { - - if ((mod == MOD_MK23) || - (mod == MOD_MP5) || - (mod == MOD_M4) || - (mod == MOD_SNIPER) || - (mod == MOD_DUAL) || - (mod == MOD_KNIFE) || - (mod == MOD_KNIFE_THROWN)) - { - z_rel = point[2] - targ->s.origin[2]; - from_top = targ->maxs[2] - z_rel; - bleeding = 1; - instant_dam = 0; - - // damage reduction for longer range pistol shots - if ( mod == MOD_MK23 || mod == MOD_DUAL ) - { - VectorSubtract(targ->s.origin, inflictor->s.origin, line ); - dist = VectorLength( line ); - if ( dist > 600.0 && dist < 1400.0 ) - { - damage = (int)(damage*2/3); - } - else if ( dist > 1400.0 ) - damage = (int)(damage*1/2); - } - - - //gi.cprintf(targ, PRINT_HIGH, "z_rel is %f\n leg: %f stomach: %f chest: %f\n", z_rel, LEG_DAMAGE, STOMACH_DAMAGE, CHEST_DAMAGE ); - //gi.cprintf(targ, PRINT_HIGH, "point[2]: %f targ->s.origin[2]: %f height: %d\n", point[2], targ->s.origin[2], height ); - //gi.cprintf(targ, PRINT_HIGH, "abs(trag->min[2]): %d targ_max[2] %d\n", (int)abs(targ->mins[2]), (int)targ->maxs[2]); - //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[2]): %d targ_max[2] %d\n", (int)abs(targ->mins[2]), (int)targ->maxs[2]); - //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[0]): %d targ_max[0] %d\n", (int)abs(targ->mins[0]), (int)targ->maxs[0]); - //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[1]): %d targ_max[1] %d\n", (int)abs(targ->mins[1]), (int)targ->maxs[1]); - - - if ( from_top < 2*HEAD_HEIGHT ) - { - vec3_t new_point; - VerifyHeadShot( point, dir, HEAD_HEIGHT, new_point ); - VectorSubtract( new_point, targ->s.origin, new_point ); - //gi.cprintf(attacker, PRINT_HIGH, "z: %d y: %d x: %d\n", (int)(targ->maxs[2] - new_point[2]),(int)(new_point[1]) , (int)(new_point[0]) ); - - if ( (targ->maxs[2] - new_point[2]) < HEAD_HEIGHT - && (abs(new_point[1])) < HEAD_HEIGHT*.8 - && (abs(new_point[0])) < HEAD_HEIGHT*.8 ) - - { - head_success = 1; - } - } - - if ( head_success ) - { - - damage = damage*1.8 + 1; - gi.cprintf(targ, PRINT_HIGH, "Head damage\n"); - if (attacker->client) - gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the head\n", targ->client->pers.netname); - damage_type = LOC_HDAM; - if ( mod != MOD_KNIFE && mod != MOD_KNIFE_THROWN ) - gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/headshot.wav"), 1, ATTN_NORM, 0); - //else - // gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/glurp.wav"), 1, ATTN_NORM, 0); - } - - else if (z_rel < LEG_DAMAGE) - { - damage = damage * .25; - gi.cprintf(targ, PRINT_HIGH, "Leg damage\n"); - if (attacker->client) - gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the legs\n", targ->client->pers.netname); - damage_type = LOC_LDAM; - targ->client->leg_damage = 1; - targ->client->leghits++; - } - else if (z_rel < STOMACH_DAMAGE) - { - damage = damage * .4; - gi.cprintf(targ, PRINT_HIGH, "Stomach damage\n"); - if (attacker->client) - gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the stomach\n", targ->client->pers.netname); - damage_type = LOC_SDAM; - } - else //(z_rel < CHEST_DAMAGE) - { - if ( (targ->client->pers.inventory[ITEM_INDEX(item)]) - && mod != MOD_KNIFE - && mod != MOD_KNIFE_THROWN - && mod != MOD_SNIPER ) - { - if (attacker->client) - { - gi.cprintf(attacker, PRINT_HIGH, "%s has a Kevlar Vest - AIM FOR THE HEAD!\n", - targ->client->pers.netname); - gi.cprintf(targ, PRINT_HIGH, "Kevlar Vest absorbed most of %s's shot\n", - attacker->client->pers.netname); - /* - if (IsFemale(targ)) - gi.cprintf(attacker, PRINT_HIGH, "You bruised %s through her Kevlar Vest\n", targ->client->pers.netname); - else - gi.cprintf(attacker, PRINT_HIGH, "You bruised %s through his Kevlar Vest\n", targ->client->pers.netname); - */ - } - gi.sound(targ, CHAN_ITEM, gi.soundindex("misc/vest.wav"), 1, ATTN_NORM, 0); - damage = (int)(damage/10); - damage_type = LOC_CDAM; - bleeding = 0; - instant_dam = 1; - stopAP = 1; do_sparks = 1; - } - else if ( (targ->client->pers.inventory[ITEM_INDEX(item)]) - && mod == MOD_SNIPER ) - { - if ( attacker->client ) - { - gi.cprintf(attacker, PRINT_HIGH, "%s has a Kevlar Vest, too bad you have AP rounds...\n", - targ->client->pers.netname); - gi.cprintf(targ, PRINT_HIGH, "Kevlar Vest absorbed some of %s's AP sniper round\n", - attacker->client->pers.netname); - } - damage = damage * .325; - damage_type = LOC_CDAM; - } - else - { - damage = damage * .65; - gi.cprintf(targ, PRINT_HIGH, "Chest damage\n"); - if (attacker->client) - gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the chest\n", targ->client->pers.netname); - damage_type = LOC_CDAM; - } - - } - /*else - { - - // no mod to damage - gi.cprintf(targ, PRINT_HIGH, "Head damage\n"); - if (attacker->client) - gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the head\n", targ->client->pers.netname); - damage_type = LOC_HDAM; - gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/headshot.wav"), 1, ATTN_NORM, 0); - } */ - } - - } - } - - - if ( damage_type && !instant_dam) // bullets but not vest hits - { - vec3_t temp; - vec3_t temporig; - //vec3_t forward; - VectorMA (targ->s.origin, 50, dir, temp); - //AngleVectors (attacker->client->v_angle, forward, NULL, NULL); - VectorScale( dir, 20, temp); - VectorAdd( point, temp, temporig ); - if ( mod != MOD_SNIPER ) - spray_blood (targ, temporig, dir, damage, mod ); - else - { - spray_sniper_blood( targ, temporig, dir ); - } - } - - if ( mod == MOD_FALLING && !(targ->flags & FL_GODMODE)) - { - if ( targ->client && targ->health > 0) - { - gi.cprintf(targ, PRINT_HIGH, "Leg damage\n"); - targ->client->leg_damage = 1; - targ->client->leghits++; - // bleeding = 1; for testing - } - } - - - // friendly fire avoidance - // if enabled you can't hurt teammates (but you can hurt yourself) - // knockback still occurs - if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) - { - if (OnSameTeam (targ, attacker)) - { - if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) - damage = 0; - else - mod |= MOD_FRIENDLY_FIRE; - } - } - meansOfDeath = mod; - locOfDeath = damage_type; // location - - - - - - - - client = targ->client; - - if (dflags & DAMAGE_BULLET) - te_sparks = TE_BULLET_SPARKS; - else - te_sparks = TE_SPARKS; - - VectorNormalize(dir); - - // bonus damage for suprising a monster - // if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) - // damage *= 2; - - if (targ->flags & FL_NO_KNOCKBACK) - knockback = 0; - - // figure momentum add - if (!(dflags & DAMAGE_NO_KNOCKBACK)) - { - if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) - { - vec3_t kvel, flydir; - float mass; - - if ( mod != MOD_FALLING ) - { - VectorCopy(dir, flydir); - flydir[2] += 0.4; - } - - if (targ->mass < 50) - mass = 50; - else - mass = targ->mass; - - if (targ->client && attacker == targ) - VectorScale (flydir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... - else - VectorScale (flydir, 500.0 * (float)knockback / mass, kvel); - - // FB - //if (mod == MOD_KICK ) - //{ - // kvel[2] = 0; - //} - - VectorAdd (targ->velocity, kvel, targ->velocity); - } - } - - take = damage; - save = 0; - - // check for godmode - if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) - { - take = 0; - save = damage; - SpawnDamage (te_sparks, point, normal, save); - } - - // zucc don't need this stuff, but to remove it need to change how damagefeedback works with colors - - // check for invincibility - if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) - { - if (targ->pain_debounce_time < level.time) - { - gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); - targ->pain_debounce_time = level.time + 2; - } - take = 0; - save = damage; - } - - psave = CheckPowerArmor (targ, point, normal, take, dflags); - take -= psave; - - asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); - take -= asave; - - //treat cheat/powerup savings the same as armor - asave += save; - - // team damage avoidance - if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) - return; - - if ( (mod == MOD_M3) || (mod == MOD_HC) || (mod == MOD_HELD_GRENADE) || - (mod == MOD_HG_SPLASH) || (mod == MOD_G_SPLASH) ) - { - bleeding = 1; - instant_dam = 0; - - } - -/* if ( (mod == MOD_M3) || (mod == MOD_HC) ) - { - instant_dam = 1; - remain = take % 2; - take = (int)(take/2); // balances out difference in how action and axshun handle damage/bleeding - - } - */ - // do the damage - if (take) - { - // zucc added check for stopAP, if it hit a vest we want sparks - if (((targ->svflags & SVF_MONSTER) || (client)) && !do_sparks ) - SpawnDamage (TE_BLOOD, point, normal, take); - else - SpawnDamage (te_sparks, point, normal, take); - - // all things that have at least some instantaneous damage, i.e. bruising/falling - if ( instant_dam ) - targ->health = targ->health - take; - - if (targ->health <= 0) - { - if ((targ->svflags & SVF_MONSTER) || (client)) - targ->flags |= FL_NO_KNOCKBACK; - Killed (targ, inflictor, attacker, take, point); - return; - } - } - - if (targ->svflags & SVF_MONSTER) - { - M_ReactToDamage (targ, attacker); - if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) - { - targ->pain (targ, attacker, knockback, take); - // nightmare mode monsters don't go into pain frames often - if (skill->value == 3) - targ->pain_debounce_time = level.time + 5; - } - } - else if (client) - { - if (!(targ->flags & FL_GODMODE) && (take)) - targ->pain (targ, attacker, knockback, take); - } - else if (take) - { - if (targ->pain) - targ->pain (targ, attacker, knockback, take); - } - - // add to the damage inflicted on a player this frame - // the total will be turned into screen blends and view angle kicks - // at the end of the frame - if (client) - { - client->damage_parmor += psave; - client->damage_armor += asave; - client->damage_blood += take; - client->damage_knockback += knockback; - //zucc handle adding bleeding here - if ( damage_type && bleeding ) // one of the hit location weapons - { - /* zucc add in partial bleeding, changed - if ( client->bleeding < 4*damage*BLEED_TIME ) - { - client->bleeding = 4*damage*BLEED_TIME + client->bleeding/2; - - } - else - { - client->bleeding += damage*BLEED_TIME*2; - - }*/ - client->bleeding += damage*BLEED_TIME; - VectorSubtract (point, targ->absmax, targ->client->bleedloc_offset); - //VectorSubtract(point, targ->s.origin, client->bleedloc_offset); - - } - else if ( bleeding ) - { - /* - if ( client->bleeding < damage*BLEED_TIME ) - { - client->bleeding = damage*BLEED_TIME; - //client->bleedcount = 0; - }*/ - client->bleeding += damage*BLEED_TIME; - VectorSubtract (point, targ->absmax, targ->client->bleedloc_offset); - //VectorSubtract(point, targ->s.origin, client->bleedloc_offset); - - } - if ( attacker->client ) - { - client->attacker = attacker; - client->attacker_mod = mod; - client->attacker_loc = damage_type; - client->push_timeout = 50; - //VectorCopy(dir, client->bleeddir ); - //VectorCopy(point, client->bleedpoint ); - //VectorCopy(normal, client->bleednormal); - - } - - VectorCopy (point, client->damage_from); - } -} - - -/* -============ -T_RadiusDamage -============ -*/ -void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) -{ - float points; - edict_t *ent = NULL; - vec3_t v; - vec3_t dir; - - while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) - { - if (ent == ignore) - continue; - if (!ent->takedamage) - continue; - - VectorAdd (ent->mins, ent->maxs, v); - VectorMA (ent->s.origin, 0.5, v, v); - VectorSubtract (inflictor->s.origin, v, v); - points = damage - 0.5 * VectorLength (v); - //zucc reduce damage for crouching, max is 32 when standing - if (ent->maxs[2] < 20 ) - { - points = points * 0.5; // hefty reduction in damage - } - //if (ent == attacker) - //points = points * 0.5; - if (points > 0) - { - if (CanDamage (ent, inflictor)) - { - VectorSubtract (ent->s.origin, inflictor->s.origin, dir); - // zucc scaled up knockback(kick) of grenades - T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)(points*.75), (int)(points*.75), DAMAGE_RADIUS, mod); - } - } - } -} +// g_combat.c + +#include "g_local.h" +#include "cgf_sfx_glass.h" + +void Add_TeamWound( edict_t *attacker, edict_t *victim, int mod); + +/* +============ +CanDamage + + Returns true if the inflictor can directly damage the target. Used for + explosions and melee attacks. + ============ +*/ +qboolean CanDamage (edict_t *targ, edict_t *inflictor) +{ + vec3_t dest; + trace_t trace; + + // bmodels need special checking because their origin is 0,0,0 +//GLASS FX + if ((targ->movetype == MOVETYPE_PUSH) + || + ((targ->movetype == MOVETYPE_FLYMISSILE) + && + (0 == Q_stricmp("func_explosive", targ->classname)) + ) + ) +//GLASS FX + { + VectorAdd (targ->absmin, targ->absmax, dest); + VectorScale (dest, 0.5, dest); + PRETRACE(); + trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); + POSTTRACE(); + if (trace.fraction == 1.0) + return true; + if (trace.ent == targ) + return true; + return false; + } + + PRETRACE(); + trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID); + POSTTRACE(); + if (trace.fraction == 1.0) + return true; + + VectorCopy (targ->s.origin, dest); + dest[0] += 15.0; + dest[1] += 15.0; + PRETRACE(); + trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); + POSTTRACE(); + if (trace.fraction == 1.0) + return true; + + VectorCopy (targ->s.origin, dest); + dest[0] += 15.0; + dest[1] -= 15.0; + PRETRACE(); + trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); + POSTTRACE(); + if (trace.fraction == 1.0) + return true; + + VectorCopy (targ->s.origin, dest); + dest[0] -= 15.0; + dest[1] += 15.0; + PRETRACE(); + trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); + POSTTRACE(); + if (trace.fraction == 1.0) + return true; + + VectorCopy (targ->s.origin, dest); + dest[0] -= 15.0; + dest[1] -= 15.0; + PRETRACE(); + trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); + POSTTRACE(); + if (trace.fraction == 1.0) + return true; + + + return false; +} + + +/* +============ +Killed +============ +*/ +void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) +{ + if (targ->health < -999) + targ->health = -999; + + if ( targ->client ) + { + targ->client->bleeding = 0; + //targ->client->bleedcount = 0; + targ->client->bleed_remain = 0; + } + + targ->enemy = attacker; + + if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) + { + // targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type + if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) + { + level.killed_monsters++; + if (coop->value && attacker->client) + attacker->client->resp.score++; + // medics won't heal monsters that they kill themselves + if (strcmp(attacker->classname, "monster_medic") == 0) + targ->owner = attacker; + } + } + + if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE) + { // doors, triggers, etc + targ->die (targ, inflictor, attacker, damage, point); + return; + } + + if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) + { + targ->touch = NULL; + monster_death_use (targ); + } + + targ->die (targ, inflictor, attacker, damage, point); +} + + +/* +================ +SpawnDamage +================ +*/ +void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage) +{ + if (damage > 255) + damage = 255; + gi.WriteByte (svc_temp_entity); + gi.WriteByte (type); + // gi.WriteByte (damage); + gi.WritePosition (origin); + gi.WriteDir (normal); + gi.multicast (origin, MULTICAST_PVS); +} + + +/* +============ +T_Damage + + targ entity that is being damaged + inflictor entity that is causing the damage + attacker entity that caused the inflictor to damage targ + example: targ=monster, inflictor=rocket, attacker=player + + dir direction of the attack + point point at which the damage is being inflicted + normal normal vector from that point + damage amount of damage being inflicted + knockback force to be applied against targ as a result of the damage + + dflags these flags are used to control how T_Damage works + DAMAGE_RADIUS damage was indirect (from a nearby explosion) + DAMAGE_NO_ARMOR armor does not protect from this damage + DAMAGE_ENERGY damage is from an energy based weapon + DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles + DAMAGE_BULLET damage is from a bullet (used for ricochets) + DAMAGE_NO_PROTECTION kills godmode, armor, everything + ============ +*/ +static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags) +{ + gclient_t *client; + int save; + int power_armor_type; + int index; + int damagePerCell; + int pa_te_type; + int power; + int power_used; + + if (!damage) + return 0; + + client = ent->client; + + if (dflags & DAMAGE_NO_ARMOR) + return 0; + + if (client) + { + power_armor_type = PowerArmorType (ent); + if (power_armor_type != POWER_ARMOR_NONE) + { + index = ITEM_INDEX(FindItem("Cells")); + power = client->pers.inventory[index]; + } + } + else if (ent->svflags & SVF_MONSTER) + { + power_armor_type = ent->monsterinfo.power_armor_type; + power = ent->monsterinfo.power_armor_power; + } + else + return 0; + + if (power_armor_type == POWER_ARMOR_NONE) + return 0; + if (!power) + return 0; + + if (power_armor_type == POWER_ARMOR_SCREEN) + { + vec3_t vec; + float dot; + vec3_t forward; + + // only works if damage point is in front + AngleVectors (ent->s.angles, forward, NULL, NULL); + VectorSubtract (point, ent->s.origin, vec); + VectorNormalize (vec); + dot = DotProduct (vec, forward); + if (dot <= 0.3) + return 0; + + damagePerCell = 1; + pa_te_type = TE_SCREEN_SPARKS; + damage = damage / 3; + } + else + { + damagePerCell = 2; + pa_te_type = TE_SHIELD_SPARKS; + damage = (2 * damage) / 3; + } + + save = power * damagePerCell; + if (!save) + return 0; + if (save > damage) + save = damage; + + SpawnDamage (pa_te_type, point, normal, save); + ent->powerarmor_time = level.time + 0.2; + + power_used = save / damagePerCell; + + if (client) + client->pers.inventory[index] -= power_used; + else + ent->monsterinfo.power_armor_power -= power_used; + return save; +} + +static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags) +{ + gclient_t *client; + int save; + int index; + gitem_t *armor; + + if (!damage) + return 0; + + client = ent->client; + + if (!client) + return 0; + + if (dflags & DAMAGE_NO_ARMOR) + return 0; + + index = ArmorIndex (ent); + if (!index) + return 0; + + armor = GetItemByIndex (index); + + if (dflags & DAMAGE_ENERGY) + save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage); + else + save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage); + if (save >= client->pers.inventory[index]) + save = client->pers.inventory[index]; + + if (!save) + return 0; + + client->pers.inventory[index] -= save; + SpawnDamage (te_sparks, point, normal, save); + + return save; +} + +void M_ReactToDamage (edict_t *targ, edict_t *attacker) +{ + if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) + return; + + if (attacker == targ || attacker == targ->enemy) + return; + + // if we are a good guy monster and our attacker is a player + // or another good guy, do not get mad at them + if (targ->monsterinfo.aiflags & AI_GOOD_GUY) + { + if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) + return; + } + + // we now know that we are not both good guys + + // if attacker is a client, get mad at them because he's good and we're not + if (attacker->client) + { + // this can only happen in coop (both new and old enemies are clients) + // only switch if can't see the current enemy + if (targ->enemy && targ->enemy->client) + { + if (visible(targ, targ->enemy)) + { + targ->oldenemy = attacker; + return; + } + targ->oldenemy = targ->enemy; + } + targ->enemy = attacker; + if (!(targ->monsterinfo.aiflags & AI_DUCKED)) + FoundTarget (targ); + return; + } + + // it's the same base (walk/swim/fly) type and a different classname and it's not a tank + // (they spray too much), get mad at them + if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && + (strcmp (targ->classname, attacker->classname) != 0) && + (strcmp(attacker->classname, "monster_tank") != 0) && + (strcmp(attacker->classname, "monster_supertank") != 0) && + (strcmp(attacker->classname, "monster_makron") != 0) && + (strcmp(attacker->classname, "monster_jorg") != 0) ) + { + if (targ->enemy) + if (targ->enemy->client) + targ->oldenemy = targ->enemy; + targ->enemy = attacker; + if (!(targ->monsterinfo.aiflags & AI_DUCKED)) + FoundTarget (targ); + } + else + // otherwise get mad at whoever they are mad at (help our buddy) + { + if (targ->enemy) + if (targ->enemy->client) + targ->oldenemy = targ->enemy; + targ->enemy = attacker->enemy; + if (!(targ->monsterinfo.aiflags & AI_DUCKED)) + FoundTarget (targ); + } +} + +qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker) +{ + //FIXME make the next line real and uncomment this block + // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team)) + return false; +} + + + +void BloodSprayThink (edict_t *self) +{ + + +/* if ( self->dmg > 0 ) +{ +self->dmg -= 10; +// SpawnDamage (TE_BLOOD, self->s.origin, self->movedir, self->dmg); +gi.WriteByte (svc_temp_entity); +gi.WriteByte (TE_SPLASH); +gi.WriteByte (6); +gi.WritePosition (self->s.origin); +gi.WriteDir (self->movedir); +gi.WriteByte (6); //blood +gi.multicast (self->s.origin, MULTICAST_PVS); + + } + else + { + self->think = G_FreeEdict; + } + + self->nextthink = level.time + 0.1; + gi.linkentity (self); + */ + + G_FreeEdict(self); + +} + +void blood_spray_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + if (other == ent->owner) + return; + ent->think = G_FreeEdict; + ent->nextthink = level.time + 0.1; +} + + + +void spray_blood (edict_t *self, vec3_t start, vec3_t dir, int damage, int mod ) +{ + edict_t *blood; + int speed; + + switch(mod) + { + case MOD_MK23: + speed = 1800; + break; + case MOD_MP5: + speed = 1500; + break; + case MOD_M4: + speed = 2400; + break; + case MOD_KNIFE: + speed = 0; + break; + case MOD_KNIFE_THROWN: + speed = 0; + break; + case MOD_DUAL: + speed = 1800; + break; + case MOD_SNIPER: + speed = 4000; + break; + default: + speed = 1800; + } + + + + + blood = G_Spawn(); + VectorNormalize(dir); + VectorCopy (start, blood->s.origin); + VectorCopy (dir, blood->movedir); + vectoangles (dir, blood->s.angles); + VectorScale (dir, speed, blood->velocity); + blood->movetype = MOVETYPE_BLOOD; + blood->clipmask = MASK_SHOT; + blood->solid = SOLID_BBOX; + blood->s.effects |= EF_GIB; + VectorClear (blood->mins); + VectorClear (blood->maxs); + blood->s.modelindex = gi.modelindex ("sprites/null.sp2"); + blood->owner = self; + blood->nextthink = level.time + speed/1000;//3.2; + blood->touch = blood_spray_touch; + blood->think = BloodSprayThink; + blood->dmg = damage; + blood->classname = "blood_spray"; + + gi.linkentity (blood); +} + + +// zucc based on some code in Action Quake +void spray_sniper_blood( edict_t *self, vec3_t start, vec3_t dir ) +{ + vec3_t forward; + int mod = MOD_SNIPER; + + VectorCopy (dir, forward); + + forward[2] += .03; + + spray_blood(self, start, forward, 0, mod); + + + VectorCopy (dir, forward); + forward[2] -= .03; + spray_blood(self, start, forward, 0, mod); + + + VectorCopy (dir, forward); + if ( (forward[0] > 0) && (forward[1] > 0) ) + { + forward[0] -= .03; + forward[1] += .03; + } + if ( (forward[0] > 0) && (forward[1] < 0) ) + { + forward[0] += .03; + forward[1] += .03; + } + if ( (forward[0] < 0) && (forward[1] > 0) ) + { + forward[0] -= .03; + forward[1] -= .03; + } + if ( (forward[0] < 0) && (forward[1] < 0) ) + { + forward[0] += .03; + forward[1] -= .03; + } + spray_blood(self, start, forward, 0, mod); + + + VectorCopy (dir, forward); + if ( (forward[0] > 0) && (forward[1] > 0) ) + { + forward[0] += .03; + forward[1] -= .03; + } + if ( (forward[0] > 0) && (forward[1] < 0) ) + { + forward[0] -= .03; + forward[1] -= .03; + } + if ( (forward[0] < 0) && (forward[1] > 0) ) + { + forward[0] += .03; + forward[1] += .03; + } + if ( (forward[0] < 0) && (forward[1] < 0) ) + { + forward[0] -= .03; + forward[1] += .03; + } + spray_blood(self, start, forward, 0, mod); + + VectorCopy (dir, forward); + spray_blood(self, start, forward, 0, mod); + +} + + +void VerifyHeadShot( vec3_t point, vec3_t dir, float height, vec3_t newpoint) +{ + vec3_t normdir; + vec3_t normdir2; + + + VectorNormalize2(dir, normdir); + VectorScale( normdir, height, normdir2 ); + VectorAdd( point, normdir2, newpoint ); +} + + + +// zucc adding location hit code +// location hit code based off ideas by pyromage and shockman + +#define LEG_DAMAGE (height/2.2) - abs(targ->mins[2]) - 3 +#define STOMACH_DAMAGE (height/1.8) - abs(targ->mins[2]) +#define CHEST_DAMAGE (height/1.4) - abs(targ->mins[2]) + +#define HEAD_HEIGHT 12.0 +qboolean IsFemale (edict_t *ent); + + +void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) +{ + gclient_t *client; + int take; + int save; + int asave; + int psave; + int te_sparks; + int do_sparks = 0; + int damage_type = 0; // used for MOD later + int bleeding = 0; // damage causes bleeding + int head_success = 0; + int instant_dam = 1; + + float z_rel; + int height; + + gitem_t* item; + + float from_top; + + vec3_t line; + vec_t dist; + float targ_maxs2; //FB 6/1/99 + + // do this before teamplay check + if (!targ->takedamage) + return; + + //FIREBLADE + if (teamplay->value && mod != MOD_TELEFRAG) + { + if (lights_camera_action) + return; + + if (targ != attacker && targ->client && attacker->client && + (targ->client->resp.team == attacker->client->resp.team && + ((int)(dmflags->value) & (DF_NO_FRIENDLY_FIRE)))) + return; + } + //FIREBLADE + + + // damage reduction for shotgun + // if far away, reduce it to original action levels + if ( mod == MOD_M3 ) + { + VectorSubtract(targ->s.origin, inflictor->s.origin, line ); + dist = VectorLength( line ); + if ( dist > 450.0 ) + { + damage = damage - 2; + } + + } + + item = FindItem(KEV_NAME); + + targ_maxs2 = targ->maxs[2]; + if (targ_maxs2 == 4) + targ_maxs2 = CROUCHING_MAXS2; //FB 6/1/99 + + height = abs(targ->mins[2]) + targ_maxs2; + + // locational damage code + // base damage is head shot damage, so all the scaling is downwards + if (targ->client) + { + if (!((targ != attacker) && + ((deathmatch->value && ((int)(dmflags->value) + & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && + (attacker && attacker->client && + OnSameTeam (targ, attacker) && + ((int)(dmflags->value) & (DF_NO_FRIENDLY_FIRE))))) + { + + if ((mod == MOD_MK23) || + (mod == MOD_MP5) || + (mod == MOD_M4) || + (mod == MOD_SNIPER) || + (mod == MOD_DUAL) || + (mod == MOD_KNIFE) || + (mod == MOD_KNIFE_THROWN)) + { + z_rel = point[2] - targ->s.origin[2]; + from_top = targ_maxs2 - z_rel; + if (from_top < 0.0) //FB 6/1/99 + from_top = 0.0; //Slightly negative values were being handled wrong + bleeding = 1; + instant_dam = 0; + + // damage reduction for longer range pistol shots + if ( mod == MOD_MK23 || mod == MOD_DUAL ) + { + VectorSubtract(targ->s.origin, inflictor->s.origin, line ); + dist = VectorLength( line ); + if ( dist > 600.0 && dist < 1400.0 ) + { + damage = (int)(damage*2/3); + } + else if ( dist > 1400.0 ) + damage = (int)(damage*1/2); + } + + + //gi.cprintf(targ, PRINT_HIGH, "z_rel is %f\n leg: %f stomach: %f chest: %f\n", z_rel, LEG_DAMAGE, STOMACH_DAMAGE, CHEST_DAMAGE ); + //gi.cprintf(targ, PRINT_HIGH, "point[2]: %f targ->s.origin[2]: %f height: %d\n", point[2], targ->s.origin[2], height ); + //gi.cprintf(targ, PRINT_HIGH, "abs(trag->min[2]): %d targ_max[2] %d\n", (int)abs(targ->mins[2]), (int)targ_maxs2); + //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[2]): %d targ_max[2] %d\n", (int)abs(targ->mins[2]), (int)targ_maxs2); + //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[0]): %d targ_max[0] %d\n", (int)abs(targ->mins[0]), (int)targ->maxs[0]); + //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[1]): %d targ_max[1] %d\n", (int)abs(targ->mins[1]), (int)targ->maxs[1]); + + + if ( from_top < 2*HEAD_HEIGHT ) + { + vec3_t new_point; + VerifyHeadShot( point, dir, HEAD_HEIGHT, new_point ); + VectorSubtract( new_point, targ->s.origin, new_point ); + //gi.cprintf(attacker, PRINT_HIGH, "z: %d y: %d x: %d\n", (int)(targ_maxs2 - new_point[2]),(int)(new_point[1]) , (int)(new_point[0]) ); + + if ( (targ_maxs2 - new_point[2]) < HEAD_HEIGHT + && (abs(new_point[1])) < HEAD_HEIGHT*.8 + && (abs(new_point[0])) < HEAD_HEIGHT*.8 ) + + { + head_success = 1; + } + } + + if ( head_success ) + { + + damage = damage*1.8 + 1; + gi.cprintf(targ, PRINT_HIGH, "Head damage\n"); + if (attacker->client) + gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the head\n", targ->client->pers.netname); + damage_type = LOC_HDAM; + if ( mod != MOD_KNIFE && mod != MOD_KNIFE_THROWN ) + gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/headshot.wav"), 1, ATTN_NORM, 0); + //else + // gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/glurp.wav"), 1, ATTN_NORM, 0); + } + + else if (z_rel < LEG_DAMAGE) + { + damage = damage * .25; + gi.cprintf(targ, PRINT_HIGH, "Leg damage\n"); + if (attacker->client) + gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the legs\n", targ->client->pers.netname); + damage_type = LOC_LDAM; + targ->client->leg_damage = 1; + targ->client->leghits++; + } + else if (z_rel < STOMACH_DAMAGE) + { + damage = damage * .4; + gi.cprintf(targ, PRINT_HIGH, "Stomach damage\n"); + if (attacker->client) + gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the stomach\n", targ->client->pers.netname); + damage_type = LOC_SDAM; + } + else //(z_rel < CHEST_DAMAGE) + { + if ( (targ->client->pers.inventory[ITEM_INDEX(item)]) + && mod != MOD_KNIFE + && mod != MOD_KNIFE_THROWN + && mod != MOD_SNIPER ) + { + if (attacker->client) + { + gi.cprintf(attacker, PRINT_HIGH, "%s has a Kevlar Vest - AIM FOR THE HEAD!\n", + targ->client->pers.netname); + gi.cprintf(targ, PRINT_HIGH, "Kevlar Vest absorbed most of %s's shot\n", + attacker->client->pers.netname); + /* + if (IsFemale(targ)) + gi.cprintf(attacker, PRINT_HIGH, "You bruised %s through her Kevlar Vest\n", targ->client->pers.netname); + else + gi.cprintf(attacker, PRINT_HIGH, "You bruised %s through his Kevlar Vest\n", targ->client->pers.netname); + */ + } + gi.sound(targ, CHAN_ITEM, gi.soundindex("misc/vest.wav"), 1, ATTN_NORM, 0); + damage = (int)(damage/10); + damage_type = LOC_CDAM; + bleeding = 0; + instant_dam = 1; + stopAP = 1; do_sparks = 1; + } + else if ( (targ->client->pers.inventory[ITEM_INDEX(item)]) + && mod == MOD_SNIPER ) + { + if ( attacker->client ) + { + gi.cprintf(attacker, PRINT_HIGH, "%s has a Kevlar Vest, too bad you have AP rounds...\n", + targ->client->pers.netname); + gi.cprintf(targ, PRINT_HIGH, "Kevlar Vest absorbed some of %s's AP sniper round\n", + attacker->client->pers.netname); + } + damage = damage * .325; + damage_type = LOC_CDAM; + } + else + { + damage = damage * .65; + gi.cprintf(targ, PRINT_HIGH, "Chest damage\n"); + if (attacker->client) + gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the chest\n", targ->client->pers.netname); + damage_type = LOC_CDAM; + } + + } + /*else + { + + // no mod to damage + gi.cprintf(targ, PRINT_HIGH, "Head damage\n"); + if (attacker->client) + gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the head\n", targ->client->pers.netname); + damage_type = LOC_HDAM; + gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/headshot.wav"), 1, ATTN_NORM, 0); + } */ + } + if (team_round_going && attacker->client && targ != attacker && OnSameTeam(targ, attacker)) + { + Add_TeamWound(attacker, targ, mod); + } + } + } + + + if ( damage_type && !instant_dam) // bullets but not vest hits + { + vec3_t temp; + vec3_t temporig; + //vec3_t forward; + VectorMA (targ->s.origin, 50, dir, temp); + //AngleVectors (attacker->client->v_angle, forward, NULL, NULL); + VectorScale( dir, 20, temp); + VectorAdd( point, temp, temporig ); + if ( mod != MOD_SNIPER ) + spray_blood (targ, temporig, dir, damage, mod ); + else + { + spray_sniper_blood( targ, temporig, dir ); + } + } + + if ( mod == MOD_FALLING && !(targ->flags & FL_GODMODE)) + { + if ( targ->client && targ->health > 0) + { + gi.cprintf(targ, PRINT_HIGH, "Leg damage\n"); + targ->client->leg_damage = 1; + targ->client->leghits++; + // bleeding = 1; for testing + } + } + + + // friendly fire avoidance + // if enabled you can't hurt teammates (but you can hurt yourself) + // knockback still occurs + if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) + { + if (OnSameTeam (targ, attacker)) + { + if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) + damage = 0; + else + mod |= MOD_FRIENDLY_FIRE; + } + } + + meansOfDeath = mod; + locOfDeath = damage_type; // location + + client = targ->client; + + if (dflags & DAMAGE_BULLET) + te_sparks = TE_BULLET_SPARKS; + else + te_sparks = TE_SPARKS; + + VectorNormalize(dir); + + // bonus damage for suprising a monster + // if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) + // damage *= 2; + + if (targ->flags & FL_NO_KNOCKBACK) + knockback = 0; + + // figure momentum add + if (!(dflags & DAMAGE_NO_KNOCKBACK)) + { + if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) + { + vec3_t kvel, flydir; + float mass; + + if ( mod != MOD_FALLING ) + { + VectorCopy(dir, flydir); + flydir[2] += 0.4; + } + + if (targ->mass < 50) + mass = 50; + else + mass = targ->mass; + + if (targ->client && attacker == targ) + VectorScale (flydir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... + else + VectorScale (flydir, 500.0 * (float)knockback / mass, kvel); + + // FB + //if (mod == MOD_KICK ) + //{ + // kvel[2] = 0; + //} + + VectorAdd (targ->velocity, kvel, targ->velocity); + } + } + + take = damage; + save = 0; + + // check for godmode + if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) + { + take = 0; + save = damage; + SpawnDamage (te_sparks, point, normal, save); + } + + // zucc don't need this stuff, but to remove it need to change how damagefeedback works with colors + + // check for invincibility + if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) + { + if (targ->pain_debounce_time < level.time) + { + gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); + targ->pain_debounce_time = level.time + 2; + } + take = 0; + save = damage; + } + + psave = CheckPowerArmor (targ, point, normal, take, dflags); + take -= psave; + + asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); + take -= asave; + + //treat cheat/powerup savings the same as armor + asave += save; + + // team damage avoidance + if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) + return; + if ( (mod == MOD_M3) + || (mod == MOD_HC) + || (mod == MOD_HELD_GRENADE) + || (mod == MOD_HG_SPLASH) + || (mod == MOD_G_SPLASH) + || (mod == MOD_BREAKINGGLASS) + ) + { +//FB 6/3/99 - shotgun damage report stuff + int playernum = targ - g_edicts; + playernum--; + if (playernum >= 0 && + playernum <= game.maxclients - 1) + *(took_damage + playernum) = 1; +//FB 6/3/99 + + bleeding = 1; + instant_dam = 0; + } + + /* if ( (mod == MOD_M3) || (mod == MOD_HC) ) + { + instant_dam = 1; + remain = take % 2; + take = (int)(take/2); // balances out difference in how action and axshun handle damage/bleeding + + } + */ + // do the damage + if (take) + { + // zucc added check for stopAP, if it hit a vest we want sparks + if (((targ->svflags & SVF_MONSTER) || (client)) && !do_sparks ) + SpawnDamage (TE_BLOOD, point, normal, take); + else + SpawnDamage (te_sparks, point, normal, take); + + // all things that have at least some instantaneous damage, i.e. bruising/falling + if ( instant_dam ) + targ->health = targ->health - take; + + if (targ->health <= 0) + { + if ((targ->svflags & SVF_MONSTER) || (client)) + targ->flags |= FL_NO_KNOCKBACK; + Killed (targ, inflictor, attacker, take, point); + return; + } + } + + if (targ->svflags & SVF_MONSTER) + { + M_ReactToDamage (targ, attacker); + if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) + { + targ->pain (targ, attacker, knockback, take); + // nightmare mode monsters don't go into pain frames often + if (skill->value == 3) + targ->pain_debounce_time = level.time + 5; + } + } + else if (client) + { + if (!(targ->flags & FL_GODMODE) && (take)) + targ->pain (targ, attacker, knockback, take); + } + else if (take) + { + if (targ->pain) + targ->pain (targ, attacker, knockback, take); + } + + // add to the damage inflicted on a player this frame + // the total will be turned into screen blends and view angle kicks + // at the end of the frame + if (client) + { + client->damage_parmor += psave; + client->damage_armor += asave; + client->damage_blood += take; + client->damage_knockback += knockback; + //zucc handle adding bleeding here + if ( damage_type && bleeding ) // one of the hit location weapons + { + /* zucc add in partial bleeding, changed + if ( client->bleeding < 4*damage*BLEED_TIME ) + { + client->bleeding = 4*damage*BLEED_TIME + client->bleeding/2; + + } + else + { + client->bleeding += damage*BLEED_TIME*2; + + }*/ + client->bleeding += damage*BLEED_TIME; + VectorSubtract (point, targ->absmax, targ->client->bleedloc_offset); + //VectorSubtract(point, targ->s.origin, client->bleedloc_offset); + + } + else if ( bleeding ) + { + /* + if ( client->bleeding < damage*BLEED_TIME ) + { + client->bleeding = damage*BLEED_TIME; + //client->bleedcount = 0; + }*/ + client->bleeding += damage*BLEED_TIME; + VectorSubtract (point, targ->absmax, targ->client->bleedloc_offset); + //VectorSubtract(point, targ->s.origin, client->bleedloc_offset); + + } + if ( attacker->client ) + { + attacker->client->resp.damage_dealt += damage; + client->attacker = attacker; + client->attacker_mod = mod; + client->attacker_loc = damage_type; + client->push_timeout = 50; + //VectorCopy(dir, client->bleeddir ); + //VectorCopy(point, client->bleedpoint ); + //VectorCopy(normal, client->bleednormal); + + } + + VectorCopy (point, client->damage_from); + } +} + + +/* +============ +T_RadiusDamage +============ +*/ +void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) +{ + float points; + edict_t *ent = NULL; + vec3_t v; + vec3_t dir; + + while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) + { + if (ent == ignore) + continue; + if (!ent->takedamage) + continue; + + VectorAdd (ent->mins, ent->maxs, v); + VectorMA (ent->s.origin, 0.5, v, v); + VectorSubtract (inflictor->s.origin, v, v); + points = damage - 0.5 * VectorLength (v); + //zucc reduce damage for crouching, max is 32 when standing + if (ent->maxs[2] < 20 ) + { + points = points * 0.5; // hefty reduction in damage + } + //if (ent == attacker) + //points = points * 0.5; + if (points > 0) + { + #ifdef _DEBUG + if (0 == Q_stricmp(ent->classname, "func_explosive")) + { + CGF_SFX_ShootBreakableGlass(ent, inflictor, 0, mod); + } + else + #endif + if (CanDamage (ent, inflictor)) + { + VectorSubtract (ent->s.origin, inflictor->s.origin, dir); + // zucc scaled up knockback(kick) of grenades + T_Damage (ent, inflictor, attacker, dir, ent->s.origin, vec3_origin, (int)(points*.75), (int)(points*.75), DAMAGE_RADIUS, mod); + } + } + } +} diff --git a/g_items.c b/g_items.c index 638e1df..1cb1d4c 100644 --- a/g_items.c +++ b/g_items.c @@ -985,8 +985,10 @@ edict_t *Drop_Item (edict_t *ent, gitem_t *item) AngleVectors (ent->client->v_angle, forward, right, NULL); VectorSet(offset, 24, 0, -16); G_ProjectSource (ent->s.origin, offset, forward, right, dropped->s.origin); - trace = do_trace (ent->s.origin, dropped->mins, dropped->maxs, + PRETRACE(); + trace = gi.trace (ent->s.origin, dropped->mins, dropped->maxs, dropped->s.origin, ent, CONTENTS_SOLID); + POSTTRACE(); VectorCopy (trace.endpos, dropped->s.origin); } else @@ -1065,7 +1067,9 @@ void droptofloor (edict_t *ent) v = tv(0,0,-128); VectorAdd (ent->s.origin, v, dest); - tr = do_trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID); + PRETRACE(); + tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID); + POSTTRACE(); if (tr.startsolid) { gi.dprintf ("droptofloor: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); diff --git a/g_local.h b/g_local.h index ebdaa17..2494106 100644 --- a/g_local.h +++ b/g_local.h @@ -519,16 +519,21 @@ extern cvar_t *deathmatch; extern cvar_t *coop; extern cvar_t *dmflags; //FIREBLADE +extern cvar_t *needpass; extern cvar_t *hostname; extern cvar_t *teamplay; extern cvar_t *radiolog; extern cvar_t *motd_time; extern cvar_t *actionmaps; extern cvar_t *roundtimelimit; +extern cvar_t *maxteamkills; +extern cvar_t *tkbanrounds; +extern cvar_t *twbanrounds; +extern cvar_t *limchasecam; extern cvar_t *roundlimit; -extern cvar_t *skipmotd; -extern cvar_t *nohud; -extern cvar_t *noscore; +extern cvar_t *skipmotd; +extern cvar_t *nohud; +extern cvar_t *noscore; extern cvar_t *actionversion; //FIREBLADE extern cvar_t *skill; @@ -894,7 +899,7 @@ typedef struct //FIREBLADE qboolean spectator; - int firing_style; + int firing_style; //FIREBLADE } client_persistant_t; @@ -907,15 +912,9 @@ typedef struct vec3_t cmd_angles; // angles sent over in the last command int game_helpchanged; int helpchanged; - int mk23_mode; // firing mode, semi or auto int sniper_mode; //level of zoom - int mp5_mode; - int m4_mode; - int knife_mode; - int grenade_mode; - int id; // id command on or off - int ir; // ir on or off (only matters if player has ir device, currently bandolier) int kills; // real kills + int damage_dealt; // keep track of damage dealt by player to other players int streak; // kills in a row gitem_t *item; // item for teamplay gitem_t *weapon; // weapon for teamplay @@ -928,9 +927,6 @@ typedef struct radio_queue_entry_t radio_queue[MAX_RADIO_QUEUE_SIZE]; int radio_queue_size; edict_t *radio_partner; // current partner - qboolean radio_partner_mode; // 'radio' command using team or partner - qboolean radio_gender; // radiogender - qboolean radio_power_off; // radio_power edict_t *partner_last_offered_to; // last person I offered a partnership to edict_t *partner_last_offered_from; // last person I received a partnership offer from edict_t *partner_last_denied_from; // last person I denied a partnership offer from @@ -940,6 +936,18 @@ typedef struct int last_motd_refresh; edict_t *last_chase_target; // last person they chased, to resume at the same place later... //FIREBLADE +//Action + int mk23_mode; // firing mode, semi or auto + int mp5_mode; + int m4_mode; + int knife_mode; + int grenade_mode; + int id; // id command on or off + int ir; // ir on or off (only matters if player has ir device, currently bandolier) + qboolean radio_partner_mode; // 'radio' command using team or partner + qboolean radio_gender; // radiogender + qboolean radio_power_off; // radio_power +//--- } client_respawn_t; // this structure is cleared on each PutClientInServer(), @@ -1082,15 +1090,16 @@ struct gclient_s int doortoggle; // set by player with opendoor command - + edict_t* attacker; // keep track of the last person to hit us int attacker_mod; // and how they hit us int attacker_loc; // location of the hit - int push_timeout; // timeout for how long an attacker will get fall death credit - + int push_timeout; // timeout for how long an attacker will get fall death credit + int jumping; - - int reload_attempts; + + int reload_attempts; + int weapon_attempts; //FIREBLADE @@ -1100,6 +1109,22 @@ struct gclient_s qboolean update_chase; int chase_mode; //FIREBLADE + +//AZEROV + // Number of team kills this game + int team_kills; +//AZEROV + +//EEK + // Number of teammate woundings this game and a "before attack" tracker + int team_wounds; + int team_wounds_before; + int ff_warning; + + // IP address of this host to be collected at Connection time. + // (getting at it later seems to be unreliable) + char ipaddr[100]; // changed to 100 -FB +//EEK }; @@ -1266,6 +1291,7 @@ void LaserSightThink (edict_t *self); void SP_LaserSight(edict_t *self, gitem_t *item ); void Cmd_Reload_f (edict_t *ent); void Cmd_New_Reload_f (edict_t *ent); +void Cmd_New_Weapon_f (edict_t *ent); void Cmd_Weapon_f ( edict_t *ent ); void Cmd_OpenDoor_f (edict_t *ent ); void Cmd_Bandage_f ( edict_t *ent ); @@ -1315,8 +1341,8 @@ edict_t *FindEdictByClassnum (char *classname, int classnum); void EjectBlooder (edict_t *self, vec3_t start, vec3_t veloc ); void EjectShell (edict_t *self, vec3_t start, int toggle); -void AddSplat (edict_t *self, vec3_t point, vec3_t direct); -void AddDecal (edict_t *self, vec3_t point, vec3_t direct); +void AddDecal (edict_t *self, trace_t* tr); +void AddSplat (edict_t *self, vec3_t point, trace_t* tr); // weapon names /* bind 2 "use M3 Super 90 Assault Shotgun;" diff --git a/g_main.c b/g_main.c index afcf18e..425c4ba 100644 --- a/g_main.c +++ b/g_main.c @@ -23,11 +23,16 @@ cvar_t *radiolog; cvar_t *motd_time; cvar_t *actionmaps; cvar_t *roundtimelimit; +cvar_t *maxteamkills; +cvar_t *twbanrounds; +cvar_t *tkbanrounds; +cvar_t *limchasecam; cvar_t *roundlimit; -cvar_t *skipmotd; -cvar_t *nohud; -cvar_t *noscore; +cvar_t *skipmotd; +cvar_t *nohud; +cvar_t *noscore; cvar_t *actionversion; +cvar_t *needpass; //FIREBLADE cvar_t *deathmatch; cvar_t *coop; @@ -86,6 +91,7 @@ void ClientUserinfoChanged (edict_t *ent, char *userinfo); void ClientDisconnect (edict_t *ent); void ClientBegin (edict_t *ent); void ClientCommand (edict_t *ent); +void CheckNeedPass (void); void RunEntity (edict_t *ent); void WriteGame (char *filename, qboolean autosave); void ReadGame (char *filename); @@ -205,6 +211,11 @@ EndDMLevel The timelimit or fraglimit has been exceeded ================= */ + +//AZEROV +extern void UnBan_TeamKillers (void); +//AZEROV + void EndDMLevel (void) { edict_t *ent; @@ -258,8 +269,12 @@ void EndDMLevel (void) } //FIREBLADE - ReadMOTDFile(); + ReadMOTDFile(); BeginIntermission (ent); + +//AZEROV + UnBan_TeamKillers (); +//AZEROV } /* @@ -415,7 +430,41 @@ void G_RunFrame (void) // see if it is time to end a deathmatch CheckDMRules (); +//FIREBLADE + CheckNeedPass(); +//FIREBLADE + // build the playerstate_t structures for all players ClientEndServerFrames (); } +//ADDED FROM 3.20 SOURCE -FB +//Commented out spectator_password stuff since we don't have that now. +/* +================= +CheckNeedPass +================= +*/ +void CheckNeedPass (void) +{ + int need; + + // if password or spectator_password has changed, update needpass + // as needed + if (password->modified /*|| spectator_password->modified*/) + { + password->modified = /*spectator_password->modified = */ false; + + need = 0; + + if (*password->string && Q_stricmp(password->string, "none")) + need |= 1; + /* + if (*spectator_password->string && Q_stricmp(spectator_password->string, "none")) + need |= 2; + */ + + gi.cvar_set("needpass", va("%d", need)); + } +} +//FROM 3.20 END diff --git a/g_misc.c b/g_misc.c index 735219a..2a1fb0c 100644 --- a/g_misc.c +++ b/g_misc.c @@ -1,7 +1,7 @@ // g_misc.c #include "g_local.h" - +#include "cgf_sfx_glass.h" /*QUAKED func_group (0 0 0) ? Used to group brushes together just for editor convenience. @@ -797,11 +797,13 @@ void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator) void SP_func_explosive (edict_t *self) { +/* removed for glass fx if (deathmatch->value) { // auto-remove for deathmatch G_FreeEdict (self); return; } +*/ self->movetype = MOVETYPE_PUSH; @@ -837,6 +839,8 @@ void SP_func_explosive (edict_t *self) } gi.linkentity (self); + self->think = CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think; + self->nextthink = level.time + FRAMETIME; } diff --git a/g_phys.c b/g_phys.c index 2247809..d179226 100644 --- a/g_phys.c +++ b/g_phys.c @@ -1,6 +1,7 @@ // g_phys.c #include "g_local.h" +#include "cgf_sfx_glass.h" /* @@ -36,7 +37,9 @@ edict_t *SV_TestEntityPosition (edict_t *ent) mask = ent->clipmask; else mask = MASK_SOLID; - trace = do_trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); + PRETRACE(); + trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); + POSTTRACE(); if (trace.startsolid) return g_edicts; @@ -191,7 +194,9 @@ int SV_FlyMove (edict_t *ent, float time, int mask) for (i=0 ; i<3 ; i++) end[i] = ent->s.origin[i] + time_left * ent->velocity[i]; - trace = do_trace (ent->s.origin, ent->mins, ent->maxs, end, ent, mask); + PRETRACE(); + trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, mask); + POSTTRACE(); if (trace.allsolid) { // entity is trapped in another solid @@ -336,7 +341,9 @@ retry: else mask = MASK_SOLID; - trace = do_trace (start, ent->mins, ent->maxs, end, ent, mask); + PRETRACE(); + trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask); + POSTTRACE(); VectorCopy (trace.endpos, ent->s.origin); gi.linkentity (ent); @@ -824,7 +831,7 @@ void SV_Physics_Toss (edict_t *ent) { if (!ent->splatted) { - AddSplat (ent->owner, ent->s.origin, trace.plane.normal); + AddSplat (ent->owner, ent->s.origin, &trace); ent->splatted = true; } diff --git a/g_save.c b/g_save.c index 42a35f1..c2b19e7 100644 --- a/g_save.c +++ b/g_save.c @@ -1,4 +1,5 @@ #include "g_local.h" +#include "cgf_sfx_glass.h" field_t fields[] = { {"classname", FOFS(classname), F_LSTRING}, @@ -156,16 +157,16 @@ void InitGame (void) maxentities = gi.cvar ("maxentities", "1024", CVAR_LATCH); //FIREBLADE - if (!deathmatch->value) - { - gi.dprintf("Turning deathmatch on.\n"); - gi.cvar_set("deathmatch", "1"); - } - if (coop->value) - { - gi.dprintf("Turning coop off.\n"); - gi.cvar_set("coop", "0"); - } + if (!deathmatch->value) + { + gi.dprintf("Turning deathmatch on.\n"); + gi.cvar_set("deathmatch", "1"); + } + if (coop->value) + { + gi.dprintf("Turning coop off.\n"); + gi.cvar_set("coop", "0"); + } //FIREBLADE // change anytime vars @@ -175,6 +176,7 @@ void InitGame (void) password = gi.cvar ("password", "", CVAR_USERINFO); filterban = gi.cvar("filterban", "1", 0); //FIREBLADE + needpass = gi.cvar("needpass", "0", CVAR_SERVERINFO); radiolog = gi.cvar("radiolog", "0", 0); teamplay = gi.cvar ("teamplay", "0", CVAR_SERVERINFO|CVAR_LATCH); motd_time = gi.cvar("motd_time", "2", 0); @@ -185,11 +187,15 @@ void InitGame (void) gi.dprintf("No maps were read from the config file, \"actionmaps\" won't be used.\n"); gi.cvar_set("actionmaps", "0"); } - nohud = gi.cvar("nohud", "0", CVAR_LATCH); + nohud = gi.cvar("nohud", "0", CVAR_LATCH); roundlimit = gi.cvar("roundlimit", "0", CVAR_SERVERINFO); - skipmotd = gi.cvar("skipmotd", "0", 0); + limchasecam = gi.cvar("limchasecam", "0", CVAR_SERVERINFO|CVAR_LATCH); + skipmotd = gi.cvar("skipmotd", "0", 0); roundtimelimit = gi.cvar("roundtimelimit", "0", CVAR_SERVERINFO); - noscore = gi.cvar("noscore", "0", CVAR_SERVERINFO|CVAR_LATCH); + maxteamkills = gi.cvar("maxteamkills", "0", 0); + twbanrounds = gi.cvar("twbanrounds", "2", 0); + tkbanrounds = gi.cvar("tkbanrounds", "2", 0); + noscore = gi.cvar("noscore", "0", CVAR_SERVERINFO|CVAR_LATCH); actionversion = gi.cvar("actionversion", "none set", CVAR_SERVERINFO|CVAR_LATCH); gi.cvar_set("actionversion", ACTION_VERSION); //FIREBLADE @@ -203,16 +209,20 @@ void InitGame (void) unique_items = gi.cvar("items", "1", CVAR_SERVERINFO|CVAR_LATCH); // zucc changed ir to 1, enabled - ir = gi.cvar("ir", "1", CVAR_SERVERINFO); + ir = gi.cvar("ir", "1", CVAR_SERVERINFO); knifelimit = gi.cvar ("knifelimit", "40", 0); allweapon = gi.cvar ("allweapon", "0", CVAR_SERVERINFO); allitem = gi.cvar ("allitem", "0", CVAR_SERVERINFO); tgren = gi.cvar ("tgren", "0", CVAR_SERVERINFO); + // zucc from action sv_shelloff = gi.cvar ("shelloff", "1", 0); bholelimit = gi.cvar ("bholelimit", "0", 0); splatlimit = gi.cvar ("splatlimit", "0", 0); + // william for CGF (glass fx) + CGF_SFX_InstallGlassSupport(); + g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE); run_pitch = gi.cvar ("run_pitch", "0.002", 0); diff --git a/g_spawn.c b/g_spawn.c index cb56b93..eda239f 100644 --- a/g_spawn.c +++ b/g_spawn.c @@ -333,10 +333,10 @@ char *sp_item[ITEM_SWITCH_COUNT][2]= {"ammo_slugs", "ammo_sniper"}, {"ammo_shells", "ammo_m3"}, {"ammo_grenades", "weapon_Grenade"} - , - {"ammo_box", "ammo_m3"}, - {"weapon_cannon", "weapon_HC"}, - {"weapon_sniper", "weapon_Sniper"} + , + {"ammo_box", "ammo_m3"}, + {"weapon_cannon", "weapon_HC"}, + {"weapon_sniper", "weapon_Sniper"} }; @@ -1143,30 +1143,30 @@ void SP_worldspawn (edict_t *ent) gi.configstring (CS_MAXCLIENTS, va("%i", (int)(maxclients->value) ) ); //FIREBLADE - if (nohud->value) - { - gi.configstring(CS_STATUSBAR, ""); - } - else + if (nohud->value) + { + gi.configstring(CS_STATUSBAR, ""); + } + else //FIREBLADE - { - // status bar program - if (deathmatch->value) + { + // status bar program + if (deathmatch->value) //FIREBLADE - { - if (noscore->value && teamplay->value) - { - gi.configstring (CS_STATUSBAR, dm_noscore_statusbar); - } - else - { - gi.configstring (CS_STATUSBAR, dm_statusbar); - } - } + { + if (noscore->value && teamplay->value) + { + gi.configstring (CS_STATUSBAR, dm_noscore_statusbar); + } + else + { + gi.configstring (CS_STATUSBAR, dm_statusbar); + } + } //FIREBLADE - else - gi.configstring (CS_STATUSBAR, single_statusbar); - } + else + gi.configstring (CS_STATUSBAR, single_statusbar); + } //--------------- @@ -1338,5 +1338,11 @@ void SP_worldspawn (edict_t *ent) // 63 testing gi.configstring(CS_LIGHTS+63, "a"); + +//FB 6/2/99 + if (took_damage != NULL) + gi.TagFree(took_damage); + took_damage = (int *)gi.TagMalloc(sizeof(int) * game.maxclients, TAG_GAME); +//FB 6/2/99 } diff --git a/g_svcmds.c b/g_svcmds.c index 3996e77..8be6d49 100644 --- a/g_svcmds.c +++ b/g_svcmds.c @@ -2,8 +2,8 @@ void SVCmd_ReloadMOTD_f() { - ReadMOTDFile(); - gi.cprintf(NULL, PRINT_HIGH, "MOTD reloaded.\n"); + ReadMOTDFile(); + gi.cprintf(NULL, PRINT_HIGH, "MOTD reloaded.\n"); } /* @@ -41,6 +41,10 @@ typedef struct { unsigned mask; unsigned compare; + +//AZEROV + int temp_ban_games; +//AZEROV } ipfilter_t; #define MAX_IPFILTERS 1024 @@ -53,7 +57,7 @@ int numipfilters; StringToFilter ================= */ -static qboolean StringToFilter (char *s, ipfilter_t *f) +static qboolean StringToFilter (char *s, ipfilter_t *f, int temp_ban_games) { char num[128]; int i, j; @@ -92,6 +96,8 @@ static qboolean StringToFilter (char *s, ipfilter_t *f) f->mask = *(unsigned *)m; f->compare = *(unsigned *)b; + f->temp_ban_games = temp_ban_games; + return true; } @@ -157,7 +163,7 @@ void SVCmd_AddIP_f (void) numipfilters++; } - if (!StringToFilter (gi.argv(2), &ipfilters[i])) + if (!StringToFilter (gi.argv(2), &ipfilters[i], 0)) ipfilters[i].compare = 0xffffffff; } @@ -176,7 +182,7 @@ void SVCmd_RemoveIP_f (void) return; } - if (!StringToFilter (gi.argv(2), &f)) + if (!StringToFilter (gi.argv(2), &f, 0)) return; for (i=0 ; iclient) + { + return; + } + + // We used to kick on names, but people got crafty and figured + // out that putting in a space after their name let them get + // around the stupid 'kick' function. So now we kick by number. + for (i=0 ; iinuse) + continue; + if (entL->client && ent == entL) + { + sprintf (ban_string, "kick %d\n", i); + gi.AddCommandString (ban_string); + } + } +} + +/* +========================== +Ban a client for N rounds +========================== +*/ +qboolean Ban_TeamKiller ( edict_t *ent, int rounds ) +{ + int i = 0; + + if (!ent || !ent->client || !ent->client->ipaddr) + { + gi.cprintf (NULL, PRINT_HIGH, "Unable to determine client->ipaddr for edict\n"); + return false; + } + + for (i=0 ; iclient->ipaddr, &ipfilters[i], rounds)) + { + ipfilters[i].compare = 0xffffffff; + return false; + } + + return true; +} + +void UnBan_TeamKillers (void) +{ + // We don't directly unban them all - we subtract 1 from temp_ban_games, + // and unban them if it's 0. + + int i, j; + + for (i=0 ; i 0) + { + if (!--ipfilters[i].temp_ban_games) + { + // re-pack the filters + for (j=i+1 ; jmovedir, end); while(1) { - tr = do_trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); + PRETRACE(); + tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); + POSTTRACE(); if (!tr.ent) break; diff --git a/g_weapon.c b/g_weapon.c index ebeb6e6..c70053e 100644 --- a/g_weapon.c +++ b/g_weapon.c @@ -1,1466 +1,1629 @@ -#include "g_local.h" - - -void knife_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); -void Zylon_Grenade(edict_t *ent); - -//FIREBLADE -qboolean ap_already_hit[1000]; // 1000 = a number much larger than the possible max # of clients -//FIREBLADE - -/* -================= -check_dodge - -This is a support routine used when a client is firing -a non-instant attack weapon. It checks to see if a -monster's dodge function should be called. -================= -*/ -static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed) -{ - vec3_t end; - vec3_t v; - trace_t tr; - float eta; - - // easy mode only ducks one quarter the time - if (skill->value == 0) - { - if (random() > 0.25) - return; - } - VectorMA (start, 8192, dir, end); - tr = do_trace (start, NULL, NULL, end, self, MASK_SHOT); - if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self)) - { - VectorSubtract (tr.endpos, start, v); - eta = (VectorLength(v) - tr.ent->maxs[0]) / speed; - tr.ent->monsterinfo.dodge (tr.ent, self, eta); - } -} - - -/* zucc - bulletholes for testing spread patterns */ - -void BulletHoleThink (edict_t *self) -{ - G_FreeEdict( self ); -} - - -void SpawnHole( trace_t *tr, vec3_t dir ) -{ - - vec3_t origin; - - edict_t* lss; - lss = G_Spawn (); - lss->movetype = MOVETYPE_NOCLIP; - lss->solid = SOLID_TRIGGER; - lss->classname = "bhole"; - //lss->s.modelindex = gi.modelindex ("models/weapons/g_m4/tris.md2" ); - //lss->s.modelindex = gi.modelindex ("models/objects/holes/hole1/tris.md2" ); -// bhole model doesn't want to show up, so be it - lss->s.modelindex = gi.modelindex ("sprites/lsight.sp2"); - lss->s.renderfx = RF_GLOW; - lss->gravity = 0; - lss->think = BulletHoleThink; - lss->nextthink = level.time + 1000; - vectoangles(tr->plane.normal,lss->s.angles); - VectorNormalize(dir); - VectorMA(tr->endpos, 0,dir,origin); - VectorCopy(origin,lss->s.origin); - gi.linkentity (lss); - - - -} - - -/* -================= -fire_hit - -Used for all impact (hit/punch/slash) attacks -================= -*/ -qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) -{ - trace_t tr; - vec3_t forward, right, up; - vec3_t v; - vec3_t point; - float range; - vec3_t dir; - - //see if enemy is in range - VectorSubtract (self->enemy->s.origin, self->s.origin, dir); - range = VectorLength(dir); - if (range > aim[0]) - return false; - - if (aim[1] > self->mins[0] && aim[1] < self->maxs[0]) - { - // the hit is straight on so back the range up to the edge of their bbox - range -= self->enemy->maxs[0]; - } - else - { - // this is a side hit so adjust the "right" value out to the edge of their bbox - if (aim[1] < 0) - aim[1] = self->enemy->mins[0]; - else - aim[1] = self->enemy->maxs[0]; - } - - VectorMA (self->s.origin, range, dir, point); - - tr = do_trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT); - if (tr.fraction < 1) - { - if (!tr.ent->takedamage) - return false; - // if it will hit any client/monster then hit the one we wanted to hit - if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) - tr.ent = self->enemy; - } - - AngleVectors(self->s.angles, forward, right, up); - VectorMA (self->s.origin, range, forward, point); - VectorMA (point, aim[1], right, point); - VectorMA (point, aim[2], up, point); - VectorSubtract (point, self->enemy->s.origin, dir); - - // do the damage - T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); - - if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) - return false; - - // do our special form of knockback here - VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v); - VectorSubtract (v, point, v); - VectorNormalize (v); - VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity); - if (self->enemy->velocity[2] > 0) - self->enemy->groundentity = NULL; - return true; -} - - -/* -================= -fire_lead - -This is an internal support routine used for bullet/pellet based weapons. -================= -*/ -static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) -{ - trace_t tr; - vec3_t dir; - vec3_t forward, right, up; - vec3_t end; - float r; - float u; - vec3_t water_start; - qboolean water = false; - int content_mask = MASK_SHOT | MASK_WATER; - - tr = do_trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); - if (!(tr.fraction < 1.0)) - { - vectoangles (aimdir, dir); - AngleVectors (dir, forward, right, up); - - r = crandom()*hspread; - u = crandom()*vspread; - VectorMA (start, 8192, forward, end); - VectorMA (end, r, right, end); - VectorMA (end, u, up, end); - - if (gi.pointcontents (start) & MASK_WATER) - { - water = true; - VectorCopy (start, water_start); - content_mask &= ~MASK_WATER; - } - - tr = do_trace (start, NULL, NULL, end, self, content_mask); - - // see if we hit water - if (tr.contents & MASK_WATER) - { - int color; - - water = true; - VectorCopy (tr.endpos, water_start); - - if (!VectorCompare (start, tr.endpos)) - { - if (tr.contents & CONTENTS_WATER) - { - if (strcmp(tr.surface->name, "*brwater") == 0) - color = SPLASH_BROWN_WATER; - else - color = SPLASH_BLUE_WATER; - } - else if (tr.contents & CONTENTS_SLIME) - color = SPLASH_SLIME; - else if (tr.contents & CONTENTS_LAVA) - color = SPLASH_LAVA; - else - color = SPLASH_UNKNOWN; - - if (color != SPLASH_UNKNOWN) - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_SPLASH); - gi.WriteByte (8); - gi.WritePosition (tr.endpos); - gi.WriteDir (tr.plane.normal); - gi.WriteByte (color); - gi.multicast (tr.endpos, MULTICAST_PVS); - } - - // change bullet's course when it enters water - VectorSubtract (end, start, dir); - vectoangles (dir, dir); - AngleVectors (dir, forward, right, up); - r = crandom()*hspread*2; - u = crandom()*vspread*2; - VectorMA (water_start, 8192, forward, end); - VectorMA (end, r, right, end); - VectorMA (end, u, up, end); - } - - // re-trace ignoring water this time - tr = do_trace (water_start, NULL, NULL, end, self, MASK_SHOT); - } - } - - // send gun puff / flash - if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) - { - if (tr.fraction < 1.0) - { - if (tr.ent->takedamage) - { - T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); - } - else - { - if ( mod != MOD_M3 && mod != MOD_HC) - { - AddDecal (self, tr.endpos, tr.plane.normal); - } - //zucc remove spawnhole for real release -// SpawnHole( &tr, dir ); - if (strncmp (tr.surface->name, "sky", 3) != 0) - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (te_impact); - gi.WritePosition (tr.endpos); - gi.WriteDir (tr.plane.normal); - gi.multicast (tr.endpos, MULTICAST_PVS); - - if (self->client) - PlayerNoise(self, tr.endpos, PNOISE_IMPACT); - } - } - } - } - - // if went through water, determine where the end and make a bubble trail - if (water) - { - vec3_t pos; - - VectorSubtract (tr.endpos, water_start, dir); - VectorNormalize (dir); - VectorMA (tr.endpos, -2, dir, pos); - if (gi.pointcontents (pos) & MASK_WATER) - VectorCopy (pos, tr.endpos); - else - tr = do_trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); - - VectorAdd (water_start, tr.endpos, pos); - VectorScale (pos, 0.5, pos); - - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_BUBBLETRAIL); - gi.WritePosition (water_start); - gi.WritePosition (tr.endpos); - gi.multicast (pos, MULTICAST_PVS); - } -} - - -/* -================= -fire_bullet - -Fires a single round. Used for machinegun and chaingun. Would be fine for -pistols, rifles, etc.... -================= -*/ -void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) -{ - fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); -} - - - -// zucc fire_load_ap for rounds that pass through soft targets and keep going -static void fire_lead_ap (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) -{ - trace_t tr; - vec3_t dir; - vec3_t forward, right, up; - vec3_t end; - float r; - float u; - vec3_t water_start; - qboolean water = false; - int content_mask = MASK_SHOT | MASK_WATER; - vec3_t from; - edict_t *ignore; - -//FIREBLADE - memset(ap_already_hit, 0, - game.maxclients * sizeof(qboolean)); -//FIREBLADE - - // setup - stopAP = 0; - vectoangles (aimdir, dir); - AngleVectors (dir, forward, right, up); - - r = crandom()*hspread; - u = crandom()*vspread; - VectorMA (start, 8192, forward, end); - VectorMA (end, r, right, end); - VectorMA (end, u, up, end); - VectorCopy (start, from); - if (gi.pointcontents (start) & MASK_WATER) - { - water = true; - VectorCopy (start, water_start); - content_mask &= ~MASK_WATER; - } - - ignore = self; - - // tr = do_trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); - while (ignore) - //if (!(tr.fraction < 1.0)) - { - - //tr = do_trace (from, NULL, NULL, end, ignore, mask); - tr = do_trace (from, NULL, NULL, end, ignore, content_mask); - - // see if we hit water - if (tr.contents & MASK_WATER) - { - int color; - - water = true; - VectorCopy (tr.endpos, water_start); - - if (!VectorCompare (from, tr.endpos)) - { - if (tr.contents & CONTENTS_WATER) - { - if (strcmp(tr.surface->name, "*brwater") == 0) - color = SPLASH_BROWN_WATER; - else - color = SPLASH_BLUE_WATER; - } - else if (tr.contents & CONTENTS_SLIME) - color = SPLASH_SLIME; - else if (tr.contents & CONTENTS_LAVA) - color = SPLASH_LAVA; - else - color = SPLASH_UNKNOWN; - - if (color != SPLASH_UNKNOWN) - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_SPLASH); - gi.WriteByte (8); - gi.WritePosition (tr.endpos); - gi.WriteDir (tr.plane.normal); - gi.WriteByte (color); - gi.multicast (tr.endpos, MULTICAST_PVS); - } - - // change bullet's course when it enters water - VectorSubtract (end, from, dir); - vectoangles (dir, dir); - AngleVectors (dir, forward, right, up); - r = crandom()*hspread*2; - u = crandom()*vspread*2; - VectorMA (water_start, 8192, forward, end); - VectorMA (end, r, right, end); - VectorMA (end, u, up, end); - } - - // re-trace ignoring water this time - tr = do_trace (water_start, NULL, NULL, end, ignore, MASK_SHOT); - - } - - // send gun puff / flash - - ignore = NULL; - - if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) - { - if (tr.fraction < 1.0) - { - - if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) - { - ignore = tr.ent; - VectorCopy (tr.endpos, from); -//FIREBLADE - // Advance the "from" point a few units - // towards "end" here - if (tr.ent->client) - { - vec3_t tempv; - vec3_t out; - if (ap_already_hit[tr.ent - g_edicts - 1]) - { - VectorSubtract(end, from, tempv); - VectorNormalize(tempv); - VectorScale(tempv, 8, out); - VectorAdd(out, from, from); - continue; - } - ap_already_hit[tr.ent - g_edicts - 1] = true; - } -//FIREBLADE - } - - if ((tr.ent != self) && (tr.ent->takedamage)) - { - T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, mod); - if ( stopAP ) // the AP round hit something that would stop it (kevlar) - ignore = NULL; - } - else if ( tr.ent != self && !water ) - { - //zucc remove spawnhole for real release - // SpawnHole( &tr, dir ); - - if (strncmp (tr.surface->name, "sky", 3) != 0) - { - AddDecal (self, tr.endpos, tr.plane.normal); - gi.WriteByte (svc_temp_entity); - gi.WriteByte (te_impact); - gi.WritePosition (tr.endpos); - gi.WriteDir (tr.plane.normal); - gi.multicast (tr.endpos, MULTICAST_PVS); - - if (self->client) - PlayerNoise(self, tr.endpos, PNOISE_IMPACT); - } - } - } - } - } - // if went through water, determine where the end and make a bubble trail - if (water) - { - vec3_t pos; - - VectorSubtract (tr.endpos, water_start, dir); - VectorNormalize (dir); - VectorMA (tr.endpos, -2, dir, pos); - if (gi.pointcontents (pos) & MASK_WATER) - VectorCopy (pos, tr.endpos); - else - tr = do_trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); - - VectorAdd (water_start, tr.endpos, pos); - VectorScale (pos, 0.5, pos); - - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_BUBBLETRAIL); - gi.WritePosition (water_start); - gi.WritePosition (tr.endpos); - gi.multicast (pos, MULTICAST_PVS); - } - -} - - - -// zucc - for the M4 -void fire_bullet_sparks (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) -{ - fire_lead_ap (self, start, aimdir, damage, kick, TE_BULLET_SPARKS, hspread, vspread, mod); -} - -// zucc - for sniper -void fire_bullet_sniper (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) -{ - fire_lead_ap (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); -} - - - - - - - -/* -================= -fire_shotgun - -Shoots shotgun pellets. Used by shotgun and super shotgun. -================= -*/ -void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) -{ - int i; - - for (i = 0; i < count; i++) - fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod); -} - - -/* -================= -fire_blaster - -Fires a single blaster bolt. Used by the blaster and hyper blaster. -================= -*/ -void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - int mod; - - if (other == self->owner) - return; - - if (surf && (surf->flags & SURF_SKY)) - { - G_FreeEdict (self); - return; - } - - if (self->owner->client) - PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); - - if (other->takedamage) - { - if (self->spawnflags & 1) - mod = MOD_HYPERBLASTER; - else - mod = MOD_BLASTER; - T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); - } - else - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_BLASTER); - gi.WritePosition (self->s.origin); - if (!plane) - gi.WriteDir (vec3_origin); - else - gi.WriteDir (plane->normal); - gi.multicast (self->s.origin, MULTICAST_PVS); - } - - G_FreeEdict (self); -} - -void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) -{ - edict_t *bolt; - trace_t tr; - - VectorNormalize (dir); - - bolt = G_Spawn(); -// 3.20 ANTI-LAG FIX, just in case we end up using this code for something... -FB - bolt->svflags = SVF_DEADMONSTER; -// ^^^ - VectorCopy (start, bolt->s.origin); - VectorCopy (start, bolt->s.old_origin); - vectoangles (dir, bolt->s.angles); - VectorScale (dir, speed, bolt->velocity); - bolt->movetype = MOVETYPE_FLYMISSILE; - bolt->clipmask = MASK_SHOT; - bolt->solid = SOLID_BBOX; - bolt->s.effects |= effect; - VectorClear (bolt->mins); - VectorClear (bolt->maxs); - bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); - bolt->s.sound = gi.soundindex ("misc/lasfly.wav"); - bolt->owner = self; - bolt->touch = blaster_touch; - bolt->nextthink = level.time + 2; - bolt->think = G_FreeEdict; - bolt->dmg = damage; - bolt->classname = "bolt"; - if (hyper) - bolt->spawnflags = 1; - gi.linkentity (bolt); - - if (self->client) - check_dodge (self, bolt->s.origin, dir, speed); - - tr = do_trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); - if (tr.fraction < 1.0) - { - VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); - bolt->touch (bolt, tr.ent, NULL, NULL); - } -} - -qboolean G_EntExists(edict_t *ent) -{ - return ((ent) && (ent->client));//(ent->inuse)); -} - - - -/* -================= -fire_grenade -================= -*/ -static void Grenade_Explode (edict_t *ent) -{ - vec3_t origin; - int mod; - - - if (ent->owner->client) - PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); - - //FIXME: if we are onground then raise our Z just a bit since we are a point? - if (ent->enemy) - { - float points; - vec3_t v; - vec3_t dir; - - VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); - VectorMA (ent->enemy->s.origin, 0.5, v, v); - VectorSubtract (ent->s.origin, v, v); - points = ent->dmg - 0.5 * VectorLength (v); - VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); - if (ent->spawnflags & 1) - mod = MOD_HANDGRENADE; - else - mod = MOD_GRENADE; - T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); - } - - if (ent->spawnflags & 2) - mod = MOD_HELD_GRENADE; - else if (ent->spawnflags & 1) - mod = MOD_HG_SPLASH; - else - mod = MOD_G_SPLASH; - T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); - - VectorMA (ent->s.origin, -0.02, ent->velocity, origin); - gi.WriteByte (svc_temp_entity); - if (ent->waterlevel) - { - if (ent->groundentity) - gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); - else - gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); - } - else - { - if (ent->groundentity) - gi.WriteByte (TE_GRENADE_EXPLOSION); - else - gi.WriteByte (TE_ROCKET_EXPLOSION); - } - gi.WritePosition (origin); - gi.multicast (ent->s.origin, MULTICAST_PHS); - - G_FreeEdict (ent); -} - -static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (other == ent->owner) - return; - - if (surf && (surf->flags & SURF_SKY)) - { - G_FreeEdict (ent); - return; - } - - if (!other->takedamage) - { -/*FIREBLADE - if (ent->spawnflags & 1) - { - if (random() > 0.5) - gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); - else - gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); - } - else -FIREBLADE*/ - { - gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); - } - return; - } - - // zucc not needed since grenades don't blow up on contact - //ent->enemy = other; - //Grenade_Explode (ent); -} - -void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) -{ - edict_t *grenade; - vec3_t dir; - vec3_t forward, right, up; - - vectoangles (aimdir, dir); - AngleVectors (dir, forward, right, up); - - grenade = G_Spawn(); - VectorCopy (start, grenade->s.origin); - VectorScale (aimdir, speed, grenade->velocity); - VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); - VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); - VectorSet (grenade->avelocity, 300, 300, 300); - grenade->movetype = MOVETYPE_BOUNCE; - grenade->clipmask = MASK_SHOT; - grenade->solid = SOLID_BBOX; - grenade->s.effects |= EF_GRENADE; - VectorClear (grenade->mins); - VectorClear (grenade->maxs); - grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2"); - grenade->owner = self; - grenade->touch = Grenade_Touch; - grenade->nextthink = level.time + timer; - grenade->think = Grenade_Explode; - grenade->dmg = damage; - grenade->dmg_radius = damage_radius; - grenade->classname = "grenade"; - - gi.linkentity (grenade); -} - -void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) -{ - edict_t *grenade; - vec3_t dir; - vec3_t forward, right, up; - - vectoangles (aimdir, dir); - AngleVectors (dir, forward, right, up); - - grenade = G_Spawn(); - VectorCopy (start, grenade->s.origin); - VectorScale (aimdir, speed, grenade->velocity); - VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); - VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); - VectorSet (grenade->avelocity, 300, 300, 300); - grenade->movetype = MOVETYPE_BOUNCE; - grenade->clipmask = MASK_SHOT; - grenade->solid = SOLID_BBOX; - //grenade->s.effects |= EF_GRENADE; - VectorClear (grenade->mins); - VectorClear (grenade->maxs); - grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); - grenade->owner = self; - grenade->touch = Grenade_Touch; - grenade->nextthink = level.time + timer; - grenade->think = Grenade_Explode; - grenade->dmg = damage; - grenade->dmg_radius = damage_radius; - grenade->classname = "hgrenade"; - if (held) - grenade->spawnflags = 3; - else - grenade->spawnflags = 1; - //grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); - - if (timer <= 0.0) - Grenade_Explode (grenade); - else - { - gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0); - gi.linkentity (grenade); - } -} - - -/* -================= -fire_rocket -================= -*/ -void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - vec3_t origin; - int n; - - if (other == ent->owner) - return; - - if (surf && (surf->flags & SURF_SKY)) - { - G_FreeEdict (ent); - return; - } - - if (ent->owner->client) - PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); - - // calculate position for the explosion entity - VectorMA (ent->s.origin, -0.02, ent->velocity, origin); - - if (other->takedamage) - { - T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); - } - else - { - // don't throw any debris in net games - if (!deathmatch->value && !coop->value) - { - if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING))) - { - n = rand() % 5; - while(n--) - ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); - } - } - } - - T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); - - gi.WriteByte (svc_temp_entity); - if (ent->waterlevel) - gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); - else - gi.WriteByte (TE_ROCKET_EXPLOSION); - gi.WritePosition (origin); - gi.multicast (ent->s.origin, MULTICAST_PHS); - - G_FreeEdict (ent); -} - -void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) -{ - edict_t *rocket; - - rocket = G_Spawn(); - VectorCopy (start, rocket->s.origin); - VectorCopy (dir, rocket->movedir); - vectoangles (dir, rocket->s.angles); - VectorScale (dir, speed, rocket->velocity); - rocket->movetype = MOVETYPE_FLYMISSILE; - rocket->clipmask = MASK_SHOT; - rocket->solid = SOLID_BBOX; - rocket->s.effects |= EF_ROCKET; - VectorClear (rocket->mins); - VectorClear (rocket->maxs); - rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); - rocket->owner = self; - rocket->touch = rocket_touch; - rocket->nextthink = level.time + 8000/speed; - rocket->think = G_FreeEdict; - rocket->dmg = damage; - rocket->radius_dmg = radius_damage; - rocket->dmg_radius = damage_radius; - rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); - rocket->classname = "rocket"; - - if (self->client) - check_dodge (self, rocket->s.origin, dir, speed); - - gi.linkentity (rocket); -} - - -/* -================= -fire_rail -================= -*/ -void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) -{ - vec3_t from; - vec3_t end; - trace_t tr; - edict_t *ignore; - int mask; - qboolean water; - - VectorMA (start, 8192, aimdir, end); - VectorCopy (start, from); - ignore = self; - water = false; - mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; - while (ignore) - { - tr = do_trace (from, NULL, NULL, end, ignore, mask); - - if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA)) - { - mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA); - water = true; - } - else - { -// FROM 3.20, just in case we ever end up using this code for something... -FB - //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc) - if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) || - (tr.ent->solid == SOLID_BBOX)) -// ^^^ - ignore = tr.ent; - else - ignore = NULL; - - if ((tr.ent != self) && (tr.ent->takedamage)) - T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); - } - - VectorCopy (tr.endpos, from); - } - - // send gun puff / flash - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_RAILTRAIL); - gi.WritePosition (start); - gi.WritePosition (tr.endpos); - gi.multicast (self->s.origin, MULTICAST_PHS); -// gi.multicast (start, MULTICAST_PHS); - if (water) - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_RAILTRAIL); - gi.WritePosition (start); - gi.WritePosition (tr.endpos); - gi.multicast (tr.endpos, MULTICAST_PHS); - } - - if (self->client) - PlayerNoise(self, tr.endpos, PNOISE_IMPACT); -} - - -/* -================= -fire_bfg -================= -*/ -void bfg_explode (edict_t *self) -{ - edict_t *ent; - float points; - vec3_t v; - float dist; - - if (self->s.frame == 0) - { - // the BFG effect - ent = NULL; - while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) - { - if (!ent->takedamage) - continue; - if (ent == self->owner) - continue; - if (!CanDamage (ent, self)) - continue; - if (!CanDamage (ent, self->owner)) - continue; - - VectorAdd (ent->mins, ent->maxs, v); - VectorMA (ent->s.origin, 0.5, v, v); - VectorSubtract (self->s.origin, v, v); - dist = VectorLength(v); - points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); - if (ent == self->owner) - points = points * 0.5; - - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_BFG_EXPLOSION); - gi.WritePosition (ent->s.origin); - gi.multicast (ent->s.origin, MULTICAST_PHS); - T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); - } - } - - self->nextthink = level.time + FRAMETIME; - self->s.frame++; - if (self->s.frame == 5) - self->think = G_FreeEdict; -} - -void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (other == self->owner) - return; - - if (surf && (surf->flags & SURF_SKY)) - { - G_FreeEdict (self); - return; - } - - if (self->owner->client) - PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); - - // core explosion - prevents firing it into the wall/floor - if (other->takedamage) - T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); - T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); - - gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); - self->solid = SOLID_NOT; - self->touch = NULL; - VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); - VectorClear (self->velocity); - self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2"); - self->s.frame = 0; - self->s.sound = 0; - self->s.effects &= ~EF_ANIM_ALLFAST; - self->think = bfg_explode; - self->nextthink = level.time + FRAMETIME; - self->enemy = other; - - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_BFG_BIGEXPLOSION); - gi.WritePosition (self->s.origin); - gi.multicast (self->s.origin, MULTICAST_PVS); -} - - -void bfg_think (edict_t *self) -{ - edict_t *ent; - edict_t *ignore; - vec3_t point; - vec3_t dir; - vec3_t start; - vec3_t end; - int dmg; - trace_t tr; - - if (deathmatch->value) - dmg = 5; - else - dmg = 10; - - ent = NULL; - while ((ent = findradius(ent, self->s.origin, 256)) != NULL) - { - if (ent == self) - continue; - - if (ent == self->owner) - continue; - - if (!ent->takedamage) - continue; - - if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) - continue; - - VectorMA (ent->absmin, 0.5, ent->size, point); - - VectorSubtract (point, self->s.origin, dir); - VectorNormalize (dir); - - ignore = self; - VectorCopy (self->s.origin, start); - VectorMA (start, 2048, dir, end); - while(1) - { - tr = do_trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); - - if (!tr.ent) - break; - - // hurt it if we can - if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) - T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); - - // if we hit something that's not a monster or player we're done - if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_LASER_SPARKS); - gi.WriteByte (4); - gi.WritePosition (tr.endpos); - gi.WriteDir (tr.plane.normal); - gi.WriteByte (self->s.skinnum); - gi.multicast (tr.endpos, MULTICAST_PVS); - break; - } - - ignore = tr.ent; - VectorCopy (tr.endpos, start); - } - - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_BFG_LASER); - gi.WritePosition (self->s.origin); - gi.WritePosition (tr.endpos); - gi.multicast (self->s.origin, MULTICAST_PHS); - } - - self->nextthink = level.time + FRAMETIME; -} - - -void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) -{ - edict_t *bfg; - - bfg = G_Spawn(); - VectorCopy (start, bfg->s.origin); - VectorCopy (dir, bfg->movedir); - vectoangles (dir, bfg->s.angles); - VectorScale (dir, speed, bfg->velocity); - bfg->movetype = MOVETYPE_FLYMISSILE; - bfg->clipmask = MASK_SHOT; - bfg->solid = SOLID_BBOX; - bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; - VectorClear (bfg->mins); - VectorClear (bfg->maxs); - bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); - bfg->owner = self; - bfg->touch = bfg_touch; - bfg->nextthink = level.time + 8000/speed; - bfg->think = G_FreeEdict; - bfg->radius_dmg = damage; - bfg->dmg_radius = damage_radius; - bfg->classname = "bfg blast"; - bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); - - bfg->think = bfg_think; - bfg->nextthink = level.time + FRAMETIME; - bfg->teammaster = bfg; - bfg->teamchain = NULL; - - if (self->client) - check_dodge (self, bfg->s.origin, dir, speed); - - gi.linkentity (bfg); -} - - -void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result); - -qboolean IsFemale (edict_t *ent); - -void kick_attack (edict_t * ent ) -{ - vec3_t start; - vec3_t forward, right; - vec3_t offset; - int damage = 20; - int kick = 400; - trace_t tr; - vec3_t end; - - - - AngleVectors (ent->client->v_angle, forward, right, NULL); - - VectorScale (forward, 0, ent->client->kick_origin); - - VectorSet(offset, 0, 0, ent->viewheight-20); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - - VectorMA( start, 25, forward, end ); - - tr = do_trace (ent->s.origin, NULL, NULL, end, ent, MASK_SHOT); - - // don't need to check for water - if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) - { - if (tr.fraction < 1.0) - { - if (tr.ent->takedamage) - { - if (teamplay->value) - { - if (lights_camera_action) - return; - - if (tr.ent != ent && tr.ent->client && ent->client && - tr.ent->client->resp.team == ent->client->resp.team) - return; - } - - if ( tr.ent->health <= 0 ) - return; - - if (((tr.ent != ent) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && OnSameTeam (tr.ent, ent))) - return; - // zucc stop powerful upwards kicking - forward[2] = 0; - - - T_Damage (tr.ent, ent, ent, forward, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KICK ); - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/kick.wav"), 1, ATTN_NORM, 0); - PlayerNoise(ent, ent->s.origin, PNOISE_SELF); - ent->client->jumping = 0; // only 1 jumpkick per jump - if (tr.ent->client && ( tr.ent->client->curr_weap == M4_NUM - || tr.ent->client->curr_weap == MP5_NUM - || tr.ent->client->curr_weap == M3_NUM - || tr.ent->client->curr_weap == SNIPER_NUM - || tr.ent->client->curr_weap == HC_NUM ) - && 1 ) // crandom() > .8 ) - { - - DropSpecialWeapon(tr.ent); - if (IsFemale(tr.ent)) - { - gi.cprintf(ent, PRINT_HIGH, "You kick %s's %s from her hands!\n", tr.ent->client->pers.netname, (tr.ent->client->pers.weapon)->pickup_name ); - } - else - { - gi.cprintf(ent, PRINT_HIGH, "You kick %s's %s from his hands!\n", tr.ent->client->pers.netname, (tr.ent->client->pers.weapon)->pickup_name ); - } - gi.cprintf(tr.ent, PRINT_HIGH, "%s kicked your weapon from your hands!\n", ent->client->pers.netname ); - } - - } - - } - } - -} - -// zucc -// return values -// 0 - missed -// 1 - hit player -// 2 - hit wall - - -int knife_attack ( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) -{ - trace_t tr; - vec3_t end; - - - VectorMA (start, 45, aimdir, end); - - tr = do_trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT); - - // don't need to check for water - if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) - { - if (tr.fraction < 1.0) - { - if (tr.ent->takedamage) - { - T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KNIFE ); - return -2; - } - else - { - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_SPARKS); - gi.WritePosition (tr.endpos); - gi.WriteDir (tr.plane.normal); - gi.multicast (tr.endpos, MULTICAST_PVS); - return -1; - } - - } - else - return 0; - } - return 0; // we hit the sky, call it a miss -} - -static int knives = 0; - -void knife_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - vec3_t origin; - // int n; - - edict_t *dropped; - edict_t *knife; - // vec3_t forward, right, up; - vec3_t move_angles; - gitem_t *item; - - - if (other == ent->owner) - return; - - if (surf && (surf->flags & SURF_SKY)) - { - G_FreeEdict (ent); - return; - } - - if (ent->owner->client) - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/clank.wav"), 1, ATTN_NORM, 0); - PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); - } - - // calculate position for the explosion entity - VectorMA (ent->s.origin, -0.02, ent->velocity, origin); - - if (other->takedamage) - { - T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_KNIFE_THROWN); - } - else - { - - // code to manage excess knives in the game, guarantees that - // no more than knifelimit knives will be stuck in walls. - // if knifelimit == 0 then it won't be in effect and it can - // start removing knives even when less than the limit are - // out there. - if ( knifelimit->value != 0 ) - { - knives++; - - if (knives > knifelimit->value) - knives = 1; - - knife = FindEdictByClassnum ("weapon_Knife", knives); - - if (knife) - { - knife->nextthink = level.time + .1; - } - - } - - dropped = G_Spawn(); - item = FindItem(KNIFE_NAME); - - dropped->classname = item->classname; - dropped->item = item; - dropped->spawnflags = DROPPED_ITEM; - dropped->s.effects = item->world_model_flags; - dropped->s.renderfx = RF_GLOW; - VectorSet (dropped->mins, -15, -15, -15); - VectorSet (dropped->maxs, 15, 15, 15); - gi.setmodel (dropped, dropped->item->world_model); - dropped->solid = SOLID_TRIGGER; - dropped->movetype = MOVETYPE_TOSS; - dropped->touch = Touch_Item; - dropped->owner = ent; - dropped->gravity = 0; - dropped->classnum = knives; - - vectoangles (ent->velocity, move_angles); - //AngleVectors (ent->s.angles, forward, right, up); - VectorCopy (ent->s.origin, dropped->s.origin); - VectorCopy (move_angles, dropped->s.angles); - //VectorScale (forward, 100, dropped->velocity); - //dropped->velocity[2] = 300; - - //dropped->think = drop_make_touchable; - //dropped->nextthink = level.time + 1; - - dropped->nextthink = level.time + 120; - dropped->think = G_FreeEdict; - - gi.linkentity (dropped); - - - if ( !(ent->waterlevel) ) - { - /*gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_WELDING_SPARKS); - gi.WritePosition (origin); - gi.multicast (ent->s.origin, MULTICAST_PHS); - */ - gi.WriteByte (svc_temp_entity); - gi.WriteByte (TE_SPARKS); - gi.WritePosition (origin); - gi.WriteDir (plane->normal); - gi.multicast (ent->s.origin, MULTICAST_PVS); - } - - } - G_FreeEdict (ent); -} - - - - -void knife_throw (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed ) -{ - edict_t *knife; -// vec3_t forward, right, up; - trace_t tr; - - knife = G_Spawn(); - - VectorNormalize (dir); - VectorCopy (start, knife->s.origin); - VectorCopy (start, knife->s.old_origin); - vectoangles (dir, knife->s.angles); - VectorScale (dir, speed, knife->velocity); - knife->movetype = MOVETYPE_TOSS; - -/* - VectorCopy (start, knife->s.origin); - VectorCopy (dir, knife->movedir); - vectoangles (dir, knife->s.angles); - VectorScale (dir, speed, knife->velocity); -*/ - - // gi.cprintf(self, PRINT_HIGH, "speed %d\n", speed); - // below add upwards trajectory, not in action though - // AngleVectors (knife->s.angles, forward, right, up); - // VectorMA (knife->velocity, 210, up, knife->velocity); - VectorSet (knife->avelocity, 1200, 0, 0); - - knife->movetype = MOVETYPE_TOSS; - knife->clipmask = MASK_SHOT; - knife->solid = SOLID_BBOX; - knife->s.effects = 0; //EF_ROTATE? - VectorClear (knife->mins); - VectorClear (knife->maxs); - knife->s.modelindex = gi.modelindex ("models/objects/knife/tris.md2"); - knife->owner = self; - knife->touch = knife_touch; - knife->nextthink = level.time + 8000/speed; - knife->think = G_FreeEdict; - knife->dmg = damage; - knife->s.sound = gi.soundindex ("misc/flyloop.wav"); - knife->classname = "thrown_knife"; - -// used by dodging monsters, skip -// if (self->client) -// check_dodge (self, rocket->s.origin, dir, speed); - - tr = gi.trace (self->s.origin, NULL, NULL, knife->s.origin, knife, MASK_SHOT); - if (tr.fraction < 1.0) - { - VectorMA (knife->s.origin, -10, dir, knife->s.origin); - knife->touch (knife, tr.ent, NULL, NULL); - } - - - gi.linkentity (knife); -} - +#include "g_local.h" +#include "cgf_sfx_glass.h" +#include "a_game.h" //zucc for KickDoor + + +void knife_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); +void Zylon_Grenade(edict_t *ent); +void setFFState(edict_t *ent); + +//FIREBLADE +qboolean ap_already_hit[1000]; // 1000 = a number much larger than the possible max # of clients +int *took_damage; //FB 6/2/99, to keep track of shotgun damage +//FIREBLADE + +/* +================= +check_dodge + +This is a support routine used when a client is firing +a non-instant attack weapon. It checks to see if a +monster's dodge function should be called. +================= +*/ +static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed) +{ + vec3_t end; + vec3_t v; + trace_t tr; + float eta; + + // easy mode only ducks one quarter the time + if (skill->value == 0) + { + if (random() > 0.25) + return; + } + VectorMA (start, 8192, dir, end); + PRETRACE(); + tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); + POSTTRACE(); + if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self)) + { + VectorSubtract (tr.endpos, start, v); + eta = (VectorLength(v) - tr.ent->maxs[0]) / speed; + tr.ent->monsterinfo.dodge (tr.ent, self, eta); + } +} + + +/* zucc - bulletholes for testing spread patterns */ + +void BulletHoleThink (edict_t *self) +{ + G_FreeEdict( self ); +} + + +void SpawnHole( trace_t *tr, vec3_t dir ) +{ + + vec3_t origin; + + edict_t* lss; + lss = G_Spawn (); + lss->movetype = MOVETYPE_NOCLIP; + lss->solid = SOLID_TRIGGER; + lss->classname = "bhole"; + //lss->s.modelindex = gi.modelindex ("models/weapons/g_m4/tris.md2" ); + //lss->s.modelindex = gi.modelindex ("models/objects/holes/hole1/tris.md2" ); +// bhole model doesn't want to show up, so be it + lss->s.modelindex = gi.modelindex ("sprites/lsight.sp2"); + lss->s.renderfx = RF_GLOW; + lss->gravity = 0; + lss->think = BulletHoleThink; + lss->nextthink = level.time + 1000; + vectoangles(tr->plane.normal,lss->s.angles); + VectorNormalize(dir); + VectorMA(tr->endpos, 0,dir,origin); + VectorCopy(origin,lss->s.origin); + gi.linkentity (lss); +} + + +/* +================= +fire_hit + +Used for all impact (hit/punch/slash) attacks +================= +*/ +qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) +{ + trace_t tr; + vec3_t forward, right, up; + vec3_t v; + vec3_t point; + float range; + vec3_t dir; + + //see if enemy is in range + VectorSubtract (self->enemy->s.origin, self->s.origin, dir); + range = VectorLength(dir); + if (range > aim[0]) + return false; + + if (aim[1] > self->mins[0] && aim[1] < self->maxs[0]) + { + // the hit is straight on so back the range up to the edge of their bbox + range -= self->enemy->maxs[0]; + } + else + { + // this is a side hit so adjust the "right" value out to the edge of their bbox + if (aim[1] < 0) + aim[1] = self->enemy->mins[0]; + else + aim[1] = self->enemy->maxs[0]; + } + + VectorMA (self->s.origin, range, dir, point); + + PRETRACE(); + tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT); + POSTTRACE(); + if (tr.fraction < 1) + { + if (!tr.ent->takedamage) + return false; + // if it will hit any client/monster then hit the one we wanted to hit + if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) + tr.ent = self->enemy; + } + + AngleVectors(self->s.angles, forward, right, up); + VectorMA (self->s.origin, range, forward, point); + VectorMA (point, aim[1], right, point); + VectorMA (point, aim[2], up, point); + VectorSubtract (point, self->enemy->s.origin, dir); + + setFFState(self); + // do the damage + T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); + + if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) + return false; + + // do our special form of knockback here + VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v); + VectorSubtract (v, point, v); + VectorNormalize (v); + VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity); + if (self->enemy->velocity[2] > 0) + self->enemy->groundentity = NULL; + return true; +} + + +/* +================= +fire_lead + +This is an internal support routine used for bullet/pellet based weapons. +================= +*/ +static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) +{ + trace_t tr; + vec3_t dir; + vec3_t forward, right, up; + vec3_t end; + float r; + float u; + vec3_t water_start; + qboolean water = false; + int content_mask = MASK_SHOT | MASK_WATER; + + PRETRACE(); + tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); + POSTTRACE(); + if (!(tr.fraction < 1.0)) + { + vectoangles (aimdir, dir); + AngleVectors (dir, forward, right, up); + + r = crandom()*hspread; + u = crandom()*vspread; + VectorMA (start, 8192, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + + if (gi.pointcontents (start) & MASK_WATER) + { + water = true; + VectorCopy (start, water_start); + content_mask &= ~MASK_WATER; + } + + PRETRACE(); + tr = gi.trace (start, NULL, NULL, end, self, content_mask); + POSTTRACE(); + +// glass fx + // catch case of firing thru one or breakable glasses + while ( (tr.fraction < 1.0) + && (tr.surface->flags & (SURF_TRANS33 | SURF_TRANS66)) + && (tr.ent) + && (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + ) + { + // break glass + CGF_SFX_ShootBreakableGlass(tr.ent, self, &tr, mod); + // continue trace from current endpos to start + PRETRACE(); + tr = gi.trace (tr.endpos, NULL, NULL, end, tr.ent, content_mask); + POSTTRACE(); + } +// --- + + // see if we hit water + if (tr.contents & MASK_WATER) + { + int color; + + water = true; + VectorCopy (tr.endpos, water_start); + + if (!VectorCompare (start, tr.endpos)) + { + if (tr.contents & CONTENTS_WATER) + { + if (strcmp(tr.surface->name, "*brwater") == 0) + color = SPLASH_BROWN_WATER; + else + color = SPLASH_BLUE_WATER; + } + else if (tr.contents & CONTENTS_SLIME) + color = SPLASH_SLIME; + else if (tr.contents & CONTENTS_LAVA) + color = SPLASH_LAVA; + else + color = SPLASH_UNKNOWN; + + if (color != SPLASH_UNKNOWN) + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_SPLASH); + gi.WriteByte (8); + gi.WritePosition (tr.endpos); + gi.WriteDir (tr.plane.normal); + gi.WriteByte (color); + gi.multicast (tr.endpos, MULTICAST_PVS); + } + + // change bullet's course when it enters water + VectorSubtract (end, start, dir); + vectoangles (dir, dir); + AngleVectors (dir, forward, right, up); + r = crandom()*hspread*2; + u = crandom()*vspread*2; + VectorMA (water_start, 8192, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + } + + // re-trace ignoring water this time + PRETRACE(); + tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); + POSTTRACE(); + } + } + + // send gun puff / flash + if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) + { + if (tr.fraction < 1.0) + { + if (tr.ent->takedamage) + { + T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); + } + else + { + if ( mod != MOD_M3 && mod != MOD_HC) + { + AddDecal (self, &tr); + } + //zucc remove spawnhole for real release +// SpawnHole( &tr, dir ); + if (strncmp (tr.surface->name, "sky", 3) != 0) + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (te_impact); + gi.WritePosition (tr.endpos); + gi.WriteDir (tr.plane.normal); + gi.multicast (tr.endpos, MULTICAST_PVS); + + if (self->client) + PlayerNoise(self, tr.endpos, PNOISE_IMPACT); + } + } + } + } + + // if went through water, determine where the end and make a bubble trail + if (water) + { + vec3_t pos; + + VectorSubtract (tr.endpos, water_start, dir); + VectorNormalize (dir); + VectorMA (tr.endpos, -2, dir, pos); + if (gi.pointcontents (pos) & MASK_WATER) + VectorCopy (pos, tr.endpos); + else + { + PRETRACE(); + tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); + POSTTRACE(); + } + + VectorAdd (water_start, tr.endpos, pos); + VectorScale (pos, 0.5, pos); + + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BUBBLETRAIL); + gi.WritePosition (water_start); + gi.WritePosition (tr.endpos); + gi.multicast (pos, MULTICAST_PVS); + } +} + + +/* +================= +fire_bullet + +Fires a single round. Used for machinegun and chaingun. Would be fine for +pistols, rifles, etc.... +================= +*/ +void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) +{ + setFFState(self); + fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); +} + + + +// zucc fire_load_ap for rounds that pass through soft targets and keep going +static void fire_lead_ap (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) +{ + trace_t tr; + vec3_t dir; + vec3_t forward, right, up; + vec3_t end; + float r; + float u; + vec3_t water_start; + qboolean water = false; + int content_mask = MASK_SHOT | MASK_WATER; + vec3_t from; + edict_t *ignore; + +//FIREBLADE + memset(ap_already_hit, 0, + game.maxclients * sizeof(qboolean)); +//FIREBLADE + + // setup + stopAP = 0; + vectoangles (aimdir, dir); + AngleVectors (dir, forward, right, up); + + r = crandom()*hspread; + u = crandom()*vspread; + VectorMA (start, 8192, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + VectorCopy (start, from); + if (gi.pointcontents (start) & MASK_WATER) + { + water = true; + VectorCopy (start, water_start); + content_mask &= ~MASK_WATER; + } + + ignore = self; + + // PRETRACE(); + // tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); + // POSTTRACE(); + while (ignore) + //if (!(tr.fraction < 1.0)) + { + PRETRACE(); + //tr = gi.trace (from, NULL, NULL, end, ignore, mask); + tr = gi.trace (from, NULL, NULL, end, ignore, content_mask); + POSTTRACE(); + +// glass fx + // catch case of firing thru one or breakable glasses + while ( (tr.fraction < 1.0) + && (tr.surface->flags & (SURF_TRANS33 | SURF_TRANS66)) + && (tr.ent) + && (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + ) + { + // break glass + CGF_SFX_ShootBreakableGlass(tr.ent, self, &tr, mod); + // continue trace from current endpos to start + PRETRACE(); + tr = gi.trace (tr.endpos, NULL, NULL, end, tr.ent, content_mask); + POSTTRACE(); + } +// --- + + // see if we hit water + if (tr.contents & MASK_WATER) + { + int color; + + water = true; + VectorCopy (tr.endpos, water_start); + + if (!VectorCompare (from, tr.endpos)) + { + if (tr.contents & CONTENTS_WATER) + { + if (strcmp(tr.surface->name, "*brwater") == 0) + color = SPLASH_BROWN_WATER; + else + color = SPLASH_BLUE_WATER; + } + else if (tr.contents & CONTENTS_SLIME) + color = SPLASH_SLIME; + else if (tr.contents & CONTENTS_LAVA) + color = SPLASH_LAVA; + else + color = SPLASH_UNKNOWN; + + if (color != SPLASH_UNKNOWN) + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_SPLASH); + gi.WriteByte (8); + gi.WritePosition (tr.endpos); + gi.WriteDir (tr.plane.normal); + gi.WriteByte (color); + gi.multicast (tr.endpos, MULTICAST_PVS); + } + + // change bullet's course when it enters water + VectorSubtract (end, from, dir); + vectoangles (dir, dir); + AngleVectors (dir, forward, right, up); + r = crandom()*hspread*2; + u = crandom()*vspread*2; + VectorMA (water_start, 8192, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + } + + // re-trace ignoring water this time + PRETRACE(); + tr = gi.trace (water_start, NULL, NULL, end, ignore, MASK_SHOT); + POSTTRACE(); + + } + + // send gun puff / flash + + ignore = NULL; + + if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) + { + if (tr.fraction < 1.0) + { + + if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) + { + ignore = tr.ent; + VectorCopy (tr.endpos, from); +//FIREBLADE + // Advance the "from" point a few units + // towards "end" here + if (tr.ent->client) + { + vec3_t tempv; + vec3_t out; + if (ap_already_hit[tr.ent - g_edicts - 1]) + { + VectorSubtract(end, from, tempv); + VectorNormalize(tempv); + VectorScale(tempv, 8, out); + VectorAdd(out, from, from); + continue; + } + ap_already_hit[tr.ent - g_edicts - 1] = true; + } +//FIREBLADE + } + + if ((tr.ent != self) && (tr.ent->takedamage)) + { + T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, mod); + if ( stopAP ) // the AP round hit something that would stop it (kevlar) + ignore = NULL; + } + else if ( tr.ent != self && !water ) + { + //zucc remove spawnhole for real release + // SpawnHole( &tr, dir ); + + if (strncmp (tr.surface->name, "sky", 3) != 0) + { + AddDecal (self, &tr); + gi.WriteByte (svc_temp_entity); + gi.WriteByte (te_impact); + gi.WritePosition (tr.endpos); + gi.WriteDir (tr.plane.normal); + gi.multicast (tr.endpos, MULTICAST_PVS); + + if (self->client) + PlayerNoise(self, tr.endpos, PNOISE_IMPACT); + } + } + } + } + } + // if went through water, determine where the end and make a bubble trail + if (water) + { + vec3_t pos; + + VectorSubtract (tr.endpos, water_start, dir); + VectorNormalize (dir); + VectorMA (tr.endpos, -2, dir, pos); + if (gi.pointcontents (pos) & MASK_WATER) + VectorCopy (pos, tr.endpos); + else + { + PRETRACE(); + tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); + POSTTRACE(); + } + + VectorAdd (water_start, tr.endpos, pos); + VectorScale (pos, 0.5, pos); + + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BUBBLETRAIL); + gi.WritePosition (water_start); + gi.WritePosition (tr.endpos); + gi.multicast (pos, MULTICAST_PVS); + } + +} + + + +// zucc - for the M4 +void fire_bullet_sparks (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) +{ + setFFState(self); + fire_lead_ap (self, start, aimdir, damage, kick, TE_BULLET_SPARKS, hspread, vspread, mod); +} + +// zucc - for sniper +void fire_bullet_sniper (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) +{ + setFFState(self); + fire_lead_ap (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); +} + + +/* + * Init and ProduceShotgunDamageReport + */ + +void InitShotgunDamageReport() +{ + memset(took_damage, 0, game.maxclients * sizeof(int)); +} + +void ProduceShotgunDamageReport(edict_t *self) +{ + int l; + int total_to_print = 0, printed = 0; + static char textbuf[1024]; + +//FB 6/2/99 - shotgun/handcannon damage notification + for (l = 1; l <= game.maxclients; l++) + { + if (took_damage[l - 1]) + total_to_print++; + } + if (total_to_print) + { + if (total_to_print > 10) + total_to_print = 10; + + strcpy(textbuf, "You hit "); + for (l = 1; l <= game.maxclients; l++) + { + if (took_damage[l - 1]) + { + if (printed == (total_to_print - 1)) + { + if (total_to_print == 2) + strcat(textbuf, " and "); + else if (total_to_print != 1) + strcat(textbuf, ", and "); + } + else if (printed) + strcat(textbuf, ", "); + strcat(textbuf, g_edicts[l].client->pers.netname); + printed++; + } + if (printed == total_to_print) + break; + } + gi.cprintf(self, PRINT_HIGH, "%s\n", textbuf); + } +//FB 6/2/99 +} + +/* +================= +fire_shotgun + +Shoots shotgun pellets. Used by shotgun and super shotgun. +================= +*/ +void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) +{ + int i; + + for (i = 0; i < count; i++) + fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod); +} + + +/* +================= +fire_blaster + +Fires a single blaster bolt. Used by the blaster and hyper blaster. +================= +*/ +void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + int mod; + + if (other == self->owner) + return; + + if (surf && (surf->flags & SURF_SKY)) + { + G_FreeEdict (self); + return; + } + + if (self->owner->client) + PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); + + if (other->takedamage) + { + if (self->spawnflags & 1) + mod = MOD_HYPERBLASTER; + else + mod = MOD_BLASTER; + T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); + } + else + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BLASTER); + gi.WritePosition (self->s.origin); + if (!plane) + gi.WriteDir (vec3_origin); + else + gi.WriteDir (plane->normal); + gi.multicast (self->s.origin, MULTICAST_PVS); + } + + G_FreeEdict (self); +} + +void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) +{ + edict_t *bolt; + trace_t tr; + + VectorNormalize (dir); + + bolt = G_Spawn(); +// 3.20 ANTI-LAG FIX, just in case we end up using this code for something... -FB + bolt->svflags = SVF_DEADMONSTER; +// ^^^ + VectorCopy (start, bolt->s.origin); + VectorCopy (start, bolt->s.old_origin); + vectoangles (dir, bolt->s.angles); + VectorScale (dir, speed, bolt->velocity); + bolt->movetype = MOVETYPE_FLYMISSILE; + bolt->clipmask = MASK_SHOT; + bolt->solid = SOLID_BBOX; + bolt->s.effects |= effect; + VectorClear (bolt->mins); + VectorClear (bolt->maxs); + bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); + bolt->s.sound = gi.soundindex ("misc/lasfly.wav"); + bolt->owner = self; + bolt->touch = blaster_touch; + bolt->nextthink = level.time + 2; + bolt->think = G_FreeEdict; + bolt->dmg = damage; + bolt->classname = "bolt"; + if (hyper) + bolt->spawnflags = 1; + gi.linkentity (bolt); + + if (self->client) + check_dodge (self, bolt->s.origin, dir, speed); + + PRETRACE(); + tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); + POSTTRACE(); + if (tr.fraction < 1.0) + { + VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); + bolt->touch (bolt, tr.ent, NULL, NULL); + } +} + +qboolean G_EntExists(edict_t *ent) +{ + return ((ent) && (ent->client));//(ent->inuse)); +} + + + +/* +================= +fire_grenade +================= +*/ +static void Grenade_Explode (edict_t *ent) +{ + vec3_t origin; + int mod; + + + if (ent->owner->client) + PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); + + //FIXME: if we are onground then raise our Z just a bit since we are a point? + if (ent->enemy) + { + float points; + vec3_t v; + vec3_t dir; + + VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); + VectorMA (ent->enemy->s.origin, 0.5, v, v); + VectorSubtract (ent->s.origin, v, v); + points = ent->dmg - 0.5 * VectorLength (v); + VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); + if (ent->spawnflags & 1) + mod = MOD_HANDGRENADE; + else + mod = MOD_GRENADE; + T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); + } + + if (ent->spawnflags & 2) + mod = MOD_HELD_GRENADE; + else if (ent->spawnflags & 1) + mod = MOD_HG_SPLASH; + else + mod = MOD_G_SPLASH; + T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); + + VectorMA (ent->s.origin, -0.02, ent->velocity, origin); + gi.WriteByte (svc_temp_entity); + if (ent->waterlevel) + { + if (ent->groundentity) + gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); + else + gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); + } + else + { + if (ent->groundentity) + gi.WriteByte (TE_GRENADE_EXPLOSION); + else + gi.WriteByte (TE_ROCKET_EXPLOSION); + } + gi.WritePosition (origin); + gi.multicast (ent->s.origin, MULTICAST_PHS); + + G_FreeEdict (ent); +} + +static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + if (other == ent->owner) + return; + + if (surf && (surf->flags & SURF_SKY)) + { + G_FreeEdict (ent); + return; + } + + if (!other->takedamage) + { +/*FIREBLADE + if (ent->spawnflags & 1) + { + if (random() > 0.5) + gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); + else + gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); + } + else +FIREBLADE*/ + { + gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); + } + return; + } + + // zucc not needed since grenades don't blow up on contact + //ent->enemy = other; + //Grenade_Explode (ent); +} + +void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) +{ + edict_t *grenade; + vec3_t dir; + vec3_t forward, right, up; + + vectoangles (aimdir, dir); + AngleVectors (dir, forward, right, up); + + grenade = G_Spawn(); + VectorCopy (start, grenade->s.origin); + VectorScale (aimdir, speed, grenade->velocity); + VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); + VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); + VectorSet (grenade->avelocity, 300, 300, 300); + grenade->movetype = MOVETYPE_BOUNCE; + grenade->clipmask = MASK_SHOT; + grenade->solid = SOLID_BBOX; + grenade->s.effects |= EF_GRENADE; + VectorClear (grenade->mins); + VectorClear (grenade->maxs); + grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2"); + grenade->owner = self; + grenade->touch = Grenade_Touch; + grenade->nextthink = level.time + timer; + grenade->think = Grenade_Explode; + grenade->dmg = damage; + grenade->dmg_radius = damage_radius; + grenade->classname = "grenade"; + + gi.linkentity (grenade); +} + +void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) +{ + edict_t *grenade; + vec3_t dir; + vec3_t forward, right, up; + + vectoangles (aimdir, dir); + AngleVectors (dir, forward, right, up); + + grenade = G_Spawn(); + VectorCopy (start, grenade->s.origin); + VectorScale (aimdir, speed, grenade->velocity); + VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); + VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); + VectorSet (grenade->avelocity, 300, 300, 300); + grenade->movetype = MOVETYPE_BOUNCE; + grenade->clipmask = MASK_SHOT; + grenade->solid = SOLID_BBOX; + //grenade->s.effects |= EF_GRENADE; + VectorClear (grenade->mins); + VectorClear (grenade->maxs); + grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); + grenade->owner = self; + grenade->touch = Grenade_Touch; + grenade->nextthink = level.time + timer; + grenade->think = Grenade_Explode; + grenade->dmg = damage; + grenade->dmg_radius = damage_radius; + grenade->classname = "hgrenade"; + if (held) + grenade->spawnflags = 3; + else + grenade->spawnflags = 1; + //grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); + + if (timer <= 0.0) + Grenade_Explode (grenade); + else + { + gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0); + gi.linkentity (grenade); + } +} + + +/* +================= +fire_rocket +================= +*/ +void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + vec3_t origin; + int n; + + if (other == ent->owner) + return; + + if (surf && (surf->flags & SURF_SKY)) + { + G_FreeEdict (ent); + return; + } + + if (ent->owner->client) + PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); + + // calculate position for the explosion entity + VectorMA (ent->s.origin, -0.02, ent->velocity, origin); + + if (other->takedamage) + { + T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); + } + else + { + // don't throw any debris in net games + if (!deathmatch->value && !coop->value) + { + if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING))) + { + n = rand() % 5; + while(n--) + ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); + } + } + } + + T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); + + gi.WriteByte (svc_temp_entity); + if (ent->waterlevel) + gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); + else + gi.WriteByte (TE_ROCKET_EXPLOSION); + gi.WritePosition (origin); + gi.multicast (ent->s.origin, MULTICAST_PHS); + + G_FreeEdict (ent); +} + +void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) +{ + edict_t *rocket; + + rocket = G_Spawn(); + VectorCopy (start, rocket->s.origin); + VectorCopy (dir, rocket->movedir); + vectoangles (dir, rocket->s.angles); + VectorScale (dir, speed, rocket->velocity); + rocket->movetype = MOVETYPE_FLYMISSILE; + rocket->clipmask = MASK_SHOT; + rocket->solid = SOLID_BBOX; + rocket->s.effects |= EF_ROCKET; + VectorClear (rocket->mins); + VectorClear (rocket->maxs); + rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); + rocket->owner = self; + rocket->touch = rocket_touch; + rocket->nextthink = level.time + 8000/speed; + rocket->think = G_FreeEdict; + rocket->dmg = damage; + rocket->radius_dmg = radius_damage; + rocket->dmg_radius = damage_radius; + rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); + rocket->classname = "rocket"; + + if (self->client) + check_dodge (self, rocket->s.origin, dir, speed); + + gi.linkentity (rocket); +} + + +/* +================= +fire_rail +================= +*/ +void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) +{ + vec3_t from; + vec3_t end; + trace_t tr; + edict_t *ignore; + int mask; + qboolean water; + + VectorMA (start, 8192, aimdir, end); + VectorCopy (start, from); + ignore = self; + water = false; + mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; + while (ignore) + { + PRETRACE(); + tr = gi.trace (from, NULL, NULL, end, ignore, mask); + POSTTRACE(); + + if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA)) + { + mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA); + water = true; + } + else + { +// FROM 3.20, just in case we ever end up using this code for something... -FB + //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc) + if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) || + (tr.ent->solid == SOLID_BBOX)) +// ^^^ + ignore = tr.ent; + else + ignore = NULL; + + if ((tr.ent != self) && (tr.ent->takedamage)) + T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); + } + + VectorCopy (tr.endpos, from); + } + + // send gun puff / flash + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_RAILTRAIL); + gi.WritePosition (start); + gi.WritePosition (tr.endpos); + gi.multicast (self->s.origin, MULTICAST_PHS); +// gi.multicast (start, MULTICAST_PHS); + if (water) + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_RAILTRAIL); + gi.WritePosition (start); + gi.WritePosition (tr.endpos); + gi.multicast (tr.endpos, MULTICAST_PHS); + } + + if (self->client) + PlayerNoise(self, tr.endpos, PNOISE_IMPACT); +} + + +/* +================= +fire_bfg +================= +*/ +void bfg_explode (edict_t *self) +{ + edict_t *ent; + float points; + vec3_t v; + float dist; + + if (self->s.frame == 0) + { + // the BFG effect + ent = NULL; + while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) + { + if (!ent->takedamage) + continue; + if (ent == self->owner) + continue; + if (!CanDamage (ent, self)) + continue; + if (!CanDamage (ent, self->owner)) + continue; + + VectorAdd (ent->mins, ent->maxs, v); + VectorMA (ent->s.origin, 0.5, v, v); + VectorSubtract (self->s.origin, v, v); + dist = VectorLength(v); + points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); + if (ent == self->owner) + points = points * 0.5; + + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BFG_EXPLOSION); + gi.WritePosition (ent->s.origin); + gi.multicast (ent->s.origin, MULTICAST_PHS); + T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); + } + } + + self->nextthink = level.time + FRAMETIME; + self->s.frame++; + if (self->s.frame == 5) + self->think = G_FreeEdict; +} + +void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + if (other == self->owner) + return; + + if (surf && (surf->flags & SURF_SKY)) + { + G_FreeEdict (self); + return; + } + + if (self->owner->client) + PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); + + // core explosion - prevents firing it into the wall/floor + if (other->takedamage) + T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); + T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); + + gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); + self->solid = SOLID_NOT; + self->touch = NULL; + VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); + VectorClear (self->velocity); + self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2"); + self->s.frame = 0; + self->s.sound = 0; + self->s.effects &= ~EF_ANIM_ALLFAST; + self->think = bfg_explode; + self->nextthink = level.time + FRAMETIME; + self->enemy = other; + + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BFG_BIGEXPLOSION); + gi.WritePosition (self->s.origin); + gi.multicast (self->s.origin, MULTICAST_PVS); +} + + +void bfg_think (edict_t *self) +{ + edict_t *ent; + edict_t *ignore; + vec3_t point; + vec3_t dir; + vec3_t start; + vec3_t end; + int dmg; + trace_t tr; + + if (deathmatch->value) + dmg = 5; + else + dmg = 10; + + ent = NULL; + while ((ent = findradius(ent, self->s.origin, 256)) != NULL) + { + if (ent == self) + continue; + + if (ent == self->owner) + continue; + + if (!ent->takedamage) + continue; + + if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) + continue; + + VectorMA (ent->absmin, 0.5, ent->size, point); + + VectorSubtract (point, self->s.origin, dir); + VectorNormalize (dir); + + ignore = self; + VectorCopy (self->s.origin, start); + VectorMA (start, 2048, dir, end); + while(1) + { + PRETRACE(); + tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); + POSTTRACE(); + + if (!tr.ent) + break; + + // hurt it if we can + if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) + T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); + + // if we hit something that's not a monster or player we're done + if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_LASER_SPARKS); + gi.WriteByte (4); + gi.WritePosition (tr.endpos); + gi.WriteDir (tr.plane.normal); + gi.WriteByte (self->s.skinnum); + gi.multicast (tr.endpos, MULTICAST_PVS); + break; + } + + ignore = tr.ent; + VectorCopy (tr.endpos, start); + } + + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BFG_LASER); + gi.WritePosition (self->s.origin); + gi.WritePosition (tr.endpos); + gi.multicast (self->s.origin, MULTICAST_PHS); + } + + self->nextthink = level.time + FRAMETIME; +} + + +void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) +{ + edict_t *bfg; + + bfg = G_Spawn(); + VectorCopy (start, bfg->s.origin); + VectorCopy (dir, bfg->movedir); + vectoangles (dir, bfg->s.angles); + VectorScale (dir, speed, bfg->velocity); + bfg->movetype = MOVETYPE_FLYMISSILE; + bfg->clipmask = MASK_SHOT; + bfg->solid = SOLID_BBOX; + bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; + VectorClear (bfg->mins); + VectorClear (bfg->maxs); + bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); + bfg->owner = self; + bfg->touch = bfg_touch; + bfg->nextthink = level.time + 8000/speed; + bfg->think = G_FreeEdict; + bfg->radius_dmg = damage; + bfg->dmg_radius = damage_radius; + bfg->classname = "bfg blast"; + bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); + + bfg->think = bfg_think; + bfg->nextthink = level.time + FRAMETIME; + bfg->teammaster = bfg; + bfg->teamchain = NULL; + + if (self->client) + check_dodge (self, bfg->s.origin, dir, speed); + + gi.linkentity (bfg); +} + + +void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result); + +qboolean IsFemale (edict_t *ent); + +void kick_attack (edict_t * ent ) +{ + vec3_t start; + vec3_t forward, right; + vec3_t offset; + int damage = 20; + int kick = 400; + trace_t tr; + vec3_t end; + + AngleVectors (ent->client->v_angle, forward, right, NULL); + + VectorScale (forward, 0, ent->client->kick_origin); + + VectorSet(offset, 0, 0, ent->viewheight-20); + P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); + + VectorMA( start, 25, forward, end ); + + PRETRACE(); + tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, MASK_SHOT); + POSTTRACE(); + + // don't need to check for water + if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) + { + if (tr.fraction < 1.0) + { + if (tr.ent->takedamage || KickDoor(&tr, ent, forward)) + { + if (teamplay->value) + { + if (lights_camera_action) + return; + + if (tr.ent != ent && tr.ent->client && ent->client && + tr.ent->client->resp.team == ent->client->resp.team) + return; + } + + if ( tr.ent->health <= 0 ) + return; + + if (((tr.ent != ent) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && OnSameTeam (tr.ent, ent))) + return; + // zucc stop powerful upwards kicking + //forward[2] = 0; + +// glass fx + if (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + { + CGF_SFX_ShootBreakableGlass(tr.ent, ent, &tr, MOD_KICK); + } + else +// --- + T_Damage (tr.ent, ent, ent, forward, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KICK ); + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/kick.wav"), 1, ATTN_NORM, 0); + PlayerNoise(ent, ent->s.origin, PNOISE_SELF); + ent->client->jumping = 0; // only 1 jumpkick per jump + if (tr.ent->client && ( tr.ent->client->curr_weap == M4_NUM + || tr.ent->client->curr_weap == MP5_NUM + || tr.ent->client->curr_weap == M3_NUM + || tr.ent->client->curr_weap == SNIPER_NUM + || tr.ent->client->curr_weap == HC_NUM ) + && 1 ) // crandom() > .8 ) + { + + // zucc fix this so reloading won't happen on the new gun! + tr.ent->client->reload_attempts = 0; + DropSpecialWeapon(tr.ent); + if (IsFemale(tr.ent)) + { + gi.cprintf(ent, PRINT_HIGH, "You kick %s's %s from her hands!\n", tr.ent->client->pers.netname, (tr.ent->client->pers.weapon)->pickup_name ); + } + else + { + gi.cprintf(ent, PRINT_HIGH, "You kick %s's %s from his hands!\n", tr.ent->client->pers.netname, (tr.ent->client->pers.weapon)->pickup_name ); + } + gi.cprintf(tr.ent, PRINT_HIGH, "%s kicked your weapon from your hands!\n", ent->client->pers.netname ); + } + + } + + } + } + +} + +// zucc +// return values +// 0 - missed +// 1 - hit player +// 2 - hit wall + + +int knife_attack ( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) +{ + trace_t tr; + vec3_t end; + + + VectorMA (start, 45, aimdir, end); + + PRETRACE(); + tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT); + POSTTRACE(); + + // don't need to check for water + if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) + { + if (tr.fraction < 1.0) + { +//glass fx + if (0 == Q_stricmp(tr.ent->classname, "func_explosive")) + { + CGF_SFX_ShootBreakableGlass(tr.ent, self, &tr, MOD_KNIFE); + } + else +// --- + if (tr.ent->takedamage) + { + setFFState(self); + T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KNIFE ); + return -2; + } + else + { + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_SPARKS); + gi.WritePosition (tr.endpos); + gi.WriteDir (tr.plane.normal); + gi.multicast (tr.endpos, MULTICAST_PVS); + return -1; + } + + } + else + return 0; + } + return 0; // we hit the sky, call it a miss +} + +static int knives = 0; + +void knife_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) +{ + vec3_t origin; + // int n; + + edict_t *dropped; + edict_t *knife; + // vec3_t forward, right, up; + vec3_t move_angles; + gitem_t *item; + + + if (other == ent->owner) + return; + + if (surf && (surf->flags & SURF_SKY)) + { + G_FreeEdict (ent); + return; + } + + if (ent->owner->client) + { + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/clank.wav"), 1, ATTN_NORM, 0); + PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); + } + + // calculate position for the explosion entity + VectorMA (ent->s.origin, -0.02, ent->velocity, origin); + +//glass fx + if (0 == Q_stricmp(other->classname, "func_explosive")) + { + // ignore it, so it can bounce + return; + } + else +// --- + if (other->takedamage) + { + T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_KNIFE_THROWN); + } + else + { + + // code to manage excess knives in the game, guarantees that + // no more than knifelimit knives will be stuck in walls. + // if knifelimit == 0 then it won't be in effect and it can + // start removing knives even when less than the limit are + // out there. + if ( knifelimit->value != 0 ) + { + knives++; + + if (knives > knifelimit->value) + knives = 1; + + knife = FindEdictByClassnum ("weapon_Knife", knives); + + if (knife) + { + knife->nextthink = level.time + .1; + } + + } + + dropped = G_Spawn(); + item = FindItem(KNIFE_NAME); + + dropped->classname = item->classname; + dropped->item = item; + dropped->spawnflags = DROPPED_ITEM; + dropped->s.effects = item->world_model_flags; + dropped->s.renderfx = RF_GLOW; + VectorSet (dropped->mins, -15, -15, -15); + VectorSet (dropped->maxs, 15, 15, 15); + gi.setmodel (dropped, dropped->item->world_model); + dropped->solid = SOLID_TRIGGER; + dropped->movetype = MOVETYPE_TOSS; + dropped->touch = Touch_Item; + dropped->owner = ent; + dropped->gravity = 0; + dropped->classnum = knives; + + vectoangles (ent->velocity, move_angles); + //AngleVectors (ent->s.angles, forward, right, up); + VectorCopy (ent->s.origin, dropped->s.origin); + VectorCopy (move_angles, dropped->s.angles); + //VectorScale (forward, 100, dropped->velocity); + //dropped->velocity[2] = 300; + + //dropped->think = drop_make_touchable; + //dropped->nextthink = level.time + 1; + + dropped->nextthink = level.time + 120; + dropped->think = G_FreeEdict; + + gi.linkentity (dropped); + + + if ( !(ent->waterlevel) ) + { + /*gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_WELDING_SPARKS); + gi.WritePosition (origin); + gi.multicast (ent->s.origin, MULTICAST_PHS); + */ + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_SPARKS); + gi.WritePosition (origin); + gi.WriteDir (plane->normal); + gi.multicast (ent->s.origin, MULTICAST_PVS); + } + + } + G_FreeEdict (ent); +} + + + + +void knife_throw (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed ) +{ + edict_t *knife; +// vec3_t forward, right, up; + trace_t tr; + + knife = G_Spawn(); + + VectorNormalize (dir); + VectorCopy (start, knife->s.origin); + VectorCopy (start, knife->s.old_origin); + vectoangles (dir, knife->s.angles); + VectorScale (dir, speed, knife->velocity); + knife->movetype = MOVETYPE_TOSS; + +/* + VectorCopy (start, knife->s.origin); + VectorCopy (dir, knife->movedir); + vectoangles (dir, knife->s.angles); + VectorScale (dir, speed, knife->velocity); +*/ + + // gi.cprintf(self, PRINT_HIGH, "speed %d\n", speed); + // below add upwards trajectory, not in action though + // AngleVectors (knife->s.angles, forward, right, up); + // VectorMA (knife->velocity, 210, up, knife->velocity); + VectorSet (knife->avelocity, 1200, 0, 0); + + knife->movetype = MOVETYPE_TOSS; + knife->clipmask = MASK_SHOT; + knife->solid = SOLID_BBOX; + knife->s.effects = 0; //EF_ROTATE? + VectorClear (knife->mins); + VectorClear (knife->maxs); + knife->s.modelindex = gi.modelindex ("models/objects/knife/tris.md2"); + knife->owner = self; + knife->touch = knife_touch; + knife->nextthink = level.time + 8000/speed; + knife->think = G_FreeEdict; + knife->dmg = damage; + knife->s.sound = gi.soundindex ("misc/flyloop.wav"); + knife->classname = "thrown_knife"; + +// used by dodging monsters, skip +// if (self->client) +// check_dodge (self, rocket->s.origin, dir, speed); + + PRETRACE(); + tr = gi.trace (self->s.origin, NULL, NULL, knife->s.origin, knife, MASK_SHOT); + POSTTRACE(); + if (tr.fraction < 1.0) + { + VectorMA (knife->s.origin, -10, dir, knife->s.origin); + knife->touch (knife, tr.ent, NULL, NULL); + } + + + gi.linkentity (knife); +} + +/* +===================================================================== +setFFState: Save team wound count & warning state before an attack + +The purpose of this is so that we can increment team_wounds by 1 for +each real attack instead of just counting each bullet/pellet/shrapnel +as a wound. The ff_warning flag is so that we don't overflow the +clients from repeated FF warnings. Hopefully the overhead on this +will be low enough to not affect things. +===================================================================== +*/ +void setFFState(edict_t *ent) +{ + if (ent && ent->client) + { + ent->client->team_wounds_before = ent->client->team_wounds; + ent->client->ff_warning = 0; + } + + return; +} diff --git a/p_client.c b/p_client.c index 9d74f41..028a197 100644 --- a/p_client.c +++ b/p_client.c @@ -1,5 +1,6 @@ #include "g_local.h" #include "m_player.h" +#include "cgf_sfx_glass.h" void ClientUserinfoChanged (edict_t *ent, char *userinfo); void ClientDisconnect(edict_t *ent); @@ -54,6 +55,141 @@ void Subtract_Frag( edict_t *ent ) } +// FRIENDLY FIRE functions +extern qboolean Ban_TeamKiller ( edict_t *ent, int rounds ); // g_svcmds.c +extern void Kick_Client ( edict_t *ent); // g_svcmds.c + +void Add_TeamWound( edict_t *attacker, edict_t *victim, int mod) +{ + if (!teamplay->value || !attacker->client || !victim->client) + { + return; + } + + attacker->client->team_wounds++; + + // Warn both parties that they are teammates. Since shotguns are pellet based, + // make sure we don't overflow the client when using MOD_HC or MOD_SHOTGUN. The + // ff_warning flag should have been reset before each attack. + if (attacker->client->ff_warning == 0) + { + attacker->client->ff_warning++; + gi.cprintf(victim, PRINT_HIGH, "You were hit by %s, your TEAMMATE!\n", + attacker->client->pers.netname); + gi.cprintf(attacker, PRINT_HIGH, "You hit your TEAMMATE %s!\n", + victim->client->pers.netname); + } + + // We want team_wounds to increment by one for each ATTACK, not after each + // bullet or pellet does damage. With the HAND CANNON this means 2 attacks + // since it is double barreled and we don't want to go into p_weapon.c... + attacker->client->team_wounds = (attacker->client->team_wounds_before + 1); + + // If count is less than MAX_TEAMKILLS*3, return. If count is greater than + // MAX_TEAMKILLS*3 but less than MAX_TEAMKILLS*4, print off a ban warning. If + // count equal (or greater than) MAX_TEAMKILLS*4, ban and kick the client. + if ((int)maxteamkills->value < 1) //FB + return; + if (attacker->client->team_wounds < ((int)maxteamkills->value * 3)) + { + return; + } + else if (attacker->client->team_wounds < ((int)maxteamkills->value * 4)) + { + // Print a note to console, and issue a warning to the player. + gi.cprintf(NULL, PRINT_MEDIUM, + "%s is in danger of being banned for wounding teammates\n", + attacker->client->pers.netname); + gi.cprintf(attacker, PRINT_HIGH, + "WARNING: You'll be temporarily banned if you continue wounding teammates!\n"); + return; + } + else + { + if (attacker->client->ipaddr) + { + if (Ban_TeamKiller(attacker, (int)twbanrounds->value)) + { + gi.cprintf(NULL, PRINT_MEDIUM, + "Banning %s@%s for team wounding\n", + attacker->client->pers.netname, attacker->client->ipaddr); + + gi.cprintf(attacker, PRINT_HIGH, + "You've wounded teammates too many times, and are banned for %d %s.\n", + (int)twbanrounds->value, (((int)twbanrounds->value > 1) ? "games" : "game")); + } + else + { + gi.cprintf(NULL, PRINT_MEDIUM, + "Error banning %s: unable to get ipaddr\n", + attacker->client->pers.netname); + } + Kick_Client(attacker); + } + } + + return; +} + +void Add_TeamKill( edict_t *attacker ) +{ + if (!teamplay->value || !attacker->client) + { + return; + } + + attacker->client->team_kills++; + // Because the stricter team kill was incremented, lower team_wounds + // by amount inflicted in last attack (i.e., no double penalty). + if (attacker->client->team_wounds > attacker->client->team_wounds_before) + { + attacker->client->team_wounds = attacker->client->team_wounds_before; + } + + // If count is less than 1/2 MAX_TEAMKILLS, print off simple warning. If + // count is greater than 1/2 MAX_TEAMKILLS but less than MAX_TEAMKILLS, + // print off a ban warning. If count equal or greater than MAX_TEAMKILLS, + // ban and kick the client. + if (((int)maxteamkills->value < 1) || + (attacker->client->team_kills < (((int)maxteamkills->value % 2) + (int)maxteamkills->value / 2))) + { + gi.cprintf(attacker, PRINT_HIGH, "You killed your TEAMMATE!\n"); + return; + } + else if (attacker->client->team_kills < (int)maxteamkills->value) + { + // Show this on the console + gi.cprintf(NULL, PRINT_MEDIUM, "%s is in danger of being banned for killing teammates\n", attacker->client->pers.netname); + // Issue a warning to the player + gi.cprintf(attacker, PRINT_HIGH, "WARNING: You'll be temporarily banned if you continue killing teammates!\n" ); + return; + } + else + { + // They've killed too many teammates this game - kick 'em for a while + if (attacker->client->ipaddr) + { + if (Ban_TeamKiller(attacker, (int)tkbanrounds->value)) + { + gi.cprintf (NULL, PRINT_MEDIUM, + "Banning %s@%s for team killing\n", + attacker->client->pers.netname, + attacker->client->ipaddr); + gi.cprintf(attacker, PRINT_HIGH, "You've killed too many teammates, and are banned for %d %s.\n", (int)tkbanrounds->value, + (((int)tkbanrounds->value > 1) ? "games" : "game")); + } + else + { + gi.cprintf(NULL, PRINT_MEDIUM, + "Error banning %s: unable to get ipaddr\n", + attacker->client->pers.netname); + } + } + Kick_Client(attacker); + } +} +// FRIENDLY FIRE + // // Gross, ugly, disgustuing hack section @@ -271,536 +407,577 @@ void PrintDeathMessage(char *msg, edict_t *gibee) void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker) { - int mod; - int loc; - char *message; - char *message2; - char death_msg[2048]; // enough in all situations? -FB - qboolean ff; - int special = 0; - int n; - - if (coop->value && attacker->client) + int mod; + int loc; + char *message; + char *message2; + char death_msg[2048]; // enough in all situations? -FB + qboolean ff; + int special = 0; + int n; + + if (coop->value && attacker->client) + meansOfDeath |= MOD_FRIENDLY_FIRE; + + if (attacker && attacker != self && attacker->client && OnSameTeam (self, attacker)) meansOfDeath |= MOD_FRIENDLY_FIRE; - - if (deathmatch->value || coop->value) - { - ff = meansOfDeath & MOD_FRIENDLY_FIRE; - mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; - loc = locOfDeath; // useful for location based hits - message = NULL; - message2 = ""; - - switch (mod) - { - case MOD_SUICIDE: - message = "suicides"; - break; - case MOD_FALLING: - // moved falling to the end - if (self->client->attacker) - special = 1; - //message = "hit the ground hard, real hard"; - if (IsNeutral(self)) - message = "plummets to its death"; - else if (IsFemale(self)) - message = "plummets to her death"; - else - message = "plummets to his death"; - break; - case MOD_CRUSH: - message = "was flattened"; - break; - case MOD_WATER: - message = "sank like a rock"; - break; - case MOD_SLIME: - message = "melted"; - break; - case MOD_LAVA: - message = "does a back flip into the lava"; - break; - case MOD_EXPLOSIVE: - case MOD_BARREL: - message = "blew up"; - break; - case MOD_EXIT: - message = "found a way out"; - break; - case MOD_TARGET_LASER: - message = "saw the light"; - break; - case MOD_TARGET_BLASTER: - message = "got blasted"; - break; - case MOD_BOMB: - case MOD_SPLASH: - case MOD_TRIGGER_HURT: - message = "was in the wrong place"; - break; - } - if (attacker == self) - { - switch (mod) - { - case MOD_HELD_GRENADE: - message = "tried to put the pin back in"; - break; - case MOD_HG_SPLASH: - if (IsNeutral(self)) - message = "didn't throw its grenade far enough"; - if (IsFemale(self)) - message = "didn't throw her grenade far enough"; - else - message = "didn't throw his grenade far enough"; - break; - case MOD_G_SPLASH: - if (IsNeutral(self)) - message = "tripped on its own grenade"; - else if (IsFemale(self)) - message = "tripped on her own grenade"; - else - message = "tripped on his own grenade"; - break; - case MOD_R_SPLASH: - if (IsNeutral(self)) - message = "blew itself up"; - else if (IsFemale(self)) - message = "blew herself up"; - else - message = "blew himself up"; - break; - case MOD_BFG_BLAST: - message = "should have used a smaller gun"; - break; - default: - if (IsNeutral(self)) - message = "killed itself"; - else if (IsFemale(self)) - message = "killed herself"; - else - message = "killed himself"; - break; - } - } - if (message && !special) - { - sprintf(death_msg, "%s %s\n", self->client->pers.netname, message); - PrintDeathMessage(death_msg, self); - if (deathmatch->value) - Subtract_Frag( self );//self->client->resp.score--; - self->enemy = NULL; - return; - } - else if ( special ) // handle falling with an attacker set - { - if ( self->client->attacker->client && - (self->client->attacker->client != - self->client)) - { - sprintf(death_msg, "%s was taught how to fly by %s\n", - self->client->pers.netname, self->client->attacker->client->pers.netname ); - PrintDeathMessage(death_msg, self); - Add_Frag( self->client->attacker );//self->client->attacker->client->resp.score++; - } - else - { - if (IsNeutral(self)) - sprintf(death_msg, "%s plummets to its death\n", self->client->pers.netname); - else if (IsFemale(self)) - sprintf(death_msg, "%s plummets to her death\n", self->client->pers.netname); - else - sprintf(death_msg, "%s plummets to his death\n", self->client->pers.netname); - PrintDeathMessage(death_msg, self); - } - return; - } - - self->enemy = attacker; - if (attacker && attacker->client) - { - switch (mod) - { - // zucc - case MOD_MK23: - switch (loc) - { - - case LOC_HDAM: - if (IsNeutral(self)) - message = " has a hole in its head from"; - else if (IsFemale(self)) - message = " has a hole in her head from"; - else - message = " has a hole in his head from"; - message2 = "'s Mark 23 pistol"; - break; - case LOC_CDAM: - message = " loses a vital chest organ thanks to"; - message2 = "'s Mark 23 pistol"; - break; - case LOC_SDAM: - if (IsNeutral(self)) - message = " loses its lunch to"; - else if (IsFemale(self)) - message = " loses her lunch to"; - else - message = " loses his lunch to"; - message2 = "'s .45 caliber pistol round"; - break; - case LOC_LDAM: - message = " is legless because of"; - message2 = "'s .45 caliber pistol round"; - break; - default: - message = " was shot by"; - message2 = "'s Mark 23 Pistol"; - } - break; - case MOD_MP5: - switch (loc) - { - case LOC_HDAM: - message = "'s brains are on the wall thanks to"; - message2 = "'s 10mm MP5/10 round"; - break; - case LOC_CDAM: - message = " feels some chest pain via"; - message2 = "'s MP5/10 Submachinegun"; - break; - case LOC_SDAM: - message = " needs some Pepto Bismol after"; - message2 = "'s 10mm MP5 round"; - break; - case LOC_LDAM: - if (IsNeutral(self)) - message = " had its legs blown off thanks to"; - else if (IsFemale(self)) - message = " had her legs blown off thanks to"; - else - message = " had his legs blown off thanks to"; - message2 = "'s MP5/10 Submachinegun"; - break; - default: - message = " was shot by"; - message2 = "'s MP5/10 Submachinegun"; - } - break; - - - case MOD_M4: - switch (loc) - { - case LOC_HDAM: - message = " had a makeover by"; - message2 = "'s M4 Assault Rifle"; - break; - case LOC_CDAM: - message = " feels some heart burn thanks to"; - message2 = "'s M4 Assault Rifle"; - break; - case LOC_SDAM: - message = " has an upset stomach thanks to"; - message2 = "'s M4 Assault Rifle"; - break; - case LOC_LDAM: - message = " is now shorter thanks to"; - message2 = "'s M4 Assault Rifle"; - break; - default: - message = " was shot by"; - message2 = "'s M4 Assault Rifle"; - } - break; - case MOD_M3: - n = rand() % 2 + 1; - if (n == 1) - {message = " accepts"; message2 = "'s M3 Super 90 Assault Shotgun in hole-y matrimony";} - if (n == 2) - {message = " is full of buckshot from"; message2 = "'s M3 Super 90 Assault Shotgun";} - break; - case MOD_HC: - n = rand() % 2 + 1; - if (n == 1) - {message = " ate"; message2 = "'s sawed-off 12 gauge";} - if (n == 2) - {message = " is full of buckshot from"; message2 = "'s sawed off shotgun";} - break; - case MOD_SNIPER: - switch (loc) - { - case LOC_HDAM: - if (self->client->ps.fov < 90) - { - if (IsNeutral(self)) - message = " saw the sniper bullet go through its scope thanks to"; - else if (IsFemale(self)) - message = " saw the sniper bullet go through her scope thanks to"; - else - message = " saw the sniper bullet go through his scope thanks to"; - } - else - message = " caught a sniper bullet between the eyes from"; - break; - case LOC_CDAM: - message = " was picked off by"; - break; - case LOC_SDAM: - message = " was sniped in the stomach by"; - break; - case LOC_LDAM: - message = " was shot in the legs by"; - break; - default: - message = "was sniped by"; - //message2 = "'s Sniper Rifle"; - } - break; - case MOD_DUAL: - switch (loc) - { - case LOC_HDAM: - message = " was trepanned by"; - message2 = "'s akimbo Mark 23 pistols"; - break; - case LOC_CDAM: - message = " was John Woo'd by"; - // message2 = "'s .45 caliber pistol round"; - break; - case LOC_SDAM: - message = " needs some new kidneys thanks to"; - message2 = "'s akimbo Mark 23 pistols"; - break; - case LOC_LDAM: - message = " was shot in the legs by"; - message2 = "'s akimbo Mark 23 pistols"; - break; - - - - default: - message = " was shot by"; - message2 = "'s pair of Mark 23 Pistols"; - } - break; - case MOD_KNIFE: - switch (loc) - { - - - case LOC_HDAM: - if (IsNeutral(self)) - message = " had its throat slit by"; - else if (IsFemale(self)) - message = " had her throat slit by"; - else - message = " had his throat slit by"; - break; - case LOC_CDAM: - message = " had open heart surgery, compliments of"; - break; - case LOC_SDAM: - message = " was gutted by"; - break; - case LOC_LDAM: - message = " was stabbed repeatedly in the legs by"; - break; - default: - message = " was slashed apart by"; - message2 = "'s Combat Knife"; - } - break; - case MOD_KNIFE_THROWN: - switch (loc) - { - case LOC_HDAM: - message = " caught"; - if (IsNeutral(self)) - message2 = "'s flying knife with its forehead"; - else if (IsFemale(self)) - message2 = "'s flying knife with her forehead"; - else - message2 = "'s flying knife with his forehead"; - break; - case LOC_CDAM: - message = "'s ribs don't help against"; - message2 = "'s flying knife"; - break; - case LOC_SDAM: - if (IsNeutral(self)) - message = " sees the contents of its own stomach thanks to"; - else if (IsFemale(self)) - message = " sees the contents of her own stomach thanks to"; - else - message = " sees the contents of his own stomach thanks to"; - message2 = "'s flying knife"; - break; - case LOC_LDAM: - if (IsNeutral(self)) - message = " had its legs cut off thanks to"; - else if (IsFemale(self)) - message = " had her legs cut off thanks to"; - else - message = " had his legs cut off thanks to"; - message2 = "'s flying knife"; - break; - default: - message = " was hit by"; - message2 = "'s flying Combat Knife"; - - - - } - - break; - case MOD_GAS: - message = "sucks down some toxic gas thanks to"; - break; - - case MOD_KICK: - n = rand() % 3 + 1; - if (n == 1) - { - - if (IsNeutral(self)) - message = " got its ass kicked by"; - else if (IsFemale(self)) - message = " got her ass kicked by"; - else - message = " got his ass kicked by"; - } - else if (n == 2) - { - if (IsNeutral(self)) - { - message = " couldn't remove"; message2 = "'s boot from its ass"; - } - else if (IsFemale(self)) - { - message = " couldn't remove"; message2 = "'s boot from her ass"; - } - else - { - message = " couldn't remove"; message2 = "'s boot from his ass"; - } - } - else - { - if (IsNeutral(self)) - { - message = " had a Bruce Lee put on it by"; message2 = ", with a quickness"; - } - else if (IsFemale(self)) - { - message = " had a Bruce Lee put on her by"; message2 = ", with a quickness"; - } - else - { - message = " had a Bruce Lee put on him by"; message2 = ", with a quickness"; - } - } - - break; - - case MOD_BLASTER: - message = "was blasted by"; - break; - case MOD_SHOTGUN: - message = "was gunned down by"; - break; - case MOD_SSHOTGUN: - message = "was blown away by"; - message2 = "'s super shotgun"; - break; - case MOD_MACHINEGUN: - message = "was machinegunned by"; - break; - case MOD_CHAINGUN: - message = "was cut in half by"; - message2 = "'s chaingun"; - break; - case MOD_GRENADE: - message = "was popped by"; - message2 = "'s grenade"; - break; - case MOD_G_SPLASH: - message = "was shredded by"; - message2 = "'s shrapnel"; - break; - case MOD_ROCKET: - message = "ate"; - message2 = "'s rocket"; - break; - case MOD_R_SPLASH: - message = "almost dodged"; - message2 = "'s rocket"; - break; - case MOD_HYPERBLASTER: - message = "was melted by"; - message2 = "'s hyperblaster"; - break; - case MOD_RAILGUN: - message = "was railed by"; - break; - case MOD_BFG_LASER: - message = "saw the pretty lights from"; - message2 = "'s BFG"; - break; - case MOD_BFG_BLAST: - message = "was disintegrated by"; - message2 = "'s BFG blast"; - break; - case MOD_BFG_EFFECT: - message = "couldn't hide from"; - message2 = "'s BFG"; - break; - case MOD_HANDGRENADE: - message = " caught"; - message2 = "'s handgrenade"; - break; - case MOD_HG_SPLASH: - message = " didn't see"; - message2 = "'s handgrenade"; - break; - case MOD_HELD_GRENADE: - message = " feels"; - message2 = "'s pain"; - break; - case MOD_TELEFRAG: - message = " tried to invade"; - message2 = "'s personal space"; - break; - } - if (message) + if (deathmatch->value || coop->value) + { + ff = meansOfDeath & MOD_FRIENDLY_FIRE; + mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; + loc = locOfDeath; // useful for location based hits + message = NULL; + message2 = ""; + + switch (mod) + { + case MOD_BREAKINGGLASS: + message = "ate too much glass"; + break; + case MOD_SUICIDE: + message = "suicides"; + break; + case MOD_FALLING: + // moved falling to the end + if (self->client->push_timeout) + special = 1; + //message = "hit the ground hard, real hard"; + if (IsNeutral(self)) + message = "plummets to its death"; + else if (IsFemale(self)) + message = "plummets to her death"; + else + message = "plummets to his death"; + break; + case MOD_CRUSH: + message = "was flattened"; + break; + case MOD_WATER: + message = "sank like a rock"; + break; + case MOD_SLIME: + message = "melted"; + break; + case MOD_LAVA: + message = "does a back flip into the lava"; + break; + case MOD_EXPLOSIVE: + case MOD_BARREL: + message = "blew up"; + break; + case MOD_EXIT: + message = "found a way out"; + break; + case MOD_TARGET_LASER: + message = "saw the light"; + break; + case MOD_TARGET_BLASTER: + message = "got blasted"; + break; + case MOD_BOMB: + case MOD_SPLASH: + case MOD_TRIGGER_HURT: + message = "was in the wrong place"; + break; + } + if (attacker == self) + { + switch (mod) { - //FIREBLADE - sprintf(death_msg, "%s%s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2); - PrintDeathMessage(death_msg, self); - //FIREBLADE - if (deathmatch->value) - { - if (ff) - Subtract_Frag( attacker );//attacker->client->resp.score--; - else - { - //FIREBLADE - if (!teamplay->value || mod != MOD_TELEFRAG) - //FIREBLADE - Add_Frag(attacker );//attacker->client->resp.score++; - } - } - return; + case MOD_HELD_GRENADE: + message = "tried to put the pin back in"; + break; + case MOD_HG_SPLASH: + if (IsNeutral(self)) + message = "didn't throw its grenade far enough"; + if (IsFemale(self)) + message = "didn't throw her grenade far enough"; + else + message = "didn't throw his grenade far enough"; + break; + case MOD_G_SPLASH: + if (IsNeutral(self)) + message = "tripped on its own grenade"; + else if (IsFemale(self)) + message = "tripped on her own grenade"; + else + message = "tripped on his own grenade"; + break; + case MOD_R_SPLASH: + if (IsNeutral(self)) + message = "blew itself up"; + else if (IsFemale(self)) + message = "blew herself up"; + else + message = "blew himself up"; + break; + case MOD_BFG_BLAST: + message = "should have used a smaller gun"; + break; + default: + if (IsNeutral(self)) + message = "killed itself"; + else if (IsFemale(self)) + message = "killed herself"; + else + message = "killed himself"; + break; } } + if (message && !special) + { + sprintf(death_msg, "%s %s\n", self->client->pers.netname, message); + PrintDeathMessage(death_msg, self); + if (deathmatch->value) + Subtract_Frag( self );//self->client->resp.score--; + self->enemy = NULL; + return; + } + else if ( special ) // handle falling with an attacker set + { + if (self->client->attacker && + self->client->attacker->client && + (self->client->attacker->client != + self->client)) + { + sprintf(death_msg, "%s was taught how to fly by %s\n", + self->client->pers.netname, self->client->attacker->client->pers.netname ); + PrintDeathMessage(death_msg, self); + +//MODIFIED FOR FF -FB + if (!((int)dmflags->value & DF_NO_FRIENDLY_FIRE) && + OnSameTeam(self, self->client->attacker) && + teamplay->value) + { + if (!teamplay->value || team_round_going) + { + Add_TeamKill(self->client->attacker); + Subtract_Frag( self->client->attacker );//attacker->client->resp.score--; + } + } + else + { + if (!teamplay->value || mod != MOD_TELEFRAG) + Add_Frag(self->client->attacker );//attacker->client->resp.score++; + } +//END FF ADD + } + else + { + if (IsNeutral(self)) + sprintf(death_msg, "%s plummets to its death\n", self->client->pers.netname); + else if (IsFemale(self)) + sprintf(death_msg, "%s plummets to her death\n", self->client->pers.netname); + else + sprintf(death_msg, "%s plummets to his death\n", self->client->pers.netname); + PrintDeathMessage(death_msg, self); + if (deathmatch->value) + Subtract_Frag( self );//self->client->resp.score--; + self->enemy = NULL; + } + return; + } + +#if 0 + // handle bleeding, not used because bleeding doesn't get set + if ( mod == MOD_BLEEDING ) + { + sprintf(death_msg, "%s bleeds to death\n", self->client->pers.netname); + PrintDeathMessage(death_msg, self); + return; + } +#endif + + self->enemy = attacker; + if (attacker && attacker->client) + { + switch (mod) + { + case MOD_MK23: // zucc + switch (loc) + { + case LOC_HDAM: + if (IsNeutral(self)) + message = " has a hole in its head from"; + else if (IsFemale(self)) + message = " has a hole in her head from"; + else + message = " has a hole in his head from"; + message2 = "'s Mark 23 pistol"; + break; + case LOC_CDAM: + message = " loses a vital chest organ thanks to"; + message2 = "'s Mark 23 pistol"; + break; + case LOC_SDAM: + if (IsNeutral(self)) + message = " loses its lunch to"; + else if (IsFemale(self)) + message = " loses her lunch to"; + else + message = " loses his lunch to"; + message2 = "'s .45 caliber pistol round"; + break; + case LOC_LDAM: + message = " is legless because of"; + message2 = "'s .45 caliber pistol round"; + break; + default: + message = " was shot by"; + message2 = "'s Mark 23 Pistol"; + } + break; + case MOD_MP5: + switch (loc) + { + case LOC_HDAM: + message = "'s brains are on the wall thanks to"; + message2 = "'s 10mm MP5/10 round"; + break; + case LOC_CDAM: + message = " feels some chest pain via"; + message2 = "'s MP5/10 Submachinegun"; + break; + case LOC_SDAM: + message = " needs some Pepto Bismol after"; + message2 = "'s 10mm MP5 round"; + break; + case LOC_LDAM: + if (IsNeutral(self)) + message = " had its legs blown off thanks to"; + else if (IsFemale(self)) + message = " had her legs blown off thanks to"; + else + message = " had his legs blown off thanks to"; + message2 = "'s MP5/10 Submachinegun"; + break; + default: + message = " was shot by"; + message2 = "'s MP5/10 Submachinegun"; + } + break; + case MOD_M4: + switch (loc) + { + case LOC_HDAM: + message = " had a makeover by"; + message2 = "'s M4 Assault Rifle"; + break; + case LOC_CDAM: + message = " feels some heart burn thanks to"; + message2 = "'s M4 Assault Rifle"; + break; + case LOC_SDAM: + message = " has an upset stomach thanks to"; + message2 = "'s M4 Assault Rifle"; + break; + case LOC_LDAM: + message = " is now shorter thanks to"; + message2 = "'s M4 Assault Rifle"; + break; + default: + message = " was shot by"; + message2 = "'s M4 Assault Rifle"; + } + break; + case MOD_M3: + n = rand() % 2 + 1; + if (n == 1) + { + message = " accepts"; + message2 = "'s M3 Super 90 Assault Shotgun in hole-y matrimony"; + } + else + { + message = " is full of buckshot from"; + message2 = "'s M3 Super 90 Assault Shotgun"; + } + break; + case MOD_HC: + n = rand() % 2 + 1; + if (n == 1) + { + message = " ate"; + message2 = "'s sawed-off 12 gauge"; + } + else + { + message = " is full of buckshot from"; + message2 = "'s sawed off shotgun"; + } + break; + case MOD_SNIPER: + switch (loc) + { + case LOC_HDAM: + if (self->client->ps.fov < 90) + { + if (IsNeutral(self)) + message = " saw the sniper bullet go through its scope thanks to"; + else if (IsFemale(self)) + message = " saw the sniper bullet go through her scope thanks to"; + else + message = " saw the sniper bullet go through his scope thanks to"; + } + else + { + message = " caught a sniper bullet between the eyes from"; + } + break; + case LOC_CDAM: + message = " was picked off by"; + break; + case LOC_SDAM: + message = " was sniped in the stomach by"; + break; + case LOC_LDAM: + message = " was shot in the legs by"; + break; + default: + message = "was sniped by"; + //message2 = "'s Sniper Rifle"; + } + break; + case MOD_DUAL: + switch (loc) + { + case LOC_HDAM: + message = " was trepanned by"; + message2 = "'s akimbo Mark 23 pistols"; + break; + case LOC_CDAM: + message = " was John Woo'd by"; + //message2 = "'s .45 caliber pistol round"; + break; + case LOC_SDAM: + message = " needs some new kidneys thanks to"; + message2 = "'s akimbo Mark 23 pistols"; + break; + case LOC_LDAM: + message = " was shot in the legs by"; + message2 = "'s akimbo Mark 23 pistols"; + break; + default: + message = " was shot by"; + message2 = "'s pair of Mark 23 Pistols"; + } + break; + case MOD_KNIFE: + switch (loc) + { + case LOC_HDAM: + if (IsNeutral(self)) + message = " had its throat slit by"; + else if (IsFemale(self)) + message = " had her throat slit by"; + else + message = " had his throat slit by"; + break; + case LOC_CDAM: + message = " had open heart surgery, compliments of"; + break; + case LOC_SDAM: + message = " was gutted by"; + break; + case LOC_LDAM: + message = " was stabbed repeatedly in the legs by"; + break; + default: + message = " was slashed apart by"; + message2 = "'s Combat Knife"; + } + break; + case MOD_KNIFE_THROWN: + switch (loc) + { + case LOC_HDAM: + message = " caught"; + if (IsNeutral(self)) + message2 = "'s flying knife with its forehead"; + else if (IsFemale(self)) + message2 = "'s flying knife with her forehead"; + else + message2 = "'s flying knife with his forehead"; + break; + case LOC_CDAM: + message = "'s ribs don't help against"; + message2 = "'s flying knife"; + break; + case LOC_SDAM: + if (IsNeutral(self)) + message = " sees the contents of its own stomach thanks to"; + else if (IsFemale(self)) + message = " sees the contents of her own stomach thanks to"; + else + message = " sees the contents of his own stomach thanks to"; + message2 = "'s flying knife"; + break; + case LOC_LDAM: + if (IsNeutral(self)) + message = " had its legs cut off thanks to"; + else if (IsFemale(self)) + message = " had her legs cut off thanks to"; + else + message = " had his legs cut off thanks to"; + message2 = "'s flying knife"; + break; + default: + message = " was hit by"; + message2 = "'s flying Combat Knife"; + } + break; + case MOD_GAS: + message = "sucks down some toxic gas thanks to"; + break; + case MOD_KICK: + n = rand() % 3 + 1; + if (n == 1) + { + if (IsNeutral(self)) + message = " got its ass kicked by"; + else if (IsFemale(self)) + message = " got her ass kicked by"; + else + message = " got his ass kicked by"; + } + else if (n == 2) + { + if (IsNeutral(self)) + { + message = " couldn't remove"; message2 = "'s boot from its ass"; + } + else if (IsFemale(self)) + { + message = " couldn't remove"; message2 = "'s boot from her ass"; + } + else + { + message = " couldn't remove"; message2 = "'s boot from his ass"; + } + } + else + { + if (IsNeutral(self)) + { + message = " had a Bruce Lee put on it by"; message2 = ", with a quickness"; + } + else if (IsFemale(self)) + { + message = " had a Bruce Lee put on her by"; message2 = ", with a quickness"; + } + else + { + message = " had a Bruce Lee put on him by"; message2 = ", with a quickness"; + } + } + break; + case MOD_BLASTER: + message = "was blasted by"; + break; + case MOD_SHOTGUN: + message = "was gunned down by"; + break; + case MOD_SSHOTGUN: + message = "was blown away by"; + message2 = "'s super shotgun"; + break; + case MOD_MACHINEGUN: + message = "was machinegunned by"; + break; + case MOD_CHAINGUN: + message = "was cut in half by"; + message2 = "'s chaingun"; + break; + case MOD_GRENADE: + message = "was popped by"; + message2 = "'s grenade"; + break; + case MOD_G_SPLASH: + message = "was shredded by"; + message2 = "'s shrapnel"; + break; + case MOD_ROCKET: + message = "ate"; + message2 = "'s rocket"; + break; + case MOD_R_SPLASH: + message = "almost dodged"; + message2 = "'s rocket"; + break; + case MOD_HYPERBLASTER: + message = "was melted by"; + message2 = "'s hyperblaster"; + break; + case MOD_RAILGUN: + message = "was railed by"; + break; + case MOD_BFG_LASER: + message = "saw the pretty lights from"; + message2 = "'s BFG"; + break; + case MOD_BFG_BLAST: + message = "was disintegrated by"; + message2 = "'s BFG blast"; + break; + case MOD_BFG_EFFECT: + message = "couldn't hide from"; + message2 = "'s BFG"; + break; + case MOD_HANDGRENADE: + message = " caught"; + message2 = "'s handgrenade"; + break; + case MOD_HG_SPLASH: + message = " didn't see"; + message2 = "'s handgrenade"; + break; + case MOD_HELD_GRENADE: + message = " feels"; + message2 = "'s pain"; + break; + case MOD_TELEFRAG: + message = " tried to invade"; + message2 = "'s personal space"; + break; + } + + if (message) + { +//FIREBLADE + sprintf(death_msg, "%s%s %s%s\n", self->client->pers.netname, message, + attacker->client->pers.netname, message2); + PrintDeathMessage(death_msg, self); +//FIREBLADE + if (deathmatch->value) + { + if (ff) + { + if (!teamplay->value || team_round_going) + { + Add_TeamKill(attacker); + Subtract_Frag( attacker );//attacker->client->resp.score--; + } + } + else + { +//FIREBLADE + if (!teamplay->value || mod != MOD_TELEFRAG) +//FIREBLADE + Add_Frag(attacker );//attacker->client->resp.score++; + } + } + return; + } + } } - - //FIREBLADE + +//FIREBLADE sprintf(death_msg, "%s died\n", self->client->pers.netname); PrintDeathMessage(death_msg, self); - //FIREBLADE +//FIREBLADE if (deathmatch->value) - Subtract_Frag( self );//self->client->resp.score--; + Subtract_Frag( self );//self->client->resp.score--; } void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); @@ -850,14 +1027,14 @@ void TossItemsOnDeath( edict_t *ent ) gitem_t *item; - // don't bother dropping stuff when allweapons/items is active + // don't bother dropping stuff when allweapons/items is active if (allitem->value && allweapon->value) - { - // remove the lasersight because then the observer might have it - item = FindItem(LASER_NAME); - ent->client->pers.inventory[ITEM_INDEX(item)] = 0; - return; - } + { + // remove the lasersight because then the observer might have it + item = FindItem(LASER_NAME); + ent->client->pers.inventory[ITEM_INDEX(item)] = 0; + return; + } // don't drop weapons if allweapons is on @@ -919,7 +1096,7 @@ void TossItemsOnDeath( edict_t *ent ) } // special items -/* +#if 0 item = FindItem(SIL_NAME); if ( ent->client->pers.inventory[ITEM_INDEX(item)] ) EjectItem( ent, item ); @@ -935,7 +1112,7 @@ void TossItemsOnDeath( edict_t *ent ) item = FindItem(LASER_NAME); if ( ent->client->pers.inventory[ITEM_INDEX(item)] ) EjectItem( ent, item ); -*/ +#endif } @@ -1066,6 +1243,9 @@ void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag self->s.sound = 0; self->client->weapon_sound = 0; + self->client->reload_attempts = 0; // stop them from trying to reload + self->client->weapon_attempts = 0; + self->maxs[2] = -8; self->svflags |= SVF_DEADMONSTER; @@ -1112,11 +1292,11 @@ void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag // zucc - check if they have a primed grenade if ( self->client->curr_weap == GRENADE_NUM && ( - ( self->client->ps.gunframe >= GRENADE_IDLE_FIRST + ( self->client->ps.gunframe >= GRENADE_IDLE_FIRST && self->client->ps.gunframe <= GRENADE_IDLE_LAST ) - || ( self->client->ps.gunframe >= GRENADE_THROW_FIRST - && self->client->ps.gunframe <= GRENADE_THROW_LAST ) - ) ) + || ( self->client->ps.gunframe >= GRENADE_THROW_FIRST + && self->client->ps.gunframe <= GRENADE_THROW_LAST ) + ) ) { self->client->ps.gunframe = 0; fire_grenade2 (self, self->s.origin, tv(0,0,0), GRENADE_DAMRAD, 0, 2, GRENADE_DAMRAD*2, false); @@ -1161,10 +1341,10 @@ void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag self->client->anim_end = FRAME_death308; break; } - if ((meansOfDeath == MOD_SNIPER) || (meansOfDeath == MOD_KNIFE) || (meansOfDeath == MOD_KNIFE_THROWN)) - gi.sound(self, CHAN_VOICE, gi.soundindex("misc/glurp.wav"), 1, ATTN_NORM, 0); - else - gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); + if ((meansOfDeath == MOD_SNIPER) || (meansOfDeath == MOD_KNIFE) || (meansOfDeath == MOD_KNIFE_THROWN)) + gi.sound(self, CHAN_VOICE, gi.soundindex("misc/glurp.wav"), 1, ATTN_NORM, 0); + else + gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); } } // zucc this will fix a jump kick death generating a weapon @@ -1187,7 +1367,13 @@ but is called after each death and level change in deathmatch void InitClientPersistant (gclient_t *client) { gitem_t *item; +/* + client_persistant_t oldpers; +//FB 6/3/99 + memcpy(oldpers, pers, sizeof(client->pers)); +//FB 6/3/99 +*/ memset (&client->pers, 0, sizeof(client->pers)); // changed to mk23 item = FindItem(MK23_NAME); @@ -1583,7 +1769,16 @@ void CopyToBodyQue (edict_t *ent) //body->solid = ent->solid; body->clipmask = ent->clipmask; body->owner = ent->owner; - body->movetype = ent->movetype; +//FB 5/31/99 + body->movetype = MOVETYPE_TOSS; // just in case? +// body->movetype = ent->movetype; + VectorCopy (ent->velocity, body->velocity); + body->mass = ent->mass; + body->groundentity = NULL; +//FB 5/31/99 +//FB 6/1/99 + body->s.renderfx = 0; +//FB body->die = body_die; body->takedamage = DAMAGE_YES; @@ -1878,6 +2073,12 @@ void PutClientInServer (edict_t *ent) // zucc for ammo // gitem_t *item; +//FF + int save_team_wounds; + int save_team_kills; + char save_ipaddr[100]; +//FF + // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client @@ -1921,8 +2122,24 @@ void PutClientInServer (edict_t *ent) // clear everything but the persistant data saved = client->pers; +//FF + save_team_wounds = client->team_wounds; + save_team_kills = client->team_kills; + + if (client->ipaddr) + strncpy(save_ipaddr, client->ipaddr, sizeof(save_ipaddr)-1); +//FF + memset (client, 0, sizeof(*client)); client->pers = saved; +//FF + client->team_wounds = save_team_wounds; + client->team_kills = save_team_kills; + + if (save_ipaddr && client->ipaddr) + strncpy(client->ipaddr, save_ipaddr, sizeof(client->ipaddr)); +//FF + if (client->pers.health <= 0) InitClientPersistant(client); client->resp = resp; @@ -2053,6 +2270,7 @@ void PutClientInServer (edict_t *ent) ent->lasersight = NULL; //other + client->resp.sniper_mode = SNIPER_1X; client->bandaging = 0; client->leg_damage = 0; client->leg_noise = 0; @@ -2065,6 +2283,8 @@ void PutClientInServer (edict_t *ent) client->knife_sound = 0; client->doortoggle = 0; client->have_laser = 0; + client->reload_attempts = 0; + client->weapon_attempts = 0; //FIREBLADE if (!going_observer) @@ -2121,7 +2341,7 @@ void ClientBeginDeathmatch (edict_t *ent) // locate ent at a spawn point PutClientInServer (ent); - + // FROM 3.20 -FB if (level.intermissiontime) { @@ -2130,11 +2350,14 @@ void ClientBeginDeathmatch (edict_t *ent) else { // ^^^ - // send effect - gi.WriteByte (svc_muzzleflash); - gi.WriteShort (ent-g_edicts); - gi.WriteByte (MZ_LOGIN); - gi.multicast (ent->s.origin, MULTICAST_PVS); + if (!teamplay->value) + { //FB 5/31/99 + // send effect + gi.WriteByte (svc_muzzleflash); + gi.WriteShort (ent-g_edicts); + gi.WriteByte (MZ_LOGIN); + gi.multicast (ent->s.origin, MULTICAST_PVS); + } } gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); @@ -2167,15 +2390,15 @@ void ClientBegin (edict_t *ent) ent->client = game.clients + (ent - g_edicts - 1); - // clear modes of weapons - /*ent->client->pers.mk23_mode = 0; - ent->client->pers.mp5_mode = 0; - ent->client->pers.m4_mode = 0; - ent->client->pers.sniper_mode = 0; - ent->client->pers.knife_mode = 0; - ent->client->pers.grenade_mode = 0; -*/ + /* + ent->client->resp.mk23_mode = 0; + ent->client->resp.mp5_mode = 0; + ent->client->resp.m4_mode = 0; + ent->client->resp.sniper_mode = 0; + ent->client->resp.knife_mode = 0; + ent->client->resp.grenade_mode = 0; + */ if (deathmatch->value) { @@ -2207,7 +2430,7 @@ void ClientBegin (edict_t *ent) if (level.intermissiontime) { - MoveClientToIntermission (ent); + MoveClientToIntermission (ent); } else { @@ -2215,14 +2438,14 @@ void ClientBegin (edict_t *ent) if (game.maxclients > 1) { //FIREBLADE - if (!teamplay->value) - { + if (!teamplay->value) + { //FIREBLADE - gi.WriteByte (svc_muzzleflash); - gi.WriteShort (ent-g_edicts); - gi.WriteByte (MZ_LOGIN); - gi.multicast (ent->s.origin, MULTICAST_PVS); - } + gi.WriteByte (svc_muzzleflash); + gi.WriteShort (ent-g_edicts); + gi.WriteByte (MZ_LOGIN); + gi.multicast (ent->s.origin, MULTICAST_PVS); + } gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); } @@ -2292,16 +2515,16 @@ void ClientUserinfoChanged (edict_t *ent, char *userinfo) ent->client->ps.fov = 160; } */ - ent->client->pers.firing_style = ACTION_FIRING_CENTER; + ent->client->pers.firing_style = ACTION_FIRING_CENTER; // handedness s = Info_ValueForKey (userinfo, "hand"); if (strlen(s)) { ent->client->pers.hand = atoi(s); - if (strstr(s, "classic high") != NULL) - ent->client->pers.firing_style = ACTION_FIRING_CLASSIC_HIGH; - else if (strstr(s, "classic") != NULL) - ent->client->pers.firing_style = ACTION_FIRING_CLASSIC; + if (strstr(s, "classic high") != NULL) + ent->client->pers.firing_style = ACTION_FIRING_CLASSIC_HIGH; + else if (strstr(s, "classic") != NULL) + ent->client->pers.firing_style = ACTION_FIRING_CLASSIC; } // save off the userinfo in case we want to check something later @@ -2328,12 +2551,15 @@ loadgames will. qboolean ClientConnect (edict_t *ent, char *userinfo) { char *value, *ipaddr; - char ipaddr_buf[1000]; + char ipaddr_buf[100]; // check to see if they are on the banned IP list ipaddr = Info_ValueForKey (userinfo, "ip"); //FIREBLADE - strcpy(ipaddr_buf, ipaddr); + if (strlen(ipaddr) > sizeof(ipaddr_buf)-1) + gi.dprintf("ipaddr_buf length exceeded\n"); + strncpy(ipaddr_buf, ipaddr, 99); + ipaddr_buf[99] = 0; //FIREBLADE // FROM 3.20 -FB @@ -2354,6 +2580,15 @@ qboolean ClientConnect (edict_t *ent, char *userinfo) // they can connect ent->client = game.clients + (ent - g_edicts - 1); +//AZEROV + ent->client->team_kills = 0; +//AZEROV + +//EEK + ent->client->team_wounds = 0; + ent->client->team_wounds_before = 0; +//EEK + //FIREBLADE // We're not going to attempt to support reconnection... if (ent->inuse == true) @@ -2377,6 +2612,9 @@ qboolean ClientConnect (edict_t *ent, char *userinfo) if (game.maxclients > 1) gi.dprintf ("%s@%s connected\n",ent->client->pers.netname, ipaddr_buf); +//EEK + strncpy(ent->client->ipaddr, ipaddr_buf, sizeof(ent->client->ipaddr)); +//EEK // FROM 3.20 -FB ent->svflags = 0; @@ -2428,11 +2666,14 @@ void ClientDisconnect (edict_t *ent) etemp->client->attacker = NULL; } } - // send effect - gi.WriteByte (svc_muzzleflash); - gi.WriteShort (ent-g_edicts); - gi.WriteByte (MZ_LOGOUT); - gi.multicast (ent->s.origin, MULTICAST_PVS); + if (!teamplay->value) + { //FB 5/31/99 + // send effect + gi.WriteByte (svc_muzzleflash); + gi.WriteShort (ent-g_edicts); + gi.WriteByte (MZ_LOGOUT); + gi.multicast (ent->s.origin, MULTICAST_PVS); + } gi.unlinkentity (ent); ent->s.modelindex = 0; @@ -2568,6 +2809,16 @@ void ClientThink (edict_t *ent, usercmd_t *ucmd) // perform a pmove gi.Pmove (&pm); +//FB 6/3/99 - info from Mikael Lindh from AQ:G + if (pm.maxs[2] == 4) + { + ent->maxs[2] = CROUCHING_MAXS2; + pm.maxs[2] = CROUCHING_MAXS2; + ent->viewheight = CROUCHING_VIEWHEIGHT; + pm.viewheight = (float)ent->viewheight; + } +//FB 6/3/99 + // save results of pmove client->ps.pmove = pm.s; client->old_pmove = pm.s; @@ -2728,7 +2979,14 @@ void ClientThink (edict_t *ent, usercmd_t *ucmd) ent->light_level = ucmd->lightlevel; // fire weapon from final position if needed - if (client->latched_buttons & BUTTON_ATTACK) + if ((client->latched_buttons & BUTTON_ATTACK) + //Black Cross - Begin + || (((limchasecam->value && !client->chase_mode) || + (limchasecam->value == 2 && client->chase_mode == 1)) && + team_round_going && (client->resp.team != NOTEAM) && + !(limchasecam->value == 2 && client->chase_mode == 2)) + //Black Cross - End + ) { if (ent->solid == SOLID_NOT && ent->deadflag != DEAD_DEAD) { @@ -2869,6 +3127,7 @@ void ClientBeginServerFrame (edict_t *ent) ent->client->chase_mode = 0; ent->client->chase_target = NULL; ent->client->desired_fov = 90; + ent->client->ps.fov = 90; // FB 5/31/99 added ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; ent->solid = SOLID_BBOX; gi.linkentity(ent); diff --git a/p_hud.c b/p_hud.c index d970e37..47b30dd 100644 --- a/p_hud.c +++ b/p_hud.c @@ -45,10 +45,10 @@ void MoveClientToIntermission (edict_t *ent) ent->solid = SOLID_NOT; //FIREBLADE - ent->client->resp.sniper_mode = SNIPER_1X; - ent->client->desired_fov = 90; - ent->client->ps.fov = 90; - ent->client->ps.stats[STAT_SNIPER_ICON] = 0; + ent->client->resp.sniper_mode = SNIPER_1X; + ent->client->desired_fov = 90; + ent->client->ps.fov = 90; + ent->client->ps.stats[STAT_SNIPER_ICON] = 0; //FIREBLADE // add the layout @@ -148,6 +148,23 @@ void BeginIntermission (edict_t *targ) continue; MoveClientToIntermission (client); } + +// AZEROV: Clear the team kills for everyone + //gi.cprintf(NULL,PRINT_MEDIUM,"Resetting all team kills\n"); + for (i=1; i<=maxclients->value; i++) + { + edict_t *temp_ent; + temp_ent = g_edicts + i; + + if (!temp_ent->inuse || !temp_ent->client) + { + continue; + } + + temp_ent->client->team_wounds = 0; + temp_ent->client->team_kills = 0; + } +// AZEROV } void A_ScoreboardMessage (edict_t *ent, edict_t *killer); diff --git a/p_view.c b/p_view.c index 773541a..3b6c1ea 100644 --- a/p_view.c +++ b/p_view.c @@ -1,4 +1,3 @@ - #include "g_local.h" #include "m_player.h" @@ -22,22 +21,22 @@ SV_CalcRoll */ float SV_CalcRoll (vec3_t angles, vec3_t velocity) { - float sign; - float side; - float value; + float sign; + float side; + float value; - side = DotProduct (velocity, right); - sign = side < 0 ? -1 : 1; - side = fabs(side); + side = DotProduct (velocity, right); + sign = side < 0 ? -1 : 1; + side = fabs(side); - value = sv_rollangle->value; + value = sv_rollangle->value; - if (side < sv_rollspeed->value) - side = side * value / sv_rollspeed->value; - else - side = value; + if (side < sv_rollspeed->value) + side = side * value / sv_rollspeed->value; + else + side = value; - return side*sign; + return side*sign; } @@ -51,134 +50,134 @@ Handles color blends and view kicks */ void P_DamageFeedback (edict_t *player) { - gclient_t *client; - float side; - float realcount, count, kick; - vec3_t v; - int r, l; - static vec3_t power_color = {0.0, 1.0, 0.0}; - static vec3_t acolor = {1.0, 1.0, 1.0}; - static vec3_t bcolor = {1.0, 0.0, 0.0}; + gclient_t *client; + float side; + float realcount, count, kick; + vec3_t v; + int r, l; + static vec3_t power_color = {0.0, 1.0, 0.0}; + static vec3_t acolor = {1.0, 1.0, 1.0}; + static vec3_t bcolor = {1.0, 0.0, 0.0}; - client = player->client; + client = player->client; - // flash the backgrounds behind the status numbers - client->ps.stats[STAT_FLASHES] = 0; - if (client->damage_blood) - client->ps.stats[STAT_FLASHES] |= 1; - if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) - client->ps.stats[STAT_FLASHES] |= 2; + // flash the backgrounds behind the status numbers + client->ps.stats[STAT_FLASHES] = 0; + if (client->damage_blood) + client->ps.stats[STAT_FLASHES] |= 1; + if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) + client->ps.stats[STAT_FLASHES] |= 2; - // total points of damage shot at the player this frame - count = (client->damage_blood + client->damage_armor + client->damage_parmor); - if (count == 0) - return; // didn't take any damage + // total points of damage shot at the player this frame + count = (client->damage_blood + client->damage_armor + client->damage_parmor); + if (count == 0) + return; // didn't take any damage - // start a pain animation if still in the player model - if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255) - { - static int i; + // start a pain animation if still in the player model + if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255) + { + static int i; - client->anim_priority = ANIM_PAIN; - if (client->ps.pmove.pm_flags & PMF_DUCKED) - { - player->s.frame = FRAME_crpain1-1; - client->anim_end = FRAME_crpain4; - } - else - { - i = (i+1)%3; - switch (i) - { - case 0: - player->s.frame = FRAME_pain101-1; - client->anim_end = FRAME_pain104; - break; - case 1: - player->s.frame = FRAME_pain201-1; - client->anim_end = FRAME_pain204; - break; - case 2: - player->s.frame = FRAME_pain301-1; - client->anim_end = FRAME_pain304; - break; - } - } - } + client->anim_priority = ANIM_PAIN; + if (client->ps.pmove.pm_flags & PMF_DUCKED) + { + player->s.frame = FRAME_crpain1-1; + client->anim_end = FRAME_crpain4; + } + else + { + i = (i+1)%3; + switch (i) + { + case 0: + player->s.frame = FRAME_pain101-1; + client->anim_end = FRAME_pain104; + break; + case 1: + player->s.frame = FRAME_pain201-1; + client->anim_end = FRAME_pain204; + break; + case 2: + player->s.frame = FRAME_pain301-1; + client->anim_end = FRAME_pain304; + break; + } + } + } - realcount = count; - if (count < 10) - count = 10; // always make a visible effect + realcount = count; + if (count < 10) + count = 10; // always make a visible effect - // play an apropriate pain sound - if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) - { - r = 1 + (rand()&1); - player->pain_debounce_time = level.time + 0.7; - if (player->health < 25) - l = 25; - else if (player->health < 50) - l = 50; - else if (player->health < 75) - l = 75; - else - l = 100; - gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); - } + // play an apropriate pain sound + if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) + { + r = 1 + (rand()&1); + player->pain_debounce_time = level.time + 0.7; + if (player->health < 25) + l = 25; + else if (player->health < 50) + l = 50; + else if (player->health < 75) + l = 75; + else + l = 100; + gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); + } - // the total alpha of the blend is always proportional to count - if (client->damage_alpha < 0) - client->damage_alpha = 0; - client->damage_alpha += count*0.01; - if (client->damage_alpha < 0.2) - client->damage_alpha = 0.2; - if (client->damage_alpha > 0.6) - client->damage_alpha = 0.6; // don't go too saturated + // the total alpha of the blend is always proportional to count + if (client->damage_alpha < 0) + client->damage_alpha = 0; + client->damage_alpha += count*0.01; + if (client->damage_alpha < 0.2) + client->damage_alpha = 0.2; + if (client->damage_alpha > 0.6) + client->damage_alpha = 0.6; // don't go too saturated - // the color of the blend will vary based on how much was absorbed - // by different armors - VectorClear (v); - if (client->damage_parmor) - VectorMA (v, (float)client->damage_parmor/realcount, power_color, v); - if (client->damage_armor) - VectorMA (v, (float)client->damage_armor/realcount, acolor, v); - if (client->damage_blood) - VectorMA (v, (float)client->damage_blood/realcount, bcolor, v); - VectorCopy (v, client->damage_blend); + // the color of the blend will vary based on how much was absorbed + // by different armors + VectorClear (v); + if (client->damage_parmor) + VectorMA (v, (float)client->damage_parmor/realcount, power_color, v); + if (client->damage_armor) + VectorMA (v, (float)client->damage_armor/realcount, acolor, v); + if (client->damage_blood) + VectorMA (v, (float)client->damage_blood/realcount, bcolor, v); + VectorCopy (v, client->damage_blend); - // - // calculate view angle kicks - // - kick = abs(client->damage_knockback); - if (kick && player->health > 0) // kick of 0 means no view adjust at all - { - kick = kick * 100 / player->health; + // + // calculate view angle kicks + // + kick = abs(client->damage_knockback); + if (kick && player->health > 0) // kick of 0 means no view adjust at all + { + kick = kick * 100 / player->health; - if (kick < count*0.5) - kick = count*0.5; - if (kick > 50) - kick = 50; + if (kick < count*0.5) + kick = count*0.5; + if (kick > 50) + kick = 50; - VectorSubtract (client->damage_from, player->s.origin, v); - VectorNormalize (v); + VectorSubtract (client->damage_from, player->s.origin, v); + VectorNormalize (v); - side = DotProduct (v, right); - client->v_dmg_roll = kick*side*0.3; + side = DotProduct (v, right); + client->v_dmg_roll = kick*side*0.3; - side = -DotProduct (v, forward); - client->v_dmg_pitch = kick*side*0.3; + side = -DotProduct (v, forward); + client->v_dmg_pitch = kick*side*0.3; - client->v_dmg_time = level.time + DAMAGE_TIME; - } + client->v_dmg_time = level.time + DAMAGE_TIME; + } - // - // clear totals - // - client->damage_blood = 0; - client->damage_armor = 0; - client->damage_parmor = 0; - client->damage_knockback = 0; + // + // clear totals + // + client->damage_blood = 0; + client->damage_armor = 0; + client->damage_parmor = 0; + client->damage_knockback = 0; } @@ -202,120 +201,120 @@ Auto pitching on slopes? */ void SV_CalcViewOffset (edict_t *ent) { - float *angles; - float bob; - float ratio; - float delta; - vec3_t v; + float *angles; + float bob; + float ratio; + float delta; + vec3_t v; -//=================================== + //=================================== - // base angles - angles = ent->client->ps.kick_angles; + // base angles + angles = ent->client->ps.kick_angles; - // if dead, fix the angle and don't add any kick - if (ent->deadflag) - { - VectorClear (angles); + // if dead, fix the angle and don't add any kick + if (ent->deadflag) + { + VectorClear (angles); - ent->client->ps.viewangles[ROLL] = 40; - ent->client->ps.viewangles[PITCH] = -15; - ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; - } - else - { - // add angles based on weapon kick + ent->client->ps.viewangles[ROLL] = 40; + ent->client->ps.viewangles[PITCH] = -15; + ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; + } + else + { + // add angles based on weapon kick - VectorCopy (ent->client->kick_angles, angles); + VectorCopy (ent->client->kick_angles, angles); - // add angles based on damage kick + // add angles based on damage kick - ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME; - if (ratio < 0) - { - ratio = 0; - ent->client->v_dmg_pitch = 0; - ent->client->v_dmg_roll = 0; - } - angles[PITCH] += ratio * ent->client->v_dmg_pitch; - angles[ROLL] += ratio * ent->client->v_dmg_roll; + ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME; + if (ratio < 0) + { + ratio = 0; + ent->client->v_dmg_pitch = 0; + ent->client->v_dmg_roll = 0; + } + angles[PITCH] += ratio * ent->client->v_dmg_pitch; + angles[ROLL] += ratio * ent->client->v_dmg_roll; - // add pitch based on fall kick + // add pitch based on fall kick - ratio = (ent->client->fall_time - level.time) / FALL_TIME; - if (ratio < 0) - ratio = 0; - angles[PITCH] += ratio * ent->client->fall_value; + ratio = (ent->client->fall_time - level.time) / FALL_TIME; + if (ratio < 0) + ratio = 0; + angles[PITCH] += ratio * ent->client->fall_value; - // add angles based on velocity + // add angles based on velocity - delta = DotProduct (ent->velocity, forward); - angles[PITCH] += delta*run_pitch->value; + delta = DotProduct (ent->velocity, forward); + angles[PITCH] += delta*run_pitch->value; - delta = DotProduct (ent->velocity, right); - angles[ROLL] += delta*run_roll->value; + delta = DotProduct (ent->velocity, right); + angles[ROLL] += delta*run_roll->value; - // add angles based on bob + // add angles based on bob - delta = bobfracsin * bob_pitch->value * xyspeed; - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - delta *= 6; // crouching - angles[PITCH] += delta; - delta = bobfracsin * bob_roll->value * xyspeed; - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - delta *= 6; // crouching - if (bobcycle & 1) - delta = -delta; - angles[ROLL] += delta; - } + delta = bobfracsin * bob_pitch->value * xyspeed; + if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) + delta *= 6; // crouching + angles[PITCH] += delta; + delta = bobfracsin * bob_roll->value * xyspeed; + if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) + delta *= 6; // crouching + if (bobcycle & 1) + delta = -delta; + angles[ROLL] += delta; + } -//=================================== + //=================================== - // base origin + // base origin - VectorClear (v); + VectorClear (v); - // add view height + // add view height - v[2] += ent->viewheight; + v[2] += ent->viewheight; - // add fall height + // add fall height - ratio = (ent->client->fall_time - level.time) / FALL_TIME; - if (ratio < 0) - ratio = 0; - v[2] -= ratio * ent->client->fall_value * 0.4; + ratio = (ent->client->fall_time - level.time) / FALL_TIME; + if (ratio < 0) + ratio = 0; + v[2] -= ratio * ent->client->fall_value * 0.4; - // add bob height + // add bob height - bob = bobfracsin * xyspeed * bob_up->value; - if (bob > 6) - bob = 6; - //gi.DebugGraph (bob *2, 255); - v[2] += bob; + bob = bobfracsin * xyspeed * bob_up->value; + if (bob > 6) + bob = 6; + //gi.DebugGraph (bob *2, 255); + v[2] += bob; - // add kick offset + // add kick offset - VectorAdd (v, ent->client->kick_origin, v); + VectorAdd (v, ent->client->kick_origin, v); - // absolutely bound offsets - // so the view can never be outside the player box + // absolutely bound offsets + // so the view can never be outside the player box - if (v[0] < -14) - v[0] = -14; - else if (v[0] > 14) - v[0] = 14; - if (v[1] < -14) - v[1] = -14; - else if (v[1] > 14) - v[1] = 14; - if (v[2] < -22) - v[2] = -22; - else if (v[2] > 30) - v[2] = 30; + if (v[0] < -14) + v[0] = -14; + else if (v[0] > 14) + v[0] = 14; + if (v[1] < -14) + v[1] = -14; + else if (v[1] > 14) + v[1] = 14; + if (v[2] < -22) + v[2] = -22; + else if (v[2] > 30) + v[2] = 30; - VectorCopy (v, ent->client->ps.viewoffset); + VectorCopy (v, ent->client->ps.viewoffset); } /* @@ -325,48 +324,48 @@ SV_CalcGunOffset */ void SV_CalcGunOffset (edict_t *ent) { - int i; - float delta; + int i; + float delta; - // gun angles from bobbing - ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; - ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; - if (bobcycle & 1) - { - ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL]; - ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW]; - } + // gun angles from bobbing + ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; + ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; + if (bobcycle & 1) + { + ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL]; + ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW]; + } - ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005; + ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005; - // gun angles from delta movement - for (i=0 ; i<3 ; i++) - { - delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i]; - if (delta > 180) - delta -= 360; - if (delta < -180) - delta += 360; - if (delta > 45) - delta = 45; - if (delta < -45) - delta = -45; - if (i == YAW) - ent->client->ps.gunangles[ROLL] += 0.1*delta; - ent->client->ps.gunangles[i] += 0.2 * delta; - } + // gun angles from delta movement + for (i=0 ; i<3 ; i++) + { + delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i]; + if (delta > 180) + delta -= 360; + if (delta < -180) + delta += 360; + if (delta > 45) + delta = 45; + if (delta < -45) + delta = -45; + if (i == YAW) + ent->client->ps.gunangles[ROLL] += 0.1*delta; + ent->client->ps.gunangles[i] += 0.2 * delta; + } - // gun height - VectorClear (ent->client->ps.gunoffset); -// ent->ps->gunorigin[2] += bob; + // gun height + VectorClear (ent->client->ps.gunoffset); + // ent->ps->gunorigin[2] += bob; - // gun_x / gun_y / gun_z are development tools - for (i=0 ; i<3 ; i++) - { - ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value); - ent->client->ps.gunoffset[i] += right[i]*gun_x->value; - ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value); - } + // gun_x / gun_y / gun_z are development tools + for (i=0 ; i<3 ; i++) + { + ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value); + ent->client->ps.gunoffset[i] += right[i]*gun_x->value; + ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value); + } } @@ -377,17 +376,17 @@ SV_AddBlend */ void SV_AddBlend (float r, float g, float b, float a, float *v_blend) { - float a2, a3; + float a2, a3; - if (a <= 0) - return; - a2 = v_blend[3] + (1-v_blend[3])*a; // new total alpha - a3 = v_blend[3]/a2; // fraction of color from old + if (a <= 0) + return; + a2 = v_blend[3] + (1-v_blend[3])*a; // new total alpha + a3 = v_blend[3]/a2; // fraction of color from old - v_blend[0] = v_blend[0]*a3 + r*(1-a3); - v_blend[1] = v_blend[1]*a3 + g*(1-a3); - v_blend[2] = v_blend[2]*a3 + b*(1-a3); - v_blend[3] = a2; + v_blend[0] = v_blend[0]*a3 + r*(1-a3); + v_blend[1] = v_blend[1]*a3 + g*(1-a3); + v_blend[2] = v_blend[2]*a3 + b*(1-a3); + v_blend[3] = a2; } @@ -398,96 +397,103 @@ SV_CalcBlend */ void SV_CalcBlend (edict_t *ent) { - int contents; - vec3_t vieworg; - int remaining; + int contents; + vec3_t vieworg; + int remaining; - // enable ir vision if appropriate - if ( ir->value ) - { - if ( ent->client->pers.inventory[ITEM_INDEX(FindItem(BAND_NAME))] && ent->client->resp.ir == 0 ) - { - ent->client->ps.rdflags |= RDF_IRGOGGLES; - } - else - { - ent->client->ps.rdflags &= ~RDF_IRGOGGLES; - } + // enable ir vision if appropriate + if ( ir->value ) + { + if ((ent->client->pers.inventory[ITEM_INDEX(FindItem(BAND_NAME))] && + ent->client->resp.ir == 0) || + (ent->client->chase_target != NULL && + ent->client->chase_target->client != NULL && + ent->client->chase_mode == 2 && + ent->client->chase_target->client->resp.ir == 0 && + ent->client->chase_target->client->pers.inventory[ITEM_INDEX(FindItem(BAND_NAME))])) + + { + ent->client->ps.rdflags |= RDF_IRGOGGLES; + } + else + { + ent->client->ps.rdflags &= ~RDF_IRGOGGLES; + } - } + } - ent->client->ps.blend[0] = ent->client->ps.blend[1] = - ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0; + ent->client->ps.blend[0] = ent->client->ps.blend[1] = + ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0; - // add for contents - VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg); - contents = gi.pointcontents (vieworg); - if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) ) - ent->client->ps.rdflags |= RDF_UNDERWATER; - else - ent->client->ps.rdflags &= ~RDF_UNDERWATER; + // add for contents + VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg); + contents = gi.pointcontents (vieworg); + if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) ) + ent->client->ps.rdflags |= RDF_UNDERWATER; + else + ent->client->ps.rdflags &= ~RDF_UNDERWATER; - if (contents & (CONTENTS_SOLID|CONTENTS_LAVA)) - SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend); - else if (contents & CONTENTS_SLIME) - SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend); - else if (contents & CONTENTS_WATER) - SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend); + if (contents & (CONTENTS_SOLID|CONTENTS_LAVA)) + SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend); + else if (contents & CONTENTS_SLIME) + SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend); + else if (contents & CONTENTS_WATER) + SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend); - // add for powerups - if (ent->client->quad_framenum > level.framenum) - { - remaining = ent->client->quad_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend); - } - else if (ent->client->invincible_framenum > level.framenum) - { - remaining = ent->client->invincible_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend); - } - else if (ent->client->enviro_framenum > level.framenum) - { - remaining = ent->client->enviro_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend); - } - else if (ent->client->breather_framenum > level.framenum) - { - remaining = ent->client->breather_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend); - } + // add for powerups + if (ent->client->quad_framenum > level.framenum) + { + remaining = ent->client->quad_framenum - level.framenum; + if (remaining == 30) // beginning to fade + gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0); + if (remaining > 30 || (remaining & 4) ) + SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend); + } + else if (ent->client->invincible_framenum > level.framenum) + { + remaining = ent->client->invincible_framenum - level.framenum; + if (remaining == 30) // beginning to fade + gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0); + if (remaining > 30 || (remaining & 4) ) + SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend); + } + else if (ent->client->enviro_framenum > level.framenum) + { + remaining = ent->client->enviro_framenum - level.framenum; + if (remaining == 30) // beginning to fade + gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); + if (remaining > 30 || (remaining & 4) ) + SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend); + } + else if (ent->client->breather_framenum > level.framenum) + { + remaining = ent->client->breather_framenum - level.framenum; + if (remaining == 30) // beginning to fade + gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); + if (remaining > 30 || (remaining & 4) ) + SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend); + } - // add for damage - if (ent->client->damage_alpha > 0) - SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1] - ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend); + // add for damage + if (ent->client->damage_alpha > 0) + SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1] + ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend); - if (ent->client->bonus_alpha > 0) - SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend); + if (ent->client->bonus_alpha > 0) + SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend); - // drop the damage value - ent->client->damage_alpha -= 0.06; - if (ent->client->damage_alpha < 0) - ent->client->damage_alpha = 0; + // drop the damage value + ent->client->damage_alpha -= 0.06; + if (ent->client->damage_alpha < 0) + ent->client->damage_alpha = 0; - // drop the bonus value - ent->client->bonus_alpha -= 0.1; - if (ent->client->bonus_alpha < 0) - ent->client->bonus_alpha = 0; + // drop the bonus value + ent->client->bonus_alpha -= 0.1; + if (ent->client->bonus_alpha < 0) + ent->client->bonus_alpha = 0; } @@ -498,94 +504,94 @@ P_FallingDamage */ void P_FallingDamage (edict_t *ent) { - float delta; - int damage; - vec3_t dir; - gitem_t* item; + float delta; + int damage; + vec3_t dir; + gitem_t* item; - if (teamplay->value) - { - if (lights_camera_action) - { -// ent->s.event = EV_FOOTSTEP; - return; - } - } + if (teamplay->value) + { + if (lights_camera_action) + { + // ent->s.event = EV_FOOTSTEP; + return; + } + } - // zucc look for slippers to avoid noise - item = FindItem(SLIP_NAME); + // zucc look for slippers to avoid noise + item = FindItem(SLIP_NAME); - if (ent->s.modelindex != 255) - return; // not in the player model + if (ent->s.modelindex != 255) + return; // not in the player model - if (ent->movetype == MOVETYPE_NOCLIP) - return; + if (ent->movetype == MOVETYPE_NOCLIP) + return; - if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity)) - { - delta = ent->client->oldvelocity[2]; - } - else - { - if (!ent->groundentity) - return; - delta = ent->velocity[2] - ent->client->oldvelocity[2]; - ent->client->jumping = 0; - } - delta = delta*delta * 0.0001; + if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity)) + { + delta = ent->client->oldvelocity[2]; + } + else + { + if (!ent->groundentity) + return; + delta = ent->velocity[2] - ent->client->oldvelocity[2]; + ent->client->jumping = 0; + } + delta = delta*delta * 0.0001; - // never take falling damage if completely underwater - if (ent->waterlevel == 3) - return; - if (ent->waterlevel == 2) - delta *= 0.25; - if (ent->waterlevel == 1) - delta *= 0.5; + // never take falling damage if completely underwater + if (ent->waterlevel == 3) + return; + if (ent->waterlevel == 2) + delta *= 0.25; + if (ent->waterlevel == 1) + delta *= 0.5; - if (delta < 1) - return; + if (delta < 1) + return; - //zucc add check for slippers - if (delta < 15 && !(ent->client->pers.inventory[ITEM_INDEX(item)]) ) - { - ent->s.event = EV_FOOTSTEP; - return; - } + //zucc add check for slippers + if (delta < 15 && !(ent->client->pers.inventory[ITEM_INDEX(item)]) ) + { + ent->s.event = EV_FOOTSTEP; + return; + } - ent->client->fall_value = delta*0.5; - if (ent->client->fall_value > 40) - ent->client->fall_value = 40; - ent->client->fall_time = level.time + FALL_TIME; + ent->client->fall_value = delta*0.5; + if (ent->client->fall_value > 40) + ent->client->fall_value = 40; + ent->client->fall_time = level.time + FALL_TIME; - if (delta > 30) - { - if (ent->health > 0) - { - if (delta >= 55) - ent->s.event = EV_FALLFAR; - else // all falls are far - ent->s.event = EV_FALLFAR; - } - ent->pain_debounce_time = level.time; // no normal pain sound + if (delta > 30) + { + if (ent->health > 0) + { + if (delta >= 55) + ent->s.event = EV_FALLFAR; + else // all falls are far + ent->s.event = EV_FALLFAR; + } + ent->pain_debounce_time = level.time; // no normal pain sound - damage = (int)(((delta-30)/2)); - if (damage < 1) - damage = 1; - // zucc scale this up - damage *= 10; - VectorSet (dir, 0, 0, 1); + damage = (int)(((delta-30)/2)); + if (damage < 1) + damage = 1; + // zucc scale this up + damage *= 10; + VectorSet (dir, 0, 0, 1); - if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) ) - { - T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); - } - } - //zucc added check for slippers, this is just another noise - else if ( !(ent->client->pers.inventory[ITEM_INDEX(item)]) ) - { - ent->s.event = EV_FALLSHORT; - return; - } + if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) ) + { + T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); + } + } + //zucc added check for slippers, this is just another noise + else if ( !(ent->client->pers.inventory[ITEM_INDEX(item)]) ) + { + ent->s.event = EV_FALLSHORT; + return; + } } @@ -597,162 +603,162 @@ P_WorldEffects */ void P_WorldEffects (void) { - qboolean breather; - qboolean envirosuit; - int waterlevel, old_waterlevel; + qboolean breather; + qboolean envirosuit; + int waterlevel, old_waterlevel; - if (current_player->movetype == MOVETYPE_NOCLIP) - { - current_player->air_finished = level.time + 12; // don't need air - return; - } + if (current_player->movetype == MOVETYPE_NOCLIP) + { + current_player->air_finished = level.time + 12; // don't need air + return; + } - waterlevel = current_player->waterlevel; - old_waterlevel = current_client->old_waterlevel; - current_client->old_waterlevel = waterlevel; + waterlevel = current_player->waterlevel; + old_waterlevel = current_client->old_waterlevel; + current_client->old_waterlevel = waterlevel; - breather = current_client->breather_framenum > level.framenum; - envirosuit = current_client->enviro_framenum > level.framenum; + breather = current_client->breather_framenum > level.framenum; + envirosuit = current_client->enviro_framenum > level.framenum; - // - // if just entered a water volume, play a sound - // - if (!old_waterlevel && waterlevel) - { - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - if (current_player->watertype & CONTENTS_LAVA) - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); - else if (current_player->watertype & CONTENTS_SLIME) - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); - else if (current_player->watertype & CONTENTS_WATER) - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); - current_player->flags |= FL_INWATER; + // + // if just entered a water volume, play a sound + // + if (!old_waterlevel && waterlevel) + { + PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); + if (current_player->watertype & CONTENTS_LAVA) + gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); + else if (current_player->watertype & CONTENTS_SLIME) + gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); + else if (current_player->watertype & CONTENTS_WATER) + gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); + current_player->flags |= FL_INWATER; - // clear damage_debounce, so the pain sound will play immediately - current_player->damage_debounce_time = level.time - 1; - } + // clear damage_debounce, so the pain sound will play immediately + current_player->damage_debounce_time = level.time - 1; + } - // - // if just completely exited a water volume, play a sound - // - if (old_waterlevel && ! waterlevel) - { - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); - current_player->flags &= ~FL_INWATER; - } + // + // if just completely exited a water volume, play a sound + // + if (old_waterlevel && ! waterlevel) + { + PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); + gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); + current_player->flags &= ~FL_INWATER; + } - // - // check for head just going under water - // - if (old_waterlevel != 3 && waterlevel == 3) - { - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0); - } + // + // check for head just going under water + // + if (old_waterlevel != 3 && waterlevel == 3) + { + gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0); + } - // - // check for head just coming out of water - // - if (old_waterlevel == 3 && waterlevel != 3) - { - if (current_player->air_finished < level.time) - { // gasp for air - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - } - else if (current_player->air_finished < level.time + 11) - { // just break surface - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); - } - } + // + // check for head just coming out of water + // + if (old_waterlevel == 3 && waterlevel != 3) + { + if (current_player->air_finished < level.time) + { // gasp for air + gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); + PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); + } + else if (current_player->air_finished < level.time + 11) + { // just break surface + gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); + } + } - // - // check for drowning - // - if (waterlevel == 3) - { - // breather or envirosuit give air - if (breather || envirosuit) - { - current_player->air_finished = level.time + 10; + // + // check for drowning + // + if (waterlevel == 3) + { + // breather or envirosuit give air + if (breather || envirosuit) + { + current_player->air_finished = level.time + 10; - if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) - { - if (!current_client->breather_sound) - gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); - current_client->breather_sound ^= 1; - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); + if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) + { + if (!current_client->breather_sound) + gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); + else + gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); + current_client->breather_sound ^= 1; + PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); //FIXME: release a bubble? - } - } + } + } - // if out of air, start drowning - if (current_player->air_finished < level.time) - { // drown! - if (current_player->client->next_drown_time < level.time - && current_player->health > 0) - { - current_player->client->next_drown_time = level.time + 1; + // if out of air, start drowning + if (current_player->air_finished < level.time) + { // drown! + if (current_player->client->next_drown_time < level.time + && current_player->health > 0) + { + current_player->client->next_drown_time = level.time + 1; // take more damage the longer underwater - current_player->dmg += 2; - if (current_player->dmg > 15) - current_player->dmg = 15; + current_player->dmg += 2; + if (current_player->dmg > 15) + current_player->dmg = 15; // play a gurp sound instead of a normal pain sound - if (current_player->health <= current_player->dmg) - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); - else if (rand()&1) - gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); - - current_player->pain_debounce_time = level.time; - - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); - } - } - } + if (current_player->health <= current_player->dmg) + gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); + else if (rand()&1) + gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); else - { - current_player->air_finished = level.time + 12; - current_player->dmg = 2; - } + gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); - // - // check for sizzle damage - // - if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) - { - if (current_player->watertype & CONTENTS_LAVA) - { - if (current_player->health > 0 - && current_player->pain_debounce_time <= level.time - && current_client->invincible_framenum < level.framenum) - { - if (rand()&1) - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); - current_player->pain_debounce_time = level.time + 1; - } + current_player->pain_debounce_time = level.time; - if (envirosuit) // take 1/3 damage with envirosuit - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA); - else - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA); - } + T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); + } + } + } + else + { + current_player->air_finished = level.time + 12; + current_player->dmg = 2; + } - if (current_player->watertype & CONTENTS_SLIME) - { - if (!envirosuit) - { // no damage from slime with envirosuit - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME); - } - } - } + // + // check for sizzle damage + // + if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) + { + if (current_player->watertype & CONTENTS_LAVA) + { + if (current_player->health > 0 + && current_player->pain_debounce_time <= level.time + && current_client->invincible_framenum < level.framenum) + { + if (rand()&1) + gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); + else + gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); + current_player->pain_debounce_time = level.time + 1; + } + + if (envirosuit) // take 1/3 damage with envirosuit + T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA); + else + T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA); + } + + if (current_player->watertype & CONTENTS_SLIME) + { + if (!envirosuit) + { // no damage from slime with envirosuit + T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME); + } + } + } } @@ -763,50 +769,54 @@ G_SetClientEffects */ void G_SetClientEffects (edict_t *ent) { - int pa_type; - int remaining; + int pa_type; + int remaining; - ent->s.effects = 0; - // zucc added RF_IR_VISIBLE - ent->s.renderfx = RF_IR_VISIBLE; + ent->s.effects = 0; + // zucc added RF_IR_VISIBLE + //FB 6/1/99 - only for live players + if (ent->deadflag != DEAD_DEAD) + ent->s.renderfx = RF_IR_VISIBLE; + else + ent->s.renderfx = 0; - if (ent->health <= 0 || level.intermissiontime) - return; + if (ent->health <= 0 || level.intermissiontime) + return; - if (ent->powerarmor_time > level.time) - { - pa_type = PowerArmorType (ent); - if (pa_type == POWER_ARMOR_SCREEN) - { - ent->s.effects |= EF_POWERSCREEN; - } - else if (pa_type == POWER_ARMOR_SHIELD) - { - ent->s.effects |= EF_COLOR_SHELL; - ent->s.renderfx |= RF_SHELL_GREEN; - } - } + if (ent->powerarmor_time > level.time) + { + pa_type = PowerArmorType (ent); + if (pa_type == POWER_ARMOR_SCREEN) + { + ent->s.effects |= EF_POWERSCREEN; + } + else if (pa_type == POWER_ARMOR_SHIELD) + { + ent->s.effects |= EF_COLOR_SHELL; + ent->s.renderfx |= RF_SHELL_GREEN; + } + } - if (ent->client->quad_framenum > level.framenum) - { - remaining = ent->client->quad_framenum - level.framenum; - if (remaining > 30 || (remaining & 4) ) - ent->s.effects |= EF_QUAD; - } + if (ent->client->quad_framenum > level.framenum) + { + remaining = ent->client->quad_framenum - level.framenum; + if (remaining > 30 || (remaining & 4) ) + ent->s.effects |= EF_QUAD; + } - if (ent->client->invincible_framenum > level.framenum) - { - remaining = ent->client->invincible_framenum - level.framenum; - if (remaining > 30 || (remaining & 4) ) - ent->s.effects |= EF_PENT; - } + if (ent->client->invincible_framenum > level.framenum) + { + remaining = ent->client->invincible_framenum - level.framenum; + if (remaining > 30 || (remaining & 4) ) + ent->s.effects |= EF_PENT; + } - // show cheaters!!! - if (ent->flags & FL_GODMODE) - { - ent->s.effects |= EF_COLOR_SHELL; - ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); - } + // show cheaters!!! + if (ent->flags & FL_GODMODE) + { + ent->s.effects |= EF_COLOR_SHELL; + ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); + } } @@ -817,20 +827,20 @@ G_SetClientEvent */ void G_SetClientEvent (edict_t *ent) { - gitem_t* item; + gitem_t* item; - item = FindItem(SLIP_NAME); + item = FindItem(SLIP_NAME); - if (ent->s.event) - return; + if (ent->s.event) + return; - if ( ent->groundentity && xyspeed > 225) - { - //zucc added item check to see if they have slippers - if ( (int)(current_client->bobtime+bobmove) != bobcycle - && !(ent->client->pers.inventory[ITEM_INDEX(item)]) ) - ent->s.event = EV_FOOTSTEP; - } + if ( ent->groundentity && xyspeed > 225) + { + //zucc added item check to see if they have slippers + if ( (int)(current_client->bobtime+bobmove) != bobcycle + && !(ent->client->pers.inventory[ITEM_INDEX(item)]) ) + ent->s.event = EV_FOOTSTEP; + } } /* @@ -840,37 +850,37 @@ G_SetClientSound */ void G_SetClientSound (edict_t *ent) { - char *weap; + char *weap; - if (ent->client->resp.game_helpchanged != game.helpchanged) - { - ent->client->resp.game_helpchanged = game.helpchanged; - ent->client->resp.helpchanged = 1; - } + if (ent->client->resp.game_helpchanged != game.helpchanged) + { + ent->client->resp.game_helpchanged = game.helpchanged; + ent->client->resp.helpchanged = 1; + } - // help beep (no more than three times) - if (ent->client->resp.helpchanged && ent->client->resp.helpchanged <= 3 && !(level.framenum&63) ) - { - ent->client->resp.helpchanged++; - gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0); - } + // help beep (no more than three times) + if (ent->client->resp.helpchanged && ent->client->resp.helpchanged <= 3 && !(level.framenum&63) ) + { + ent->client->resp.helpchanged++; + gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0); + } - if (ent->client->pers.weapon) - weap = ent->client->pers.weapon->classname; - else - weap = ""; + if (ent->client->pers.weapon) + weap = ent->client->pers.weapon->classname; + else + weap = ""; - if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) - ent->s.sound = snd_fry; - else if (strcmp(weap, "weapon_railgun") == 0) - ent->s.sound = gi.soundindex("weapons/rg_hum.wav"); - else if (strcmp(weap, "weapon_bfg") == 0) - ent->s.sound = gi.soundindex("weapons/bfg_hum.wav"); - else if (ent->client->weapon_sound) - ent->s.sound = ent->client->weapon_sound; - else - ent->s.sound = 0; + if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) + ent->s.sound = snd_fry; + else if (strcmp(weap, "weapon_railgun") == 0) + ent->s.sound = gi.soundindex("weapons/rg_hum.wav"); + else if (strcmp(weap, "weapon_bfg") == 0) + ent->s.sound = gi.soundindex("weapons/bfg_hum.wav"); + else if (ent->client->weapon_sound) + ent->s.sound = ent->client->weapon_sound; + else + ent->s.sound = 0; } /* @@ -880,98 +890,98 @@ G_SetClientFrame */ void G_SetClientFrame (edict_t *ent) { - gclient_t *client; - qboolean duck, run; + gclient_t *client; + qboolean duck, run; - if (ent->s.modelindex != 255) - return; // not in the player model + if (ent->s.modelindex != 255) + return; // not in the player model - client = ent->client; + client = ent->client; - if (client->ps.pmove.pm_flags & PMF_DUCKED) - duck = true; - else - duck = false; - if (xyspeed) - run = true; - else - run = false; + if (client->ps.pmove.pm_flags & PMF_DUCKED) + duck = true; + else + duck = false; + if (xyspeed) + run = true; + else + run = false; - // check for stand/duck and stop/go transitions - if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH) - goto newanim; - if (run != client->anim_run && client->anim_priority == ANIM_BASIC) - goto newanim; - if (!ent->groundentity && client->anim_priority <= ANIM_WAVE) - goto newanim; + // check for stand/duck and stop/go transitions + if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH) + goto newanim; + if (run != client->anim_run && client->anim_priority == ANIM_BASIC) + goto newanim; + if (!ent->groundentity && client->anim_priority <= ANIM_WAVE) + goto newanim; - // zucc vwep - if(client->anim_priority == ANIM_REVERSE) - { - if(ent->s.frame > client->anim_end) - { - ent->s.frame--; - return; - } - } - else if (ent->s.frame < client->anim_end) - { // continue an animation - ent->s.frame++; - return; - } + // zucc vwep + if(client->anim_priority == ANIM_REVERSE) + { + if(ent->s.frame > client->anim_end) + { + ent->s.frame--; + return; + } + } + else if (ent->s.frame < client->anim_end) + { // continue an animation + ent->s.frame++; + return; + } - if (client->anim_priority == ANIM_DEATH) - return; // stay there - if (client->anim_priority == ANIM_JUMP) - { - if (!ent->groundentity) - return; // stay there - ent->client->anim_priority = ANIM_WAVE; - ent->s.frame = FRAME_jump3; - ent->client->anim_end = FRAME_jump6; - return; - } + if (client->anim_priority == ANIM_DEATH) + return; // stay there + if (client->anim_priority == ANIM_JUMP) + { + if (!ent->groundentity) + return; // stay there + ent->client->anim_priority = ANIM_WAVE; + ent->s.frame = FRAME_jump3; + ent->client->anim_end = FRAME_jump6; + return; + } -newanim: - // return to either a running or standing frame - client->anim_priority = ANIM_BASIC; - client->anim_duck = duck; - client->anim_run = run; + newanim: + // return to either a running or standing frame + client->anim_priority = ANIM_BASIC; + client->anim_duck = duck; + client->anim_run = run; - if (!ent->groundentity) - { - client->anim_priority = ANIM_JUMP; - if (ent->s.frame != FRAME_jump2) - ent->s.frame = FRAME_jump1; - client->anim_end = FRAME_jump2; - } - else if (run) - { // running - if (duck) - { - ent->s.frame = FRAME_crwalk1; - client->anim_end = FRAME_crwalk6; - } - else - { - ent->s.frame = FRAME_run1; - client->anim_end = FRAME_run6; - } - } - else - { // standing - if (duck) - { - ent->s.frame = FRAME_crstnd01; - client->anim_end = FRAME_crstnd19; - } - else - { - ent->s.frame = FRAME_stand01; - client->anim_end = FRAME_stand40; - } - } + if (!ent->groundentity) + { + client->anim_priority = ANIM_JUMP; + if (ent->s.frame != FRAME_jump2) + ent->s.frame = FRAME_jump1; + client->anim_end = FRAME_jump2; + } + else if (run) + { // running + if (duck) + { + ent->s.frame = FRAME_crwalk1; + client->anim_end = FRAME_crwalk6; + } + else + { + ent->s.frame = FRAME_run1; + client->anim_end = FRAME_run6; + } + } + else + { // standing + if (duck) + { + ent->s.frame = FRAME_crstnd01; + client->anim_end = FRAME_crstnd19; + } + else + { + ent->s.frame = FRAME_stand01; + client->anim_end = FRAME_stand40; + } + } } @@ -980,58 +990,128 @@ newanim: void Do_Bleeding( edict_t *ent ) { - int damage; - int temp; - vec3_t norm; - VectorSet(norm, 0.0, 0.0, 0.0 ); - if ( !(ent->client->bleeding) || (ent->health <= 0) ) - { - return; - } + int damage; + int temp; + vec3_t norm; + VectorSet(norm, 0.0, 0.0, 0.0 ); + if ( !(ent->client->bleeding) || (ent->health <= 0) ) + { + return; + } - temp = (int)(ent->client->bleeding * .2); - ent->client->bleeding -= temp; - if ( temp <= 0 ) - temp = 1; - ent->client->bleed_remain += temp; - damage = (int)(ent->client->bleed_remain/BLEED_TIME); - if ( ent->client->bleed_remain >= BLEED_TIME ) - { - ent->health -= damage; - if ( damage > 1 ) - { + temp = (int)(ent->client->bleeding * .2); + ent->client->bleeding -= temp; + if ( temp <= 0 ) + temp = 1; + ent->client->bleed_remain += temp; + damage = (int)(ent->client->bleed_remain/BLEED_TIME); + if ( ent->client->bleed_remain >= BLEED_TIME ) + { + ent->health -= damage; + if ( damage > 1 ) + { // action doens't do this - //ent->client->damage_blood += damage; // for feedback - } - if ( ent->health <= 0 ) - { - meansOfDeath = ent->client->attacker_mod; - locOfDeath = ent->client->attacker_loc; - Killed(ent, ent->client->attacker, ent->client->attacker, damage, ent->s.origin); - } - else - { - ent->client->bleed_remain %= BLEED_TIME; - } - if (ent->client->bleeddelay <= level.time) - { - vec3_t pos; - ent->client->bleeddelay = level.time + 2; // 2 seconds - VectorAdd(ent->client->bleedloc_offset, ent->absmax, pos); - //gi.cprintf(ent, PRINT_HIGH, "Bleeding now.\n"); - EjectBlooder(ent, pos, pos); + //ent->client->damage_blood += damage; // for feedback + } + if ( ent->health <= 0 ) + { + meansOfDeath = ent->client->attacker_mod; + locOfDeath = ent->client->attacker_loc; + Killed(ent, ent->client->attacker, ent->client->attacker, damage, ent->s.origin); + } + else + { + ent->client->bleed_remain %= BLEED_TIME; + } + if (ent->client->bleeddelay <= level.time) + { + vec3_t pos; + ent->client->bleeddelay = level.time + 2; // 2 seconds + VectorAdd(ent->client->bleedloc_offset, ent->absmax, pos); + //gi.cprintf(ent, PRINT_HIGH, "Bleeding now.\n"); + EjectBlooder(ent, pos, pos); - // do bleeding + // do bleeding - } + } - } + } } - +int canFire( edict_t* ent ) +{ + int result = 0; + + + switch ( ent->client->curr_weap ) + { + case MK23_NUM: + { + + if ( ent->client->mk23_rds > 0 ) + { + result = 1; + } + break; + + } + case MP5_NUM: + { + if ( ent->client->mp5_rds > 0 ) + { + result = 1; + } + break; + } + case M4_NUM: + { + if ( ent->client->m4_rds > 0 ) + { + result = 1; + } + break; + } + case M3_NUM: + { + if ( ent->client->shot_rds > 0 ) + { + result = 1; + } + break; + } + case HC_NUM: + { + if ( ent->client->cannon_rds == 2 ) + { + result = 1; + } + break; + } + case SNIPER_NUM: + { + if ( ent->client->sniper_rds > 0 ) + { + result = 1; + } + break; + } + case DUAL_NUM: + { + if ( ent->client->dual_rds > 0 ) + { + result = 1; + } + break; + } + + default: + result = 0; + } + return result; +} /* ================= ClientEndServerFrame @@ -1042,210 +1122,221 @@ and right after spawning */ void ClientEndServerFrame (edict_t *ent) { - float bobtime; - int i; -// int damage; // zucc for bleeding + float bobtime; + int i; + // int damage; // zucc for bleeding - current_player = ent; - current_client = ent->client; + current_player = ent; + current_client = ent->client; -//FIREBLADE - Unstick avoidance stuff. - if (ent->solid == SOLID_TRIGGER && !lights_camera_action) - { - edict_t *overlap; - if ((overlap = FindOverlap(ent, NULL)) == NULL) - { - ent->solid = SOLID_BBOX; - gi.linkentity(ent); - RemoveFromTransparentList(ent); - } - else - { - do - { - if (overlap->solid == SOLID_BBOX) - { - overlap->solid = SOLID_TRIGGER; - gi.linkentity(overlap); - AddToTransparentList(overlap); - } - overlap = FindOverlap(ent, overlap); - } while (overlap != NULL); - } - } -//FIREBLADE - - // - // If the origin or velocity have changed since ClientThink(), - // update the pmove values. This will happen when the client - // is pushed by a bmodel or kicked by an explosion. - // - // If it wasn't updated here, the view position would lag a frame - // behind the body position when pushed -- "sinking into plats" - // - for (i=0 ; i<3 ; i++) - { - current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0; - current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0; - } - - // - // If the end of unit layout is displayed, don't give - // the player any normal movement attributes - // - if (level.intermissiontime) + //FIREBLADE - Unstick avoidance stuff. + if (ent->solid == SOLID_TRIGGER && !lights_camera_action) + { + edict_t *overlap; + if ((overlap = FindOverlap(ent, NULL)) == NULL) + { + ent->solid = SOLID_BBOX; + gi.linkentity(ent); + RemoveFromTransparentList(ent); + } + else + { + do + { + if (overlap->solid == SOLID_BBOX) { - // FIXME: add view drifting here? - current_client->ps.blend[3] = 0; - current_client->ps.fov = 90; - G_SetStats (ent); - return; + overlap->solid = SOLID_TRIGGER; + gi.linkentity(overlap); + AddToTransparentList(overlap); } + overlap = FindOverlap(ent, overlap); + } while (overlap != NULL); + } + } + //FIREBLADE - AngleVectors (ent->client->v_angle, forward, right, up); + // + // If the origin or velocity have changed since ClientThink(), + // update the pmove values. This will happen when the client + // is pushed by a bmodel or kicked by an explosion. + // + // If it wasn't updated here, the view position would lag a frame + // behind the body position when pushed -- "sinking into plats" + // + for (i=0 ; i<3 ; i++) + { + current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0; + current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0; + } - // burn from lava, etc - P_WorldEffects (); + // + // If the end of unit layout is displayed, don't give + // the player any normal movement attributes + // + if (level.intermissiontime) + { + // FIXME: add view drifting here? + current_client->ps.blend[3] = 0; + current_client->ps.fov = 90; + G_SetStats (ent); + return; + } - // - // set model angles from view angles so other things in - // the world can tell which direction you are looking - // - if (ent->client->v_angle[PITCH] > 180) - ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; - else - ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; - ent->s.angles[YAW] = ent->client->v_angle[YAW]; - ent->s.angles[ROLL] = 0; - ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4; + AngleVectors (ent->client->v_angle, forward, right, up); - // - // calculate speed and cycle to be used for - // all cyclic walking effects - // - xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]); + // burn from lava, etc + P_WorldEffects (); - if (xyspeed < 5 || -//FIREBLADE - ent->solid == SOLID_NOT) -//FIREBLADE - { - bobmove = 0; - current_client->bobtime = 0; // start at beginning of cycle again - } - else if (ent->groundentity) - { // so bobbing only cycles when on ground - if (xyspeed > 210) - bobmove = 0.25; - else if (xyspeed > 100) - bobmove = 0.125; - else - bobmove = 0.0625; - } + // + // set model angles from view angles so other things in + // the world can tell which direction you are looking + // + if (ent->client->v_angle[PITCH] > 180) + ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; + else + ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; + ent->s.angles[YAW] = ent->client->v_angle[YAW]; + ent->s.angles[ROLL] = 0; + ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4; + + // + // calculate speed and cycle to be used for + // all cyclic walking effects + // + xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]); + + if (xyspeed < 5 || + //FIREBLADE + ent->solid == SOLID_NOT) + //FIREBLADE + { + bobmove = 0; + current_client->bobtime = 0; // start at beginning of cycle again + } + else if (ent->groundentity) + { // so bobbing only cycles when on ground + if (xyspeed > 210) + bobmove = 0.25; + else if (xyspeed > 100) + bobmove = 0.125; + else + bobmove = 0.0625; + } - bobtime = (current_client->bobtime += bobmove); + bobtime = (current_client->bobtime += bobmove); - if (current_client->ps.pmove.pm_flags & PMF_DUCKED) - bobtime *= 4; + if (current_client->ps.pmove.pm_flags & PMF_DUCKED) + bobtime *= 4; - bobcycle = (int)bobtime; - bobfracsin = fabs(sin(bobtime*M_PI)); + bobcycle = (int)bobtime; + bobfracsin = fabs(sin(bobtime*M_PI)); - // detect hitting the floor - P_FallingDamage (ent); + // detect hitting the floor + P_FallingDamage (ent); - // zucc handle any bleeding damage here - Do_Bleeding( ent ); + // zucc handle any bleeding damage here + Do_Bleeding( ent ); - // apply all the damage taken this frame - P_DamageFeedback (ent); + // apply all the damage taken this frame + P_DamageFeedback (ent); - // determine the view offsets - SV_CalcViewOffset (ent); + // determine the view offsets + SV_CalcViewOffset (ent); - // determine the gun offsets - SV_CalcGunOffset (ent); + // determine the gun offsets + SV_CalcGunOffset (ent); - // determine the full screen color blend - // must be after viewoffset, so eye contents can be - // accurately determined - // FIXME: with client prediction, the contents - // should be determined by the client - SV_CalcBlend (ent); + // determine the full screen color blend + // must be after viewoffset, so eye contents can be + // accurately determined + // FIXME: with client prediction, the contents + // should be determined by the client + SV_CalcBlend (ent); - G_SetStats (ent); + G_SetStats (ent); -//FIREBLADE - for (i = 1; i <= maxclients->value; i++) { - int stats_copy; - edict_t *e = g_edicts + i; - if (!ent->inuse || - e->client->chase_mode == 0 || - e->client->chase_target != ent) - continue; - for (stats_copy = 0; stats_copy < MAX_STATS; stats_copy++) - { - if (stats_copy >= STAT_TEAM_HEADER && stats_copy <= STAT_TEAM2_SCORE) - continue; // protect these - if (stats_copy == STAT_LAYOUTS || stats_copy == STAT_ID_VIEW) - continue; // protect these - if (stats_copy == STAT_SNIPER_ICON && - e->client->chase_mode != 2) - continue; // only show sniper lens when in chase mode 2 - if (stats_copy == STAT_FRAGS) - continue; - e->client->ps.stats[stats_copy] = - ent->client->ps.stats[stats_copy]; - } + //FIREBLADE + for (i = 1; i <= maxclients->value; i++) { + int stats_copy; + edict_t *e = g_edicts + i; + if (!ent->inuse || + e->client->chase_mode == 0 || + e->client->chase_target != ent) + continue; + for (stats_copy = 0; stats_copy < MAX_STATS; stats_copy++) + { + if (stats_copy >= STAT_TEAM_HEADER && stats_copy <= STAT_TEAM2_SCORE) + continue; // protect these + if (stats_copy == STAT_LAYOUTS || stats_copy == STAT_ID_VIEW) + continue; // protect these + if (stats_copy == STAT_SNIPER_ICON && + e->client->chase_mode != 2) + continue; // only show sniper lens when in chase mode 2 + if (stats_copy == STAT_FRAGS) + continue; + e->client->ps.stats[stats_copy] = + ent->client->ps.stats[stats_copy]; + } -//FB e->client->ps.stats[STAT_LAYOUTS] = 1; -//FB break; - } -//FIREBLADE + //FB e->client->ps.stats[STAT_LAYOUTS] = 1; + //FB break; + } + //FIREBLADE - G_SetClientEvent (ent); + G_SetClientEvent (ent); - G_SetClientEffects (ent); + G_SetClientEffects (ent); - G_SetClientSound (ent); + G_SetClientSound (ent); - G_SetClientFrame (ent); + G_SetClientFrame (ent); - VectorCopy (ent->velocity, ent->client->oldvelocity); - VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles); + VectorCopy (ent->velocity, ent->client->oldvelocity); + VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles); - // clear weapon kicks - VectorClear (ent->client->kick_origin); - VectorClear (ent->client->kick_angles); + // clear weapon kicks + VectorClear (ent->client->kick_origin); + VectorClear (ent->client->kick_angles); - // zucc - clear the open door command - ent->client->doortoggle = 0; + // zucc - clear the open door command + ent->client->doortoggle = 0; - if ( ent->client->push_timeout > 0 ) - ent->client->push_timeout--; - else - { - ent->client->attacker = NULL; - } + if ( ent->client->push_timeout > 0 ) + ent->client->push_timeout--; + /* else + { + ent->client->attacker = NULL; + ent->client->attacker_mod = MOD_BLEEDING; + } + */ + if ( ent->client->reload_attempts > 0 ) + { + if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) && canFire(ent) ) + { + ent->client->reload_attempts = 0; + } + else + { + Cmd_Reload_f(ent); + } + } + if ( ent->client->weapon_attempts > 0 ) + Cmd_Weapon_f(ent); - if ( ent->client->reload_attempts > 0 ) - Cmd_Reload_f(ent); + // if the scoreboard is up, update it + if (ent->client->showscores && !(level.framenum & 31) ) + { + //FIREBLADE + if (ent->client->menu) { + PMenu_Update(ent); + } else + //FIREBLADE + DeathmatchScoreboardMessage (ent, ent->enemy); + gi.unicast (ent, false); + } - - // if the scoreboard is up, update it - if (ent->client->showscores && !(level.framenum & 31) ) - { -//FIREBLADE - if (ent->client->menu) { - PMenu_Update(ent); - } else -//FIREBLADE - DeathmatchScoreboardMessage (ent, ent->enemy); - gi.unicast (ent, false); - } - -//FIREBLADE - RadioThink(ent); -//FIREBLADE + //FIREBLADE + RadioThink(ent); + //FIREBLADE } diff --git a/p_weapon.c b/p_weapon.c index ac2836a..28ccdb9 100644 --- a/p_weapon.c +++ b/p_weapon.c @@ -7,7 +7,7 @@ static qboolean is_quad; static byte is_silenced; - +void setFFState(edict_t *ent); void weapon_grenade_fire (edict_t *ent, qboolean held); void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) @@ -16,19 +16,19 @@ void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t f VectorCopy (distance, _distance); - if (client->pers.firing_style == ACTION_FIRING_CLASSIC || - client->pers.firing_style == ACTION_FIRING_CLASSIC_HIGH) - { - if (client->pers.hand == LEFT_HANDED) - _distance[1] *= -1; - else if (client->pers.hand == CENTER_HANDED) - _distance[1] = 0; - } - else - { + if (client->pers.firing_style == ACTION_FIRING_CLASSIC || + client->pers.firing_style == ACTION_FIRING_CLASSIC_HIGH) + { + if (client->pers.hand == LEFT_HANDED) + _distance[1] *= -1; + else if (client->pers.hand == CENTER_HANDED) + _distance[1] = 0; + } + else + { _distance[1] = 0; // fire from center always - } - + } + G_ProjectSource (point, _distance, forward, right, result); } @@ -39,10 +39,10 @@ void Old_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t VectorCopy (distance, _distance); if (client->pers.hand == LEFT_HANDED) - _distance[1] = -1; // changed from = to *= - // Fireblade 2/28/99 - // zucc reversed, this is only used for setting up shell ejection and - // since those look good this shouldn't be changed + _distance[1] = -1; // changed from = to *= + // Fireblade 2/28/99 + // zucc reversed, this is only used for setting up shell ejection and + // since those look good this shouldn't be changed else if (client->pers.hand == CENTER_HANDED) _distance[1] = 0; G_ProjectSource (point, _distance, forward, right, result); @@ -55,7 +55,7 @@ void Knife_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3 VectorCopy (distance, _distance); if (client->pers.hand == LEFT_HANDED) - _distance[1] *= -1; // changed from = to *= + _distance[1] *= -1; // changed from = to *= else if (client->pers.hand == CENTER_HANDED) _distance[1] = 0; G_ProjectSource (point, _distance, forward, right, result); @@ -511,6 +511,9 @@ void ChangeWeapon (edict_t *ent) ent->client->grenade_time = 0; } + // zucc - prevent reloading queue for previous weapon from doing anything + ent->client->reload_attempts = 0; + ent->client->pers.lastweapon = ent->client->pers.weapon; ent->client->pers.weapon = ent->client->newweapon; ent->client->newweapon = NULL; @@ -983,7 +986,7 @@ void Drop_Weapon (edict_t *ent, gitem_t *item) if ( (ent->client->pers.weapon == item ) && (ent->client->pers.inventory[index] == 1) ) { if ( (ent->client->ps.gunframe >= GRENADE_IDLE_FIRST) && (ent->client->ps.gunframe <= GRENADE_IDLE_LAST) - || ( ent->client->ps.gunframe >= GRENADE_THROW_FIRST && ent->client->ps.gunframe <= GRENADE_THROW_LAST) ) + || ( ent->client->ps.gunframe >= GRENADE_THROW_FIRST && ent->client->ps.gunframe <= GRENADE_THROW_LAST) ) { ent->client->ps.gunframe = 0; fire_grenade2 (ent, ent->s.origin, tv(0,0,0), GRENADE_DAMRAD, 0, 2, GRENADE_DAMRAD*2, false); @@ -1380,7 +1383,7 @@ void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, { ent->client->pers.inventory[ent->client->ammo_index] = 0; } - break; + return; } case DUAL_NUM: { @@ -1495,27 +1498,27 @@ void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, switch( ent->client->curr_weap ) { case MK23_NUM: - { - if (ent->client->dual_rds >= ent->client->mk23_max ) - ent->client->mk23_rds = ent->client->mk23_max; - else - ent->client->mk23_rds = ent->client->dual_rds; - if(ent->client->ps.gunframe == 3) // 3 - { - if ( ent->client->mk23_rds > 0 ) - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23slide.wav"), 1, ATTN_NORM, 0); - } - else - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - //mk23slap - ent->client->ps.gunframe = 62; - ent->client->weaponstate = WEAPON_END_MAG; - } - } - ent->client->fired = 0; //reset any firing delays - break; + { + if (ent->client->dual_rds >= ent->client->mk23_max ) + ent->client->mk23_rds = ent->client->mk23_max; + else + ent->client->mk23_rds = ent->client->dual_rds; + if(ent->client->ps.gunframe == 3) // 3 + { + if ( ent->client->mk23_rds > 0 ) + { + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23slide.wav"), 1, ATTN_NORM, 0); + } + else + { + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); + //mk23slap + ent->client->ps.gunframe = 62; + ent->client->weaponstate = WEAPON_END_MAG; + } + } + ent->client->fired = 0; //reset any firing delays + break; } case MP5_NUM: { @@ -1557,22 +1560,22 @@ void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, case DUAL_NUM: { if ( ent->client->dual_rds <= 0 && ent->client->ps.gunframe == 3) - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - if ( ent->client->dual_rds <= 0 && ent->client->ps.gunframe == 4) - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->client->ps.gunframe = 68; - ent->client->weaponstate = WEAPON_END_MAG; - ent->client->resp.sniper_mode = 0; - - ent->client->desired_fov = 90; - ent->client->ps.fov = 90; - ent->client->fired = 0; //reset any firing delays - ent->client->burst = 0; + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); + if ( ent->client->dual_rds <= 0 && ent->client->ps.gunframe == 4) + { + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); + ent->client->ps.gunframe = 68; + ent->client->weaponstate = WEAPON_END_MAG; + ent->client->resp.sniper_mode = 0; + + ent->client->desired_fov = 90; + ent->client->ps.fov = 90; + ent->client->fired = 0; //reset any firing delays + ent->client->burst = 0; - return; - } - ent->client->fired = 0; //reset any firing delays + return; + } + ent->client->fired = 0; //reset any firing delays ent->client->burst = 0; break; } @@ -1590,7 +1593,7 @@ void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, return; } - if (ent->client->weaponstate == WEAPON_BUSY) + if (ent->client->weaponstate == WEAPON_BUSY) { if ( ent->client->bandaging == 1 ) { @@ -1623,12 +1626,17 @@ void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, if( ent->client->curr_weap == SNIPER_NUM ) { if ( ent->client->desired_fov == 90 ) - ent->client->ps.fov = 90; - if ( !(ent->client->idle_weapon) ) + { + ent->client->ps.fov = 90; + ent->client->weaponstate = WEAPON_READY; + ent->client->idle_weapon = 0; + } + if ( !(ent->client->idle_weapon) && ent->client->desired_fov != 90 ) { ent->client->ps.fov = ent->client->desired_fov; ent->client->weaponstate = WEAPON_READY; ent->client->ps.gunindex = 0; + return; } else (ent->client->idle_weapon)--; @@ -2013,7 +2021,7 @@ void weapon_grenade_fire (edict_t *ent, qboolean held) timer = ent->client->grenade_time - level.time; speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER); - fire_grenade2 (ent, start, forward, damage, speed, timer, radius, held); + fire_grenade2 (ent, start, forward, GRENADE_DAMRAD, speed, timer, GRENADE_DAMRAD*2, held); if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) ) ent->client->pers.inventory[ent->client->ammo_index]--; @@ -2898,17 +2906,17 @@ void Pistol_Fire(edict_t *ent) int kick = 150; vec3_t offset; int spread = MK23_SPREAD; - int height; + int height; - if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) - { - height = 8; - } - else - height = 0; + if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) + { + height = 8; + } + else + height = 0; //If the user isn't pressing the attack button, advance the frame and go away.... if (!(ent->client->buttons & BUTTON_ATTACK)) @@ -3020,14 +3028,14 @@ void MP5_Fire(edict_t *ent) int kick = 90; vec3_t offset; int spread = MP5_SPREAD; - int height; + int height; - if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) - height = 8; - else - height = 0; + if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) + height = 8; + else + height = 0; - + //If the user isn't pressing the attack button, advance the frame and go away.... if (!(ent->client->buttons & BUTTON_ATTACK) && !(ent->client->burst) ) { @@ -3161,12 +3169,12 @@ void M4_Fire(edict_t *ent) int kick = 90; vec3_t offset; int spread = M4_SPREAD; - int height; + int height; - if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) - height = 8; - else - height = 0; + if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) + height = 8; + else + height = 0; //If the user isn't pressing the attack button, advance the frame and go away.... if (!(ent->client->buttons & BUTTON_ATTACK) && !(ent->client->burst) ) @@ -3307,6 +3315,10 @@ void Weapon_M4 (edict_t *ent) Weapon_Generic (ent, 10, 12, 39, 44, 63, 71, pause_frames, fire_frames, M4_Fire); } + +void InitShotgunDamageReport(); +void ProduceShotgunDamageReport(edict_t *); + void M3_Fire (edict_t *ent) { vec3_t start; @@ -3314,12 +3326,12 @@ void M3_Fire (edict_t *ent) vec3_t offset; int damage = 17; //actionquake is 15 standard int kick = 20; - int height; + int height; - if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) - height = 8; - else - height = 0; + if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) + height = 8; + else + height = 0; if (ent->client->ps.gunframe == 9) { @@ -3327,12 +3339,30 @@ void M3_Fire (edict_t *ent) return; } - AngleVectors (ent->client->v_angle, forward, right, NULL); + + AngleVectors (ent->client->v_angle, forward, right, NULL); VectorScale (forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -2; VectorSet(offset, 0, 8, ent->viewheight-height); + + + + if ( ent->client->ps.gunframe == 14 ) + { + if (!sv_shelloff->value) + { + vec3_t result; + Old_ProjectSource (ent->client, ent->s.origin, offset, forward, right, result); + EjectShell(ent, result, 0); + } + ent->client->ps.gunframe++; + return; + } + + + P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); /* if (is_quad) @@ -3341,28 +3371,21 @@ void M3_Fire (edict_t *ent) kick *= 4; }*/ + + setFFState(ent); + InitShotgunDamageReport(); //FB 6/3/99 if (deathmatch->value) fire_shotgun (ent, start, forward, damage, kick, 800, 800, 12/*DEFAULT_DEATHMATCH_SHOTGUN_COUNT*/, MOD_M3); else fire_shotgun (ent, start, forward, damage, kick, 800, 800, 12/*DEFAULT_SHOTGUN_COUNT*/, MOD_M3); - - if (!sv_shelloff->value) - { - vec3_t result; - Old_ProjectSource (ent->client, ent->s.origin, offset, forward, right, result); - EjectShell(ent, result, 0); - } - - - - - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/shotgf1b.wav"), 1, ATTN_NORM, 0); - + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/shotgf1b.wav"), 1, ATTN_NORM, 0); // send muzzle flash gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_SHOTGUN | is_silenced); gi.multicast (ent->s.origin, MULTICAST_PVS); + + ProduceShotgunDamageReport(ent); //FB 6/3/99 ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); @@ -3376,7 +3399,7 @@ void M3_Fire (edict_t *ent) void Weapon_M3 (edict_t *ent) { static int pause_frames[] = {22, 28, 0}; - static int fire_frames[] = {8, 9, 0}; + static int fire_frames[] = {8, 9, 14, 0}; Weapon_Generic (ent, 7, 15, 35, 41, 52, 60, pause_frames, fire_frames, M3_Fire); } @@ -3390,12 +3413,12 @@ void HC_Fire (edict_t *ent) vec3_t v; int damage = 20; int kick = 40; - int height; + int height; - if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) - height = 8; - else - height = 0; + if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) + height = 8; + else + height = 0; AngleVectors (ent->client->v_angle, forward, right, NULL); @@ -3416,18 +3439,19 @@ void HC_Fire (edict_t *ent) v[ROLL] = ent->client->v_angle[ROLL]; AngleVectors (v, forward, NULL, NULL); // default hspread is 1k and default vspread is 500 - fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD*4, DEFAULT_SHOTGUN_VSPREAD*4, 34/2, MOD_HC); + setFFState(ent); + InitShotgunDamageReport(); //FB 6/3/99 + fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD*4, DEFAULT_SHOTGUN_VSPREAD*4, 34/2, MOD_HC); v[YAW] = ent->client->v_angle[YAW] + 5; AngleVectors (v, forward, NULL, NULL); fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD*4, DEFAULT_SHOTGUN_VSPREAD*5, 34/2, MOD_HC); - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/cannon_fire.wav"), 1, ATTN_NORM, 0); - // send muzzle flash gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_SSHOTGUN | is_silenced); gi.multicast (ent->s.origin, MULTICAST_PVS); + ProduceShotgunDamageReport(ent); //FB 6/3/99 ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); @@ -3458,7 +3482,7 @@ void Sniper_Fire(edict_t *ent) int spread = SNIPER_SPREAD; - + if ( ent->client->ps.gunframe == 13 ) { @@ -3581,168 +3605,168 @@ void Weapon_Sniper (edict_t *ent) void Dual_Fire(edict_t *ent) { - int i; - vec3_t start; - vec3_t forward, right; - vec3_t angles; - int damage = 90; - int kick = 90; - vec3_t offset; - int spread = DUAL_SPREAD; - int height; + int i; + vec3_t start; + vec3_t forward, right; + vec3_t angles; + int damage = 90; + int kick = 90; + vec3_t offset; + int spread = DUAL_SPREAD; + int height; - if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) - height = 8; - else - height = 0; - - - spread = AdjustSpread( ent, spread ); - - if (ent->client->dual_rds < 1) - { - ent->client->ps.gunframe = 68; - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"),1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - //NoAmmoWeaponChange (ent); - return; - } - - - - //If the user isn't pressing the attack button, advance the frame and go away.... - if ( ent->client->ps.gunframe == 8 ) - { - //gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); - ent->client->ps.gunframe++; - - VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles); - AngleVectors (angles, forward, right, NULL); + if (ent->client->pers.firing_style == ACTION_FIRING_CLASSIC) + height = 8; + else + height = 0; + + + spread = AdjustSpread( ent, spread ); + + if (ent->client->dual_rds < 1) + { + ent->client->ps.gunframe = 68; + if (level.time >= ent->pain_debounce_time) + { + gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"),1, ATTN_NORM, 0); + ent->pain_debounce_time = level.time + 1; + } + //NoAmmoWeaponChange (ent); + return; + } + + + + //If the user isn't pressing the attack button, advance the frame and go away.... + if ( ent->client->ps.gunframe == 8 ) + { + //gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); + ent->client->ps.gunframe++; + + VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles); + AngleVectors (angles, forward, right, NULL); - VectorSet(offset, 0, 8, ent->viewheight-height); - P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); - if ( ent->client->dual_rds > 1 ) - { - - fire_bullet (ent, start, forward, damage, kick, spread, spread,MOD_DUAL); - - if (!sv_shelloff->value) - { - vec3_t result; - Old_ProjectSource (ent->client, ent->s.origin, offset, forward, right, result); - EjectShell(ent, result, 2); - } - - if ( ent->client->dual_rds > ent->client->mk23_max + 1 ) - { - ent->client->dual_rds -= 2; - } - else if ( ent->client->dual_rds > ent->client->mk23_max ) // 13 rounds left - { - ent->client->dual_rds -= 2; - ent->client->mk23_rds--; - } - else - { - ent->client->dual_rds -= 2; - ent->client->mk23_rds -= 2; - } - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); - - if (ent->client->dual_rds == 0) - { + VectorSet(offset, 0, 8, ent->viewheight-height); + P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); + if ( ent->client->dual_rds > 1 ) + { + + fire_bullet (ent, start, forward, damage, kick, spread, spread,MOD_DUAL); + + if (!sv_shelloff->value) + { + vec3_t result; + Old_ProjectSource (ent->client, ent->s.origin, offset, forward, right, result); + EjectShell(ent, result, 2); + } + + if ( ent->client->dual_rds > ent->client->mk23_max + 1 ) + { + ent->client->dual_rds -= 2; + } + else if ( ent->client->dual_rds > ent->client->mk23_max ) // 13 rounds left + { + ent->client->dual_rds -= 2; + ent->client->mk23_rds--; + } + else + { + ent->client->dual_rds -= 2; + ent->client->mk23_rds -= 2; + } + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); + + if (ent->client->dual_rds == 0) + { ent->client->ps.gunframe=68; ent->client->weaponstate = WEAPON_END_MAG; - - } - - - } - else - { - ent->client->dual_rds = 0; - ent->client->mk23_rds = 0; - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"),1, ATTN_NORM, 0); - //ent->pain_debounce_time = level.time + 1; - ent->client->ps.gunframe=68; - ent->client->weaponstate = WEAPON_END_MAG; - - } - return; - } - - if ( ent->client->ps.gunframe == 9 ) - { - ent->client->ps.gunframe += 2; - - return; - } - - - /*if (!(ent->client->buttons & BUTTON_ATTACK)) - { - ent->client->ps.gunframe++; - return; + + } + + + } + else + { + ent->client->dual_rds = 0; + ent->client->mk23_rds = 0; + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"),1, ATTN_NORM, 0); + //ent->pain_debounce_time = level.time + 1; + ent->client->ps.gunframe=68; + ent->client->weaponstate = WEAPON_END_MAG; + + } + return; + } + + if ( ent->client->ps.gunframe == 9 ) + { + ent->client->ps.gunframe += 2; + + return; + } + + + /*if (!(ent->client->buttons & BUTTON_ATTACK)) + { + ent->client->ps.gunframe++; + return; }*/ - ent->client->ps.gunframe++; - - //Oops! Out of ammo! - if (ent->client->dual_rds < 1) - { - ent->client->ps.gunframe = 12; - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"),1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - //NoAmmoWeaponChange (ent); - return; - } - - - //Calculate the kick angles - for (i=1 ; i<3 ; i++) - { - ent->client->kick_origin[i] = crandom() * 0.25; - ent->client->kick_angles[i] = crandom() * 0.5; - } - ent->client->kick_origin[0] = crandom() * 0.35; - - // get start / end positions - VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles); - AngleVectors (angles, forward, right, NULL); - // first set up for left firing - VectorSet(offset, 0, -20, ent->viewheight-height); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - - if (!sv_shelloff->value) - { - vec3_t result; - Old_ProjectSource (ent->client, ent->s.origin, offset, forward, right, result); - EjectShell(ent, result, 1); - } - - - - //If no reload, fire normally. - fire_bullet (ent, start, forward, damage, kick, spread, spread,MOD_DUAL); - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); - - - - //gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); - //Display the yellow muzzleflash light effect - gi.WriteByte (svc_muzzleflash); - gi.WriteShort (ent-g_edicts); - //If not silenced, play a shot sound for everyone else - gi.WriteByte (MZ_MACHINEGUN | is_silenced); - gi.multicast (ent->s.origin, MULTICAST_PVS); - PlayerNoise(ent, start, PNOISE_WEAPON); - - + ent->client->ps.gunframe++; + + //Oops! Out of ammo! + if (ent->client->dual_rds < 1) + { + ent->client->ps.gunframe = 12; + if (level.time >= ent->pain_debounce_time) + { + gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"),1, ATTN_NORM, 0); + ent->pain_debounce_time = level.time + 1; + } + //NoAmmoWeaponChange (ent); + return; + } + + + //Calculate the kick angles + for (i=1 ; i<3 ; i++) + { + ent->client->kick_origin[i] = crandom() * 0.25; + ent->client->kick_angles[i] = crandom() * 0.5; + } + ent->client->kick_origin[0] = crandom() * 0.35; + + // get start / end positions + VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles); + AngleVectors (angles, forward, right, NULL); + // first set up for left firing + VectorSet(offset, 0, -20, ent->viewheight-height); + P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); + + if (!sv_shelloff->value) + { + vec3_t result; + Old_ProjectSource (ent->client, ent->s.origin, offset, forward, right, result); + EjectShell(ent, result, 1); + } + + + + //If no reload, fire normally. + fire_bullet (ent, start, forward, damage, kick, spread, spread,MOD_DUAL); + gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); + + + + //gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/mk23fire.wav"), 1, ATTN_NORM, 0); + //Display the yellow muzzleflash light effect + gi.WriteByte (svc_muzzleflash); + gi.WriteShort (ent-g_edicts); + //If not silenced, play a shot sound for everyone else + gi.WriteByte (MZ_MACHINEGUN | is_silenced); + gi.multicast (ent->s.origin, MULTICAST_PVS); + PlayerNoise(ent, start, PNOISE_WEAPON); + + } void Weapon_Dual (edict_t *ent) @@ -4109,7 +4133,7 @@ int Knife_Fire (edict_t *ent) vec3_t forward, right; vec3_t offset; - int damage = 50; + int damage = 200; int throwndamage = 250; int kick = 50; // doesn't throw them back much.. int knife_return = 3; @@ -4271,7 +4295,7 @@ void gas_fire (edict_t *ent ) vec3_t offset; vec3_t forward, right; vec3_t start; - int damage = 300;//GRENADE_DAMRAD; + int damage = GRENADE_DAMRAD; float timer; int speed; float radius; @@ -4293,7 +4317,7 @@ void gas_fire (edict_t *ent ) else speed = 920; - fire_grenade2 (ent, start, forward, damage, speed, timer, radius, false); + fire_grenade2 (ent, start, forward, GRENADE_DAMRAD, speed, timer, GRENADE_DAMRAD*2, false); @@ -4333,284 +4357,284 @@ void gas_fire (edict_t *ent ) void Weapon_Gas (edict_t *ent) { - gitem_t* item; - if(ent->s.modelindex != 255) // zucc vwep - return; // not on client, so VWep animations could do wacky things - - //FIREBLADE - if (ent->client->weaponstate == WEAPON_FIRING && - ((ent->solid == SOLID_NOT && ent->deadflag != DEAD_DEAD) || - lights_camera_action)) - { - ent->client->weaponstate = WEAPON_READY; - } - //FIREBLADE - - if( ent->client->weaponstate == WEAPON_RELOADING) - { - if(ent->client->ps.gunframe < GRENADE_ACTIVATE_LAST) - { - ent->client->ps.gunframe++; - } - else - { - ent->client->ps.gunframe = GRENADE_PINIDLE_FIRST; - ent->client->weaponstate = WEAPON_READY; - } - } - - if (ent->client->weaponstate == WEAPON_DROPPING) - { - if (ent->client->ps.gunframe == 0) - { - ChangeWeapon (ent); - return; - } - else - { - // zucc going to have to do this a bit different because - // of the way I roll gunframes backwards for the thrownknife position - if((ent->client->ps.gunframe) == 3) - { - ent->client->anim_priority = ANIM_REVERSE; - if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain4+1; - ent->client->anim_end = FRAME_crpain1; - } - else - { - ent->s.frame = FRAME_pain304+1; - ent->client->anim_end = FRAME_pain301; - - } - } - ent->client->ps.gunframe--; - return; - } - - } - if (ent->client->weaponstate == WEAPON_ACTIVATING) - { - - if (ent->client->ps.gunframe == GRENADE_ACTIVATE_LAST ) - { - ent->client->ps.gunframe = GRENADE_PINIDLE_FIRST; - ent->client->weaponstate = WEAPON_READY; - return; - } - - if ( ent->client->ps.gunframe == GRENADE_PULL_LAST ) - { - ent->client->ps.gunframe = GRENADE_IDLE_FIRST; - ent->client->weaponstate = WEAPON_READY; - gi.cprintf(ent, PRINT_HIGH, "Pin pulled, ready for %s range throw\n", ent->client->resp.grenade_mode == 0 ? "short" : (ent->client->resp.grenade_mode == 1 ? "medium" : "long" ) ); - return; - } - - if ( ent->client->ps.gunframe == 75 ) - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex("misc/grenade.wav"), 1, ATTN_NORM, 0); - } - - ent->client->resp.sniper_mode = 0; - // has to be here for dropping the sniper rifle, in the drop command didn't work... - ent->client->desired_fov = 90; - ent->client->ps.fov = 90; - - ent->client->ps.gunframe++; - // gi.cprintf(ent, PRINT_HIGH, "After increment frames = %d\n", ent->client->ps.gunframe); - return; - } - - - // bandaging case - if ( (ent->client->bandaging) - && (ent->client->weaponstate != WEAPON_FIRING) - && (ent->client->weaponstate != WEAPON_BURSTING) - && (ent->client->weaponstate != WEAPON_BUSY) - && (ent->client->weaponstate != WEAPON_BANDAGING) ) - { - ent->client->weaponstate = WEAPON_BANDAGING; - ent->client->ps.gunframe = GRENADE_ACTIVATE_LAST; - return; - } - - - if ( ent->client->weaponstate == WEAPON_BUSY ) - { - - - if ( ent->client->bandaging == 1 ) - { - if ( !(ent->client->idle_weapon) ) - { - Bandage( ent ); - } - else - (ent->client->idle_weapon)--; - return; - } - // for after bandaging delay - if ( !(ent->client->idle_weapon) && ent->client->bandage_stopped ) - { - gitem_t *item; - item = FindItem(GRENADE_NAME); - if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 ) - { - ent->client->newweapon = FindItem( MK23_NAME ); - ent->client->bandage_stopped = 0; - ChangeWeapon( ent ); - return; - } - - ent->client->weaponstate = WEAPON_ACTIVATING; - ent->client->ps.gunframe = 0; - ent->client->bandage_stopped = 0; - } - else if ( ent->client->bandage_stopped ) - (ent->client->idle_weapon)--; - - - } - - if ( ent->client->weaponstate == WEAPON_BANDAGING ) - { - if (ent->client->ps.gunframe == 0 ) - { - ent->client->weaponstate = WEAPON_BUSY; - ent->client->idle_weapon = BANDAGE_TIME; - return; - } - ent->client->ps.gunframe--; - return; - } - - - if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING) - && (ent->client->weaponstate != WEAPON_BUSY ) ) - { - - // zucc - check if they have a primed grenade - - if ( ent->client->curr_weap == GRENADE_NUM - && ( ( ent->client->ps.gunframe >= GRENADE_IDLE_FIRST - && ent->client->ps.gunframe <= GRENADE_IDLE_LAST ) - || ( ent->client->ps.gunframe >= GRENADE_THROW_FIRST - && ent->client->ps.gunframe <= GRENADE_THROW_LAST ) ) ) - { - fire_grenade2 (ent, ent->s.origin, tv(0,0,0), GRENADE_DAMRAD, 0, 2, GRENADE_DAMRAD*2, false); - item = FindItem(GRENADE_NAME); - ent->client->pers.inventory[ITEM_INDEX(item)]--; - if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 ) - { - ent->client->newweapon = FindItem( MK23_NAME ); - ChangeWeapon( ent ); - return; - } - - } - - - ent->client->ps.gunframe = GRENADE_ACTIVATE_LAST; - // zucc more vwep stuff - if((GRENADE_ACTIVATE_LAST) < 4) - { - ent->client->anim_priority = ANIM_REVERSE; - if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain4+1; - ent->client->anim_end = FRAME_crpain1; - } - else - { - ent->s.frame = FRAME_pain304+1; - ent->client->anim_end = FRAME_pain301; - - } - } - - ent->client->weaponstate = WEAPON_DROPPING; - return; - } - - if (ent->client->weaponstate == WEAPON_READY) - { - if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) - //FIREBLADE - && (ent->solid != SOLID_NOT || ent->deadflag == DEAD_DEAD) && - !lights_camera_action) - //FIREBLADE - - { - - - if ( ent->client->ps.gunframe <= GRENADE_PINIDLE_LAST && - ent->client->ps.gunframe >= GRENADE_PINIDLE_FIRST ) - { - ent->client->ps.gunframe = GRENADE_PULL_FIRST; - ent->client->weaponstate = WEAPON_ACTIVATING; - ent->client->latched_buttons &= ~BUTTON_ATTACK; - } - else - { - if (ent->client->ps.gunframe == GRENADE_IDLE_LAST) - { - ent->client->ps.gunframe = GRENADE_IDLE_FIRST; - return; - } - ent->client->ps.gunframe++; - return; - } - } - - if ( ent->client->ps.gunframe >= GRENADE_IDLE_FIRST && - ent->client->ps.gunframe <= GRENADE_IDLE_LAST ) - { - ent->client->ps.gunframe = GRENADE_THROW_FIRST; - ent->client->anim_priority = ANIM_ATTACK; - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crattak1-1; - ent->client->anim_end = FRAME_crattak9; - } - else - { - ent->s.frame = FRAME_attack1-1; - ent->client->anim_end = FRAME_attack8; - } - ent->client->weaponstate = WEAPON_FIRING; - return; - - - - } - - if ( ent->client->ps.gunframe == GRENADE_PINIDLE_LAST) - { - ent->client->ps.gunframe = GRENADE_PINIDLE_FIRST; - return; - } - - ent->client->ps.gunframe++; - return; - } - if (ent->client->weaponstate == WEAPON_FIRING ) - { - - - if ( ent->client->ps.gunframe == 8 ) - { - gas_fire(ent); - return; - } - - - - ent->client->ps.gunframe++; - - if (ent->client->ps.gunframe == GRENADE_IDLE_FIRST+1 || - ent->client->ps.gunframe == GRENADE_PINIDLE_FIRST+1 ) - ent->client->weaponstate = WEAPON_READY; - } + gitem_t* item; + if(ent->s.modelindex != 255) // zucc vwep + return; // not on client, so VWep animations could do wacky things + + //FIREBLADE + if (ent->client->weaponstate == WEAPON_FIRING && + ((ent->solid == SOLID_NOT && ent->deadflag != DEAD_DEAD) || + lights_camera_action)) + { + ent->client->weaponstate = WEAPON_READY; + } + //FIREBLADE + + if( ent->client->weaponstate == WEAPON_RELOADING) + { + if(ent->client->ps.gunframe < GRENADE_ACTIVATE_LAST) + { + ent->client->ps.gunframe++; + } + else + { + ent->client->ps.gunframe = GRENADE_PINIDLE_FIRST; + ent->client->weaponstate = WEAPON_READY; + } + } + + if (ent->client->weaponstate == WEAPON_DROPPING) + { + if (ent->client->ps.gunframe == 0) + { + ChangeWeapon (ent); + return; + } + else + { + // zucc going to have to do this a bit different because + // of the way I roll gunframes backwards for the thrownknife position + if((ent->client->ps.gunframe) == 3) + { + ent->client->anim_priority = ANIM_REVERSE; + if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) + { + ent->s.frame = FRAME_crpain4+1; + ent->client->anim_end = FRAME_crpain1; + } + else + { + ent->s.frame = FRAME_pain304+1; + ent->client->anim_end = FRAME_pain301; + + } + } + ent->client->ps.gunframe--; + return; + } + + } + if (ent->client->weaponstate == WEAPON_ACTIVATING) + { + + if (ent->client->ps.gunframe == GRENADE_ACTIVATE_LAST ) + { + ent->client->ps.gunframe = GRENADE_PINIDLE_FIRST; + ent->client->weaponstate = WEAPON_READY; + return; + } + + if ( ent->client->ps.gunframe == GRENADE_PULL_LAST ) + { + ent->client->ps.gunframe = GRENADE_IDLE_FIRST; + ent->client->weaponstate = WEAPON_READY; + gi.cprintf(ent, PRINT_HIGH, "Pin pulled, ready for %s range throw\n", ent->client->resp.grenade_mode == 0 ? "short" : (ent->client->resp.grenade_mode == 1 ? "medium" : "long" ) ); + return; + } + + if ( ent->client->ps.gunframe == 75 ) + { + gi.sound(ent, CHAN_WEAPON, gi.soundindex("misc/grenade.wav"), 1, ATTN_NORM, 0); + } + + ent->client->resp.sniper_mode = 0; + // has to be here for dropping the sniper rifle, in the drop command didn't work... + ent->client->desired_fov = 90; + ent->client->ps.fov = 90; + + ent->client->ps.gunframe++; + // gi.cprintf(ent, PRINT_HIGH, "After increment frames = %d\n", ent->client->ps.gunframe); + return; + } + + + // bandaging case + if ( (ent->client->bandaging) + && (ent->client->weaponstate != WEAPON_FIRING) + && (ent->client->weaponstate != WEAPON_BURSTING) + && (ent->client->weaponstate != WEAPON_BUSY) + && (ent->client->weaponstate != WEAPON_BANDAGING) ) + { + ent->client->weaponstate = WEAPON_BANDAGING; + ent->client->ps.gunframe = GRENADE_ACTIVATE_LAST; + return; + } + + + if ( ent->client->weaponstate == WEAPON_BUSY ) + { + + + if ( ent->client->bandaging == 1 ) + { + if ( !(ent->client->idle_weapon) ) + { + Bandage( ent ); + } + else + (ent->client->idle_weapon)--; + return; + } + // for after bandaging delay + if ( !(ent->client->idle_weapon) && ent->client->bandage_stopped ) + { + gitem_t *item; + item = FindItem(GRENADE_NAME); + if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 ) + { + ent->client->newweapon = FindItem( MK23_NAME ); + ent->client->bandage_stopped = 0; + ChangeWeapon( ent ); + return; + } + + ent->client->weaponstate = WEAPON_ACTIVATING; + ent->client->ps.gunframe = 0; + ent->client->bandage_stopped = 0; + } + else if ( ent->client->bandage_stopped ) + (ent->client->idle_weapon)--; + + + } + + if ( ent->client->weaponstate == WEAPON_BANDAGING ) + { + if (ent->client->ps.gunframe == 0 ) + { + ent->client->weaponstate = WEAPON_BUSY; + ent->client->idle_weapon = BANDAGE_TIME; + return; + } + ent->client->ps.gunframe--; + return; + } + + + if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING) + && (ent->client->weaponstate != WEAPON_BUSY ) ) + { + + // zucc - check if they have a primed grenade + + if ( ent->client->curr_weap == GRENADE_NUM + && ( ( ent->client->ps.gunframe >= GRENADE_IDLE_FIRST + && ent->client->ps.gunframe <= GRENADE_IDLE_LAST ) + || ( ent->client->ps.gunframe >= GRENADE_THROW_FIRST + && ent->client->ps.gunframe <= GRENADE_THROW_LAST ) ) ) + { + fire_grenade2 (ent, ent->s.origin, tv(0,0,0), GRENADE_DAMRAD, 0, 2, GRENADE_DAMRAD*2, false); + item = FindItem(GRENADE_NAME); + ent->client->pers.inventory[ITEM_INDEX(item)]--; + if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 ) + { + ent->client->newweapon = FindItem( MK23_NAME ); + ChangeWeapon( ent ); + return; + } + + } + + + ent->client->ps.gunframe = GRENADE_ACTIVATE_LAST; + // zucc more vwep stuff + if((GRENADE_ACTIVATE_LAST) < 4) + { + ent->client->anim_priority = ANIM_REVERSE; + if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) + { + ent->s.frame = FRAME_crpain4+1; + ent->client->anim_end = FRAME_crpain1; + } + else + { + ent->s.frame = FRAME_pain304+1; + ent->client->anim_end = FRAME_pain301; + + } + } + + ent->client->weaponstate = WEAPON_DROPPING; + return; + } + + if (ent->client->weaponstate == WEAPON_READY) + { + if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) + //FIREBLADE + && (ent->solid != SOLID_NOT || ent->deadflag == DEAD_DEAD) && + !lights_camera_action) + //FIREBLADE + + { + + + if ( ent->client->ps.gunframe <= GRENADE_PINIDLE_LAST && + ent->client->ps.gunframe >= GRENADE_PINIDLE_FIRST ) + { + ent->client->ps.gunframe = GRENADE_PULL_FIRST; + ent->client->weaponstate = WEAPON_ACTIVATING; + ent->client->latched_buttons &= ~BUTTON_ATTACK; + } + else + { + if (ent->client->ps.gunframe == GRENADE_IDLE_LAST) + { + ent->client->ps.gunframe = GRENADE_IDLE_FIRST; + return; + } + ent->client->ps.gunframe++; + return; + } + } + + if ( ent->client->ps.gunframe >= GRENADE_IDLE_FIRST && + ent->client->ps.gunframe <= GRENADE_IDLE_LAST ) + { + ent->client->ps.gunframe = GRENADE_THROW_FIRST; + ent->client->anim_priority = ANIM_ATTACK; + if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) + { + ent->s.frame = FRAME_crattak1-1; + ent->client->anim_end = FRAME_crattak9; + } + else + { + ent->s.frame = FRAME_attack1-1; + ent->client->anim_end = FRAME_attack8; + } + ent->client->weaponstate = WEAPON_FIRING; + return; + + + + } + + if ( ent->client->ps.gunframe == GRENADE_PINIDLE_LAST) + { + ent->client->ps.gunframe = GRENADE_PINIDLE_FIRST; + return; + } + + ent->client->ps.gunframe++; + return; + } + if (ent->client->weaponstate == WEAPON_FIRING ) + { + + + if ( ent->client->ps.gunframe == 8 ) + { + gas_fire(ent); + return; + } + + + + ent->client->ps.gunframe++; + + if (ent->client->ps.gunframe == GRENADE_IDLE_FIRST+1 || + ent->client->ps.gunframe == GRENADE_PINIDLE_FIRST+1 ) + ent->client->weaponstate = WEAPON_READY; + } }