1#include "selection.qh" 2 3#include "weaponsystem.qh" 4#include <common/t_items.qh> 5#include <common/constants.qh> 6#include <common/net_linked.qh> 7#include <common/util.qh> 8#include <common/items/item.qh> 9#include <common/weapons/_all.qh> 10#include <common/state.qh> 11#include <common/mutators/mutator/waypoints/waypointsprites.qh> 12#include <common/wepent.qh> 13 14// switch between weapons 15void Send_WeaponComplain(entity e, float wpn, float type) 16{ 17 msg_entity = e; 18 WriteHeader(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN); 19 WriteByte(MSG_ONE, wpn); 20 WriteByte(MSG_ONE, type); 21} 22 23void Weapon_whereis(Weapon this, entity cl) 24{ 25 if (!autocvar_g_showweaponspawns) return; 26 IL_EACH(g_items, it.weapon == this.m_id && (!it.team || (it.ItemStatus & ITS_AVAILABLE)), 27 { 28 if (it.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2) 29 continue; 30 entity wp = WaypointSprite_Spawn( 31 WP_Weapon, 32 -2, 0, 33 NULL, it.origin + ('0 0 1' * it.maxs.z) * 1.2, 34 cl, 0, 35 NULL, enemy, 36 0, 37 RADARICON_NONE 38 ); 39 wp.wp_extra = this.m_id; 40 }); 41} 42 43bool client_hasweapon(entity this, Weapon wpn, .entity weaponentity, float andammo, bool complain) 44{ 45 float f = 0; 46 47 if (time < this.hasweapon_complain_spam) 48 complain = 0; 49 50 // ignore hook button when using other offhand equipment 51 if (this.offhand != OFFHAND_HOOK) 52 if (wpn == WEP_HOOK && !((this.weapons | weaponsInMap) & WepSet_FromWeapon(wpn))) 53 complain = 0; 54 55 if (complain) 56 this.hasweapon_complain_spam = time + 0.2; 57 58 if (wpn == WEP_Null) 59 { 60 if (complain) 61 sprint(this, "Invalid weapon\n"); 62 return false; 63 } 64 if (autocvar_g_weaponswitch_debug == 2 && weaponslot(weaponentity) > 0 && !(wpn.spawnflags & WEP_FLAG_DUALWIELD)) 65 return false; // no complaints needed 66 if (this.weapons & WepSet_FromWeapon(wpn)) 67 { 68 if (andammo) 69 { 70 if(this.items & IT_UNLIMITED_WEAPON_AMMO) 71 { 72 f = 1; 73 } 74 else 75 { 76 f = wpn.wr_checkammo1(wpn, this, weaponentity) + wpn.wr_checkammo2(wpn, this, weaponentity); 77 78 // always allow selecting the Mine Layer if we placed mines, so that we can detonate them 79 if(wpn == WEP_MINE_LAYER) 80 IL_EACH(g_mines, it.owner == this && it.weaponentity_fld == weaponentity, 81 { 82 f = 1; 83 break; // no need to continue 84 }); 85 } 86 if (!f) 87 { 88 if (complain) 89 if(IS_REAL_CLIENT(this)) 90 { 91 play2(this, SND(UNAVAILABLE)); 92 Send_WeaponComplain (this, wpn.m_id, 0); 93 } 94 return false; 95 } 96 } 97 return true; 98 } 99 if (complain) 100 { 101 // DRESK - 3/16/07 102 // Report Proper Weapon Status / Modified Weapon Ownership Message 103 if (weaponsInMap & WepSet_FromWeapon(wpn)) 104 { 105 Send_WeaponComplain(this, wpn.m_id, 1); 106 if(autocvar_g_showweaponspawns < 3) 107 Weapon_whereis(wpn, this); 108 else 109 { 110 FOREACH(Weapons, it.impulse == wpn.impulse, 111 { 112 Weapon_whereis(it, this); 113 }); 114 } 115 } 116 else 117 { 118 Send_WeaponComplain (this, wpn.m_id, 2); 119 } 120 121 play2(this, SND(UNAVAILABLE)); 122 } 123 return false; 124} 125 126float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, float complain, float skipmissing, .entity weaponentity) 127{ 128 // We cannot tokenize in this function, as GiveItems calls this 129 // function. Thus we must use car/cdr. 130 float weaponwant, first_valid, prev_valid, switchtonext, switchtolast; 131 WepSet wepset = '0 0 0'; 132 switchtonext = switchtolast = 0; 133 first_valid = prev_valid = 0; 134 float weaponcur; 135 entity wep; 136 137 if(skipmissing || this.(weaponentity).selectweapon == 0) 138 weaponcur = this.(weaponentity).m_switchweapon.m_id; 139 else 140 weaponcur = this.(weaponentity).selectweapon; 141 142 if(dir == 0) 143 switchtonext = 1; 144 145 int c = 0; 146 147 string rest = weaponorder; 148 while(rest != "") 149 { 150 weaponwant = stof(car(rest)); rest = cdr(rest); 151 wep = Weapons_from(weaponwant); 152 wepset = wep.m_wepset; 153 if(imp >= 0) 154 if(wep.impulse != imp) 155 continue; 156 157 bool have_other = false; 158 FOREACH(Weapons, it != WEP_Null, { 159 if(i != weaponwant) 160 if(it.impulse == imp || imp < 0) 161 if((this.weapons & (it.m_wepset)) || (weaponsInMap & (it.m_wepset))) 162 have_other = true; 163 }); 164 165 // skip weapons we don't own that aren't normal and aren't in the map 166 if(!(this.weapons & wepset)) 167 if(!(weaponsInMap & wepset)) 168 if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other) 169 continue; 170 171 ++c; 172 173 if(!skipmissing || client_hasweapon(this, wep, weaponentity, true, false)) 174 { 175 if(switchtonext) 176 return weaponwant; 177 if(!first_valid) 178 first_valid = weaponwant; 179 if(weaponwant == weaponcur) 180 { 181 if(dir >= 0) 182 switchtonext = 1; 183 else if(prev_valid) 184 return prev_valid; 185 else 186 switchtolast = 1; 187 } 188 prev_valid = weaponwant; 189 } 190 } 191 if(first_valid) 192 { 193 if(switchtolast) 194 return prev_valid; 195 else 196 return first_valid; 197 } 198 // complain (but only for one weapon on the button that has been pressed) 199 if(complain) 200 { 201 this.weaponcomplainindex += 1; 202 c = (this.weaponcomplainindex % c) + 1; 203 rest = weaponorder; 204 while(rest != "") 205 { 206 weaponwant = stof(car(rest)); rest = cdr(rest); 207 wep = Weapons_from(weaponwant); 208 wepset = wep.m_wepset; 209 if(imp >= 0) 210 if(wep.impulse != imp) 211 continue; 212 213 bool have_other = false; 214 FOREACH(Weapons, it != WEP_Null, { 215 if(i != weaponwant) 216 if(it.impulse == imp || imp < 0) 217 if((this.weapons & (it.m_wepset)) || (weaponsInMap & (it.m_wepset))) 218 have_other = true; 219 }); 220 221 // skip weapons we don't own that aren't normal and aren't in the map 222 if(!(this.weapons & wepset)) 223 if(!(weaponsInMap & wepset)) 224 if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other) 225 continue; 226 227 --c; 228 if(c == 0) 229 { 230 client_hasweapon(this, wep, weaponentity, true, true); 231 break; 232 } 233 } 234 } 235 return 0; 236} 237 238void W_SwitchWeapon_Force(Player this, Weapon wep, .entity weaponentity) 239{ 240 TC(Weapon, wep); 241 this.(weaponentity).cnt = this.(weaponentity).m_switchweapon.m_id; 242 this.(weaponentity).m_switchweapon = wep; 243 this.(weaponentity).selectweapon = wep.m_id; 244} 245 246// perform weapon to attack (weaponstate and attack_finished check is here) 247void W_SwitchToOtherWeapon(entity this, .entity weaponentity) 248{ 249 // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) 250 Weapon ww; 251 WepSet set = WepSet_FromWeapon(this.(weaponentity).m_weapon); 252 if (this.weapons & set) 253 { 254 this.weapons &= ~set; 255 ww = w_getbestweapon(this, weaponentity); 256 this.weapons |= set; 257 } 258 else 259 { 260 ww = w_getbestweapon(this, weaponentity); 261 } 262 if (ww == WEP_Null) return; 263 W_SwitchWeapon_Force(this, ww, weaponentity); 264} 265 266void W_SwitchWeapon(entity this, Weapon w, .entity weaponentity) 267{ 268 if(this.(weaponentity).m_switchweapon != w) 269 { 270 if(client_hasweapon(this, w, weaponentity, true, true)) 271 W_SwitchWeapon_Force(this, w, weaponentity); 272 else 273 this.(weaponentity).selectweapon = w.m_id; // update selectweapon anyway 274 } 275 else if(!forbidWeaponUse(this)) 276 { 277 entity actor = this; 278 w.wr_reload(w, actor, weaponentity); 279 } 280} 281 282void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity) 283{ 284 float w; 285 w = W_GetCycleWeapon(this, weaponorder, dir, -1, 1, true, weaponentity); 286 if(w > 0) 287 W_SwitchWeapon(this, Weapons_from(w), weaponentity); 288} 289 290void W_NextWeaponOnImpulse(entity this, float imp, .entity weaponentity) 291{ 292 float w; 293 w = W_GetCycleWeapon(this, this.cvar_cl_weaponpriority, +1, imp, 1, (this.cvar_cl_weaponimpulsemode == 0), weaponentity); 294 if(w > 0) 295 W_SwitchWeapon(this, Weapons_from(w), weaponentity); 296} 297 298// next weapon 299void W_NextWeapon(entity this, int list, .entity weaponentity) 300{ 301 if(list == 0) 302 W_CycleWeapon(this, weaponorder_byid, -1, weaponentity); 303 else if(list == 1) 304 W_CycleWeapon(this, this.weaponorder_byimpulse, -1, weaponentity); 305 else if(list == 2) 306 W_CycleWeapon(this, this.cvar_cl_weaponpriority, -1, weaponentity); 307} 308 309// prev weapon 310void W_PreviousWeapon(entity this, float list, .entity weaponentity) 311{ 312 if(list == 0) 313 W_CycleWeapon(this, weaponorder_byid, +1, weaponentity); 314 else if(list == 1) 315 W_CycleWeapon(this, this.weaponorder_byimpulse, +1, weaponentity); 316 else if(list == 2) 317 W_CycleWeapon(this, this.cvar_cl_weaponpriority, +1, weaponentity); 318} 319 320// previously used if exists and has ammo, (second) best otherwise 321void W_LastWeapon(entity this, .entity weaponentity) 322{ 323 Weapon wep = Weapons_from(this.(weaponentity).cnt); 324 if (client_hasweapon(this, wep, weaponentity, true, false)) 325 W_SwitchWeapon(this, wep, weaponentity); 326 else 327 W_SwitchToOtherWeapon(this, weaponentity); 328} 329