1 #include "game.h" 2 namespace weapons 3 { 4 VAR(IDF_PERSIST, autoreloading, 0, 2, 4); // 0 = never, 1 = when empty, 2 = weapons that don't add a full clip, 3 = always (+1 zooming weaps too) 5 VAR(IDF_PERSIST, autodelayreload, 0, 0, VAR_MAX); 6 7 VAR(IDF_PERSIST, skipspawnweapon, 0, 0, 6); // skip spawnweapon; 0 = never, 1 = if numweaps > 1 (+1), 3 = if carry > 0 (+2), 6 = always 8 VAR(IDF_PERSIST, skipclaw, 0, 0, 10); // skip claw; 0 = never, 1 = if numweaps > 1 (+2), 4 = if carry > 0 (+2), 7 = if carry > 0 and is offset (+2), 10 = always 9 VAR(IDF_PERSIST, skippistol, 0, 0, 10); // skip pistol; 0 = never, 1 = if numweaps > 1 (+2), 4 = if carry > 0 (+2), 7 = if carry > 0 and is offset (+2), 10 = always 10 VAR(IDF_PERSIST, skipgrenade, 0, 0, 10); // skip grenade; 0 = never, 1 = if numweaps > 1 (+2), 4 = if carry > 0 (+2), 7 = if carry > 0 and is offset (+2), 10 = always 11 VAR(IDF_PERSIST, skipmine, 0, 0, 10); // skip mine; 0 = never, 1 = if numweaps > 1 (+2), 4 = if carry > 0 (+2), 7 = if carry > 0 and is offset (+2), 10 = always 12 VAR(IDF_PERSIST, skiprocket, 0, 0, 10); // skip mine; 0 = never, 1 = if numweaps > 1 (+2), 4 = if carry > 0 (+2), 7 = if carry > 0 and is offset (+2), 10 = always 13 14 VAR(IDF_PERSIST, skippickup, 0, 0, 1); 15 16 int lastweapselect = 0; 17 VAR(IDF_PERSIST, weapselectdelay, 0, 200, VAR_MAX); 18 19 vector<int> weaplist; buildweaplist(const char * str)20 void buildweaplist(const char *str) 21 { 22 vector<char *> list; 23 explodelist(str, list); 24 weaplist.shrink(0); 25 loopv(list) 26 { 27 int weap = -1; 28 if(isnumeric(list[i][0])) weap = atoi(list[i]); 29 else loopj(W_ALL) if(!strcasecmp(weaptype[j].name, list[i])) 30 { 31 weap = j; 32 break; 33 } 34 if(isweap(weap) && weaplist.find(weap) < 0) 35 weaplist.add(weap); 36 } 37 list.deletearrays(); 38 loopi(W_ALL) if(weaplist.find(i) < 0) weaplist.add(i); // make sure all weapons have a slot 39 changedkeys = lastmillis; 40 } 41 SVARF(IDF_PERSIST, weapselectlist, "", buildweaplist(weapselectlist)); 42 VARF(IDF_PERSIST, weapselectslot, 0, 1, 2, buildweaplist(weapselectlist)); // 0 = by id, 1 = by slot, 2 = by list 43 slot(gameent * d,int n,bool back)44 int slot(gameent *d, int n, bool back) 45 { 46 if(d && weapselectslot) 47 { 48 if(weapselectslot == 2 && weaplist.empty()) buildweaplist(weapselectlist); 49 int p = m_weapon(d->actortype, game::gamemode, game::mutators), w = 0; 50 loopi(W_ALL) 51 { 52 int weap = weapselectslot == 2 ? weaplist[i] : i; 53 if(d->holdweap(weap, p, lastmillis)) 54 { 55 if(n == (back ? w : weap)) return back ? weap : w; 56 w++; 57 } 58 } 59 return -1; 60 } 61 return n; 62 } 63 64 ICOMMAND(0, weapslot, "i", (int *o), intret(slot(game::player1, *o >= 0 ? *o : game::player1->weapselect))); // -1 = weapselect slot 65 ICOMMAND(0, weapselect, "", (), intret(game::player1->weapselect)); 66 ICOMMAND(0, ammo, "i", (int *n), intret(isweap(*n) ? game::player1->ammo[*n] : -1)); 67 ICOMMAND(0, reloadweap, "i", (int *n), intret(isweap(*n) && w_reload(*n, m_weapon(game::player1->actortype, game::gamemode, game::mutators)) ? 1 : 0)); 68 ICOMMAND(0, hasweap, "ii", (int *n, int *o), intret(isweap(*n) && game::player1->hasweap(*n, *o) ? 1 : 0)); 69 ICOMMAND(0, getweap, "ii", (int *n, int *o), { 70 if(isweap(*n)) switch(*o) 71 { 72 case -1: result(weaptype[*n].name); break; 73 case 0: result(W(*n, name)); break; 74 case 1: result(hud::itemtex(WEAPON, *n)); break; 75 default: break; 76 } 77 }); 78 weapselect(gameent * d,int weap,int filter,bool local)79 bool weapselect(gameent *d, int weap, int filter, bool local) 80 { 81 if(!gs_playing(game::gamestate)) return false; 82 bool newoff = false; 83 int oldweap = d->weapselect; 84 if(local) 85 { 86 int interrupts = filter; 87 interrupts &= ~(1<<W_S_RELOAD); 88 if(!d->canswitch(weap, m_weapon(d->actortype, game::gamemode, game::mutators), lastmillis, interrupts)) 89 { 90 if(!d->canswitch(weap, m_weapon(d->actortype, game::gamemode, game::mutators), lastmillis, filter)) return false; 91 else if(!isweap(oldweap) || d->weapload[oldweap] <= 0) return false; 92 else newoff = true; 93 } 94 } 95 if(d->weapswitch(weap, lastmillis, weaponswitchdelay)) 96 { 97 if(local) 98 { 99 if(newoff) 100 { 101 int offset = d->weapload[oldweap]; 102 d->ammo[oldweap] = max(d->ammo[oldweap]-offset, 0); 103 d->weapload[oldweap] = -d->weapload[oldweap]; 104 } 105 client::addmsg(N_WSELECT, "ri3", d->clientnum, lastmillis-game::maptime, weap); 106 } 107 playsound(WSND(weap, S_W_SWITCH), d->o, d, 0, -1, -1, -1, &d->wschan); 108 return true; 109 } 110 return false; 111 } 112 weapreload(gameent * d,int weap,int load,int ammo,bool local)113 bool weapreload(gameent *d, int weap, int load, int ammo, bool local) 114 { 115 if(!gs_playing(game::gamestate) || (!local && (d == game::player1 || d->ai))) return false; // this can't be fixed until 1.5 116 if(local) 117 { 118 if(!d->canreload(weap, m_weapon(d->actortype, game::gamemode, game::mutators), false, lastmillis)) 119 { 120 if(d->weapstate[weap] == W_S_POWER) d->setweapstate(weap, W_S_WAIT, 100, lastmillis); 121 return false; 122 } 123 client::addmsg(N_RELOAD, "ri3", d->clientnum, lastmillis-game::maptime, weap); 124 int oldammo = d->ammo[weap]; 125 ammo = min(max(d->ammo[weap], 0) + W(weap, ammoadd), W(weap, ammomax)); 126 load = ammo-oldammo; 127 } 128 d->weapload[weap] = load; 129 d->ammo[weap] = min(ammo, W(weap, ammomax)); 130 playsound(WSND(weap, S_W_RELOAD), d->o, d, 0, -1, -1, -1, &d->wschan); 131 d->setweapstate(weap, W_S_RELOAD, W(weap, delayreload), lastmillis); 132 return true; 133 } 134 weaponswitch(gameent * d,int a=-1,int b=-1)135 void weaponswitch(gameent *d, int a = -1, int b = -1) 136 { 137 if(!gs_playing(game::gamestate) || a < -1 || b < -1 || a >= W_ALL || b >= W_ALL) return; 138 if(weapselectdelay && lastweapselect && totalmillis-lastweapselect < weapselectdelay) return; 139 if(d->weapwaited(d->weapselect, lastmillis, (1<<W_S_SWITCH)|(1<<W_S_RELOAD))) 140 { 141 int s = slot(d, d->weapselect); 142 loopi(W_ALL) // only loop the amount of times we have weaps for 143 { 144 if(a >= 0) s = a; 145 else s += b; 146 while(s > W_ALL-1) s -= W_ALL; 147 while(s < 0) s += W_ALL; 148 149 int n = slot(d, s, true); 150 if(a < 0) 151 { // weapon skipping when scrolling 152 int p = m_weapon(d->actortype, game::gamemode, game::mutators); 153 #define skipweap(q,w) \ 154 { \ 155 if(q && n == w) switch(q) \ 156 { \ 157 case 10: continue; break; \ 158 case 7: case 8: case 9: if(d->carry(p, 5, w) > (q-7)) continue; break; \ 159 case 4: case 5: case 6: if(d->carry(p, 1, w) > (q-3)) continue; break; \ 160 case 1: case 2: case 3: if(d->carry(p, 0, w) > q) continue; break; \ 161 case 0: default: break; \ 162 } \ 163 } 164 skipweap(skipspawnweapon, p); 165 skipweap(skipclaw, W_CLAW); 166 skipweap(skippistol, W_PISTOL); 167 if(!m_kaboom(game::gamemode, game::mutators)) 168 { 169 skipweap(skipgrenade, W_GRENADE); 170 skipweap(skipmine, W_MINE); 171 skipweap(skiprocket, W_ROCKET); 172 } 173 } 174 175 if(weapselect(d, n, (1<<W_S_SWITCH)|(1<<W_S_RELOAD))) 176 { 177 lastweapselect = totalmillis ? totalmillis : 1; 178 return; 179 } 180 else if(a >= 0) break; 181 } 182 } 183 game::errorsnd(d); 184 } 185 ICOMMAND(0, weapon, "ss", (char *a, char *b), weaponswitch(game::player1, *a ? parseint(a) : -1, *b ? parseint(b) : -1)); 186 canuse(int weap)187 bool canuse(int weap) 188 { 189 if(!skippickup || m_kaboom(game::gamemode, game::mutators)) return true; 190 #define canuseweap(q,w) if(q && weap == w) return false; 191 canuseweap(skipgrenade, W_GRENADE); 192 canuseweap(skipmine, W_MINE); 193 canuseweap(skiprocket, W_ROCKET); 194 return true; 195 } 196 weapdrop(gameent * d,int w)197 void weapdrop(gameent *d, int w) 198 { 199 if(!gs_playing(game::gamestate)) return; 200 int weap = isweap(w) ? w : d->weapselect, sweap = m_weapon(d->actortype, game::gamemode, game::mutators); 201 d->action[AC_DROP] = false; 202 if(!d->candrop(weap, sweap, lastmillis, m_loadout(game::gamemode, game::mutators), (1<<W_S_SWITCH))) 203 { 204 if(!d->candrop(weap, sweap, lastmillis, m_loadout(game::gamemode, game::mutators), (1<<W_S_SWITCH)|(1<<W_S_RELOAD)) || !isweap(d->weapselect) || d->weapload[d->weapselect] <= 0) 205 { 206 game::errorsnd(d); 207 return; 208 } 209 else 210 { 211 int offset = d->weapload[d->weapselect]; 212 d->ammo[d->weapselect] = max(d->ammo[d->weapselect]-offset, 0); 213 d->weapload[d->weapselect] = -d->weapload[d->weapselect]; 214 } 215 } 216 client::addmsg(N_DROP, "ri3", d->clientnum, lastmillis-game::maptime, weap); 217 d->setweapstate(weap, W_S_WAIT, weaponswitchdelay, lastmillis); 218 } 219 autoreload(gameent * d,int flags=0)220 bool autoreload(gameent *d, int flags = 0) 221 { 222 if(gs_playing(game::gamestate) && d == game::player1 && W2(d->weapselect, ammosub, WS(flags)) && d->canreload(d->weapselect, m_weapon(d->actortype, game::gamemode, game::mutators), false, lastmillis)) 223 { 224 bool noammo = d->ammo[d->weapselect] < W2(d->weapselect, ammosub, WS(flags)), 225 noattack = !d->action[AC_PRIMARY] && !d->action[AC_SECONDARY]; 226 if((noammo || noattack) && !d->action[AC_USE] && d->weapstate[d->weapselect] == W_S_IDLE && (noammo || lastmillis-d->weaptime[d->weapselect] >= autodelayreload)) 227 return autoreloading >= (noammo ? 1 : (W(d->weapselect, ammoadd) < W(d->weapselect, ammomax) ? 2 : (W2(d->weapselect, cooked, true)&W_C_ZOOM ? 4 : 3))); 228 } 229 return false; 230 } 231 checkweapons(gameent * d)232 void checkweapons(gameent *d) 233 { 234 int sweap = m_weapon(d->actortype, game::gamemode, game::mutators); 235 if(!d->hasweap(d->weapselect, sweap)) weapselect(d, d->bestweap(sweap, true), 1<<W_S_RELOAD, true); 236 else if(d->action[AC_RELOAD] || autoreload(d)) weapreload(d, d->weapselect); 237 else if(d->action[AC_DROP]) weapdrop(d, d->weapselect); 238 } 239 offsetray(vec & from,vec & to,float spread,float z,vec & dest)240 void offsetray(vec &from, vec &to, float spread, float z, vec &dest) 241 { 242 float f = to.dist(from)*spread/10000.f; 243 for(;;) 244 { 245 #define RNDD rnd(101)-50 246 vec v(RNDD, RNDD, RNDD); 247 if(v.magnitude() > 50) continue; 248 v.mul(f); 249 v.z = z > 0 ? v.z/z : 0; 250 dest = to; 251 dest.add(v); 252 if(dest != from) 253 { 254 vec dir = vec(dest).sub(from).normalize(); 255 raycubepos(from, dir, dest, 0, RAY_CLIPMAT|RAY_ALPHAPOLY); 256 } 257 return; 258 } 259 } 260 accmod(gameent * d,bool zooming)261 float accmod(gameent *d, bool zooming) 262 { 263 float r = 0; 264 bool running = d->running(moveslow), moving = d->move || d->strafe; 265 if(running || moving) r += running ? spreadrunning : spreadmoving; 266 else if(zooming) r += spreadzoom; 267 else if(d->crouching()) r += spreadcrouch; 268 else r += spreadstill; 269 if(spreadinair > 0 && d->airmillis && !d->onladder) r += spreadinair; 270 return r; 271 } 272 doshot(gameent * d,vec & targ,int weap,bool pressed,bool secondary,int force)273 bool doshot(gameent *d, vec &targ, int weap, bool pressed, bool secondary, int force) 274 { 275 bool hadcook = W2(weap, cooked, true)&W_C_KEEP && (d->prevstate[weap] == W_S_ZOOM || d->prevstate[weap] == W_S_POWER), 276 zooming = W2(weap, cooked, true)&W_C_ZOOM && (d->weapstate[weap] == W_S_ZOOM || (pressed && secondary) || (hadcook && d->prevstate[weap] == W_S_ZOOM)), wassecond = secondary; 277 if(hadcook || zooming) 278 { 279 if(!pressed) 280 { 281 client::addmsg(N_SPHY, "ri5", d->clientnum, SPHY_COOK, W_S_IDLE, 0, 0); 282 d->setweapstate(weap, W_S_IDLE, 0, lastmillis, 0, true); 283 return false; 284 } 285 else secondary = zooming; 286 } 287 int offset = 0, sweap = m_weapon(d->actortype, game::gamemode, game::mutators); 288 if(!d->canshoot(weap, secondary ? HIT_ALT : 0, sweap, lastmillis)) 289 { 290 if(!d->canshoot(weap, secondary ? HIT_ALT : 0, sweap, lastmillis, (1<<W_S_RELOAD))) 291 { 292 // if the problem is not enough ammo, do the reload.. 293 if(autoreload(d, secondary ? HIT_ALT : 0)) weapreload(d, weap); 294 return false; 295 } 296 else if(d->weapload[weap] <= 0 || weap != d->weapselect) return false; 297 else offset = d->weapload[weap]; 298 } 299 float scale = 1; 300 int sub = W2(weap, ammosub, secondary), cooked = force; 301 if(W2(weap, cooktime, secondary) || zooming) 302 { 303 float maxscale = 1; 304 if(sub > 1 && d->ammo[weap] < sub) maxscale = d->ammo[weap]/float(sub); 305 int len = int(W2(weap, cooktime, secondary)*maxscale), type = zooming ? W_S_ZOOM : W_S_POWER; 306 if(!cooked) 307 { 308 if(d->weapstate[weap] != type) 309 { 310 if(pressed) 311 { 312 if(offset > 0) 313 { 314 d->ammo[weap] = max(d->ammo[weap]-offset, 0); 315 d->weapload[weap] = -offset; 316 } 317 int offtime = hadcook && d->prevstate[weap] == type ? lastmillis-d->prevtime[weap] : 0; 318 client::addmsg(N_SPHY, "ri5", d->clientnum, SPHY_COOK, type, len, offtime); 319 d->setweapstate(weap, type, len, lastmillis, offtime); 320 } 321 else return false; 322 } 323 cooked = len ? clamp(lastmillis-d->weaptime[weap], 1, len) : 1; 324 if(zooming) 325 { 326 if(pressed && wassecond) return false; 327 } 328 else if(pressed && cooked < len) return false; 329 } 330 scale = len ? cooked/float(W2(weap, cooktime, secondary)) : 1; 331 if(sub > 1 && scale < 1) sub = int(ceilf(sub*scale)); 332 } 333 else if(!pressed) return false; 334 335 vec to, from; 336 vector<shotmsg> shots; 337 #define addshot(p) \ 338 { \ 339 shotmsg &s = shots.add(); \ 340 s.id = d->getprojid(); \ 341 s.pos = ivec(int(p.x*DMF), int(p.y*DMF), int(p.z*DMF)); \ 342 } 343 int rays = W2(weap, rays, secondary); 344 if(rays > 1 && W2(weap, cooked, secondary)&W_C_RAYS && W2(weap, cooktime, secondary) && scale < 1) 345 rays = max(1, int(ceilf(rays*scale))); 346 if(weap == W_MELEE || WF(false, weap, collide, secondary)&COLLIDE_HITSCAN) 347 { 348 if(weap == W_MELEE) 349 { 350 from = d->center(); 351 to = vec(from).add(vec(d->yaw*RAD, d->pitch*RAD).mul(d->radius*2.f)); 352 } 353 else 354 { 355 from = d->originpos(weap); 356 to = d->muzzlepos(weap); 357 } 358 loopi(rays) addshot(to); 359 } 360 else 361 { 362 from = d->muzzlepos(weap); 363 to = targ; 364 float m = accmod(d, W2(d->weapselect, cooked, true)&W_C_ZOOM && secondary); 365 float spread = WSP(weap, secondary, game::gamemode, game::mutators, m); 366 loopi(rays) 367 { 368 vec dest; 369 if(spread > 0) offsetray(from, to, spread, W2(weap, spreadz, secondary), dest); 370 else dest = to; 371 if(weaptype[weap].thrown[secondary ? 1 : 0] > 0) 372 dest.z += from.dist(dest)*weaptype[weap].thrown[secondary ? 1 : 0]; 373 addshot(dest); 374 } 375 } 376 projs::shootv(weap, secondary ? HIT_ALT : 0, sub, offset, scale, from, shots, d, true); 377 client::addmsg(N_SHOOT, "ri8iv", d->clientnum, lastmillis-game::maptime, weap, secondary ? HIT_ALT : 0, cooked, int(from.x*DMF), int(from.y*DMF), int(from.z*DMF), shots.length(), shots.length()*sizeof(shotmsg)/sizeof(int), shots.getbuf()); 378 379 return true; 380 } 381 shoot(gameent * d,vec & targ,int force)382 void shoot(gameent *d, vec &targ, int force) 383 { 384 if(!game::allowmove(d)) return; 385 bool secondary = physics::secondaryweap(d); 386 if(doshot(d, targ, d->weapselect, d->action[secondary ? AC_SECONDARY : AC_PRIMARY], secondary, force)) 387 if(!W2(d->weapselect, fullauto, secondary)) d->action[secondary ? AC_SECONDARY : AC_PRIMARY] = false; 388 } 389 preload()390 void preload() 391 { 392 loopi(W_ALL) 393 { 394 if(*weaptype[i].item) preloadmodel(weaptype[i].item); 395 if(*weaptype[i].vwep) preloadmodel(weaptype[i].vwep); 396 if(*weaptype[i].hwep) preloadmodel(weaptype[i].hwep); 397 } 398 } 399 } 400