diff --git a/config/menus_multiplayer.cfg b/config/menus_multiplayer.cfg index 6d5c98e2..9f51d56a 100644 --- a/config/menus_multiplayer.cfg +++ b/config/menus_multiplayer.cfg @@ -176,6 +176,12 @@ menuitemslider "Gravity Multiplier: " 50 500 "$matchgravity" 10 [ matchset match menuitemslider "Friction Multiplier: " 10 500 "$matchfriction" 10 [ matchset matchfriction $arg1 ] menuitem "" -1 menuitemcheckbox "Enable Assists: " "$matchassist" [ matchset matchassist $arg1 ] +menuitemcheckbox "Reload cancel on switch: " "$matchreloadcancel" [ matchset matchreloadcancel $arg1 ] +menuitemcheckbox "Knife+jump speed boost: " "$matchknifeboost" [ matchset matchknifeboost $arg1 ] +menuitemcheckbox "Medkit pickup item: " "$matchmedkit" [ matchset matchmedkit $arg1 ] +menuitemcheckbox "Slow backwards walk (70%): " "$matchbackwards" [ matchset matchbackwards $arg1 ] +menuitemcheckbox "Ceiling bump speed boost: " "$matchheadboost" [ matchset matchheadboost $arg1 ] +menuitemcheckbox "Source-style physics (bhop/air strafe): " "$matchsourcephysics" [ matchset matchsourcephysics $arg1 ] menuitemcheckbox "Explosive Deaths: " "$matchdeathexplosion" [ matchset matchdeathexplosion $arg1 ] menuitemslider " Blast Radius %: " 10 300 "$matchdeathradius" 10 [ matchset matchdeathradius $arg1 ] menuitemslider " Blast Power %: " 10 500 "$matchdeathpower" 10 [ matchset matchdeathpower $arg1 ] diff --git a/config/menus_settings.cfg b/config/menus_settings.cfg index 8c693080..00fb3f00 100644 --- a/config/menus_settings.cfg +++ b/config/menus_settings.cfg @@ -141,6 +141,7 @@ menuitemcheckbox [ Damage screen flash ] "$damagescreen" [ menuitemslider [ Damage maxroll: ] 0 (getvarrange max maxrolleffect) "$maxrolleffect" 1 [ maxrolleffect $arg1 ] menuitemcheckbox [ Team score counters ] "(= $hideteamscorehud 0)" [ hideteamscorehud (= $arg1 0) ] menuitemcheckbox [ KTF flag direction indicator ] "(! $hidektfindicator)" [ hidektfindicator (! $arg1) ] +menuitemcheckbox [ Grenade cook bar ] "$showgrenadebar" [ showgrenadebar $arg1 ] menuitemcheckbox [ Radar ] "(= $hideradar 0)" [ hideradar (= $arg1 0) ] menuitemslider [ Radar zoom: ] 5 500 [ $radarheight ] 5 [ radarheight $arg1 ] 1 menuitemslider [ Votes: ] 0 2 "$hidevote" ["Always show" "Show until you vote" "Never show"] [ hidevote $arg1 ] 1 @@ -327,6 +328,7 @@ menuitemkeyinput [Special action: ] altaction menuitemkeyinput [Knife attack: ] key_knifeattack menuitemkeyinput [Quick grenade throw: ] quicknadethrow menuitemkeyinput [Reload: ] reload +menuitemkeyinput [Use medkit: ] usemedkit menuitemkeyinput [Weapon menu: ] key_showmenuweapons menuitemkeyinput [Primary weapon: ] primary menuitemkeyinput [Secondary weapon: ] secondary diff --git a/config/resetbinds.cfg b/config/resetbinds.cfg index aa59ceed..ff35484a 100644 --- a/config/resetbinds.cfg +++ b/config/resetbinds.cfg @@ -24,6 +24,7 @@ alias key_voteno [ if $votepending [ && (vote 2) (echo you voted no) ] ] alias key_teamchat [ saycommand % ] alias key_commandline [ saycommand / ] alias key_prevweapon [ weapon (prevweapon) ] +alias key_usemedkit [ usemedkit ] alias key_pause [ if $connected togglepause [ paused (! $paused) ]] alias editkey_editmeta2 [ editmeta2down = 1 ; onrelease [ editmeta2down = 0 ] ] @@ -155,7 +156,7 @@ editbind F editkey_cubes_solid specbind F speckey_demo_togglespeed bind G quicknadethrow editbind G editkey_cubes_space -bind H [] +bind H key_usemedkit editbind H editkey_cubes_hf_floor bind I [] editbind I editkey_cubes_hf_ceiling diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 543dc6ee..35b9099d 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -1030,6 +1030,10 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) case SV_ARENAWIN: { int acn = getint(p); + static int lastarenawin = 0; + // a single round end can arrive several times in one burst: announce it once + if(lastarenawin && lastmillis - lastarenawin < 1500) break; + lastarenawin = lastmillis; playerent *alive = getclient(acn); conoutf("the round is over! next round in 5 seconds..."); if(m_botmode && acn==-2) hudoutf("the bots have won the round!"); diff --git a/source/src/entity.h b/source/src/entity.h index 83ee4932..80dc27c9 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -91,7 +91,7 @@ extern int matchdeathradius, matchdeathpower; // the values, sends them to every client when they change and to everybody who joins (SV_MATCHSETTINGS). // Offline/bot games have no server, so there you simply set them yourself. Defined in server.cpp (which is part of the client too). struct matchsettingdef { const char *name; int *var; int min, def, max; }; -#define NUMMATCHSETTINGS 14 +#define NUMMATCHSETTINGS 20 extern matchsettingdef matchsettings[NUMMATCHSETTINGS]; extern int matchsynced[NUMMATCHSETTINGS]; // client side: the values the server last announced extern void matchsettings_reset(); // everything back to the defaults @@ -263,6 +263,9 @@ struct poshist } }; +#define MEDKIT_MAX 2 +#define MEDKIT_HEAL 33 + class playerstate { public: @@ -272,8 +275,9 @@ public: bool akimbo; int ammo[NUMGUNS], mag[NUMGUNS], gunwait[NUMGUNS]; int pstatshots[NUMGUNS], pstatdamage[NUMGUNS]; + int medkits; // carryable health pickups when matchmedkit is on (3rd-slot style usable item) - playerstate() : armour(0), primary(GUN_ASSAULT), nextprimary(GUN_ASSAULT), akimbo(false) {} + playerstate() : armour(0), primary(GUN_ASSAULT), nextprimary(GUN_ASSAULT), akimbo(false), medkits(0) {} virtual ~playerstate() {} void resetstats() { loopi(NUMGUNS) pstatshots[i] = pstatdamage[i] = 0; } @@ -297,12 +301,13 @@ public: bool canpickup(int type) { + extern int matchmedkit; switch(type) { case I_CLIPS: return ammo[akimbo && ammo[GUN_AKIMBO]reloading && matchreloadcancel) + { + weaponsel->reloading = 0; + weaponsel->gunwait = 0; + // the 0.2s ready delay only applies to reloadable guns, so it can never sit in the way of + // grenade cooking (knife/grenade shots are not affected by reloading) - see grenades::attack() + if(reloadable_gun(w->type)) w->gunwait = max(w->gunwait, 200); + } weaponsel->ondeselecting(); weaponchanging = lastmillis; nextweaponsel = w; diff --git a/source/src/logs/debug/maprot_debug_verbose.txt b/source/src/logs/debug/maprot_debug_verbose.txt new file mode 100644 index 00000000..e69de29b diff --git a/source/src/logs/readmaps_log.txt b/source/src/logs/readmaps_log.txt new file mode 100644 index 00000000..a88088e3 --- /dev/null +++ b/source/src/logs/readmaps_log.txt @@ -0,0 +1,2 @@ + +#### 20261025_22.19.30 #### scanning all map directories... found 0 official maps, 0 servermaps and 0 maps in 'incoming' diff --git a/source/src/physics.cpp b/source/src/physics.cpp index 3aaa8b55..fef8a46a 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -5,6 +5,10 @@ #include "cube.h" +extern int matchbackwards; +extern int matchknifeboost; +extern int matchheadboost; + float raycube(const vec &o, const vec &ray, vec &surface) { surface = vec(0, 0, 0); @@ -341,6 +345,17 @@ bool collide(physent *d, bool spawn, float drop, float rise) { if(spacehi<-0.1) return true; // hack alert! if(spacelo>0.1f) d->o.z = hi-d->aboveeye; // glue to ceiling if in midair + // head bump on a ceiling: small horizontal speed boost (matchheadboost) + if(matchheadboost && d->vel.z > 0.5f && (d->type == ENT_PLAYER || d->type == ENT_BOT) && d->state == CS_ALIVE) + { + float hspeed = sqrtf(d->vel.x*d->vel.x + d->vel.y*d->vel.y); + if(hspeed > 0.05f) + { + float boost = 1.06f; + d->vel.x *= boost; + d->vel.y *= boost; + } + } d->vel.z = 0; // cancel out jumping velocity } @@ -398,6 +413,52 @@ FVARP(flyspeed, 1.0, 2.0, 5.0); extern int matchgravity; extern int matchspeedmul; extern int matchfriction; +extern int matchsourcephysics; + +// Source (Quake lineage) style movement, used when the "matchsourcephysics" match setting is on (off by default). +// Ground: constant acceleration towards the wish direction plus Source's friction model. +// Air: the classic air-strafe model - only a small part of the wish speed counts (sv_air_max_wishspeed), +// so turning while strafing in the air keeps adding speed instead of just steering. +// No friction in the air, and jumping the moment you land skips ground friction: that is what makes +// bunny hopping work. Velocities here are in the same normalized unit as physent::vel (1.0 = maxspeed). +#define SRC_ACCELERATE 10.0f // sv_accelerate +#define SRC_AIRACCELERATE 11.0f // sv_airaccelerate (11 instead of 10: our physics runs at 200Hz instead of + // Source's 66Hz, which makes small impulses add up to less speed gain - + // this brings the gained speed per second back in line with Source) +#define SRC_FRICTION 5.2f // sv_friction +#define SRC_STOPSPEED 0.25f // sv_stopspeed (80 of sv_maxspeed 320) +#define SRC_AIRWISHCAP 0.09375f // sv_air_max_wishspeed (30 of sv_maxspeed 320) + +static inline void sourcefriction(vec &vel, float dt) +{ + float speed = vel.magnitudexy(); + if(speed < 0.01f) return; + float control = max(speed, SRC_STOPSPEED); + float newspeed = max(speed - control*SRC_FRICTION*dt, 0.0f) / speed; + vel.x *= newspeed; + vel.y *= newspeed; +} + +static inline void sourceaccelerate(vec &vel, const vec &wishdir, float wishspeed, float dt) +{ + float currentspeed = vel.x*wishdir.x + vel.y*wishdir.y; + float addspeed = wishspeed - currentspeed; + if(addspeed <= 0.0f) return; + float accelspeed = min(SRC_ACCELERATE*wishspeed*dt, addspeed); + vel.x += accelspeed*wishdir.x; + vel.y += accelspeed*wishdir.y; +} + +static inline void sourceairaccelerate(vec &vel, const vec &wishdir, float wishspeed, float dt) +{ + float wishspd = min(wishspeed, SRC_AIRWISHCAP); + float currentspeed = vel.x*wishdir.x + vel.y*wishdir.y; + float addspeed = wishspd - currentspeed; + if(addspeed <= 0.0f) return; + float accelspeed = min(SRC_AIRACCELERATE*wishspeed*dt, addspeed); + vel.x += accelspeed*wishdir.x; + vel.y += accelspeed*wishdir.y; +} void moveplayer(physent *pl, int moveres, bool local, int curtime) { @@ -405,6 +466,7 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) const bool editfly = pl->state==CS_EDITING; const bool specfly = pl->type==ENT_PLAYER && ((playerent *)pl)->spectatemode==SM_FLY; const bool isfly = editfly || specfly; + const bool srcphys = matchsourcephysics && pl->type==ENT_PLAYER; // players only: bots keep normal movement vec d; // vector of direction we ideally want to move in @@ -463,8 +525,11 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) const float friction = (water ? 20.0f : (pl->onfloor || isfly ? 6.0f : (pl->onladder ? 1.5f : 30.0f))) * 100.0f / clamp(matchfriction, 10, 500); const float fpsfric = max(friction/curtime*20.0f, 1.0f); - d.x = (float)(move*cosf(RAD*(pl->yaw-90))); - d.y = (float)(move*sinf(RAD*(pl->yaw-90))); + // walking backwards is slower (70%): scale the backward input itself, + // so backpedalling (and only backpedalling) loses speed + float backmul = (matchbackwards && move < 0 && !isfly && !water) ? 0.7f : 1.0f; + d.x = (float)(move*cosf(RAD*(pl->yaw-90)))*backmul; + d.y = (float)(move*sinf(RAD*(pl->yaw-90)))*backmul; d.z = 0.0f; if(isfly || water) @@ -479,9 +544,34 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) float curfullspeed = d.magnitudexy(); - pl->vel.mul(fpsfric-1.0f); // slowly apply friction and direction to velocity, gives a smooth movement - pl->vel.add(d); - pl->vel.div(fpsfric); + if(srcphys && !isfly && !water && !pl->onladder) + { // Source style: accelerate towards the wish direction, friction on the ground only + vec wishdir(d.x, d.y, 0); + float wishspeed = wishdir.magnitudexy(); + if(wishspeed > 1e-4f) { wishdir.x /= wishspeed; wishdir.y /= wishspeed; } + else { wishdir.x = wishdir.y = 0; wishspeed = 0; } + vec vel(pl->vel.x, pl->vel.y, 0); + const float dt = curtime/1000.0f; + // jumping straight off the floor skips friction: bunny hopping + if(pl->onfloor && !pl->jumpnext) + { + sourcefriction(vel, dt); + sourceaccelerate(vel, wishdir, wishspeed, dt); + } + else if(wishspeed > 1e-4f) sourceairaccelerate(vel, wishdir, wishspeed, dt); + pl->vel.x = vel.x; + pl->vel.y = vel.y; + // The classic model lerps the whole velocity vector towards the wish direction, which also bleeds + // off the vertical part of the jump impulse (the jump is a constant upward velocity here). Keep that + // exact decay, or jumps go far too high and float: the horizontal model above is the only difference. + pl->vel.z *= (fpsfric-1.0f)/fpsfric; + } + else + { // classic AssaultCube movement: slowly apply friction and direction to velocity, gives a smooth movement + pl->vel.mul(fpsfric-1.0f); + pl->vel.add(d); + pl->vel.div(fpsfric); + } d = pl->vel; d.mul(speed); @@ -530,9 +620,19 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) { pl->jumpd = true; pl->jumpnext = false; - bool doublejump = pl->lastjump && lastmillis - pl->lastjump < 250 && pl->strafe != 0 && pl->o.z - pl->eyeheight - pl->lastjumpheight > 0.2f; + bool doublejump = !srcphys && pl->lastjump && lastmillis - pl->lastjump < 250 && pl->strafe != 0 && pl->o.z - pl->eyeheight - pl->lastjumpheight > 0.2f; pl->lastjumpheight = pl->o.z - pl->eyeheight; pl->vel.z = 2.0f; // physics impulse upwards + // knife + jump at the same time: very slight horizontal speed boost (matchknifeboost) + if(matchknifeboost && pl->type == ENT_PLAYER) + { + playerent *pp = (playerent *)pl; + if(pp->weaponsel && pp->weaponsel->type == GUN_KNIFE && (pp->attacking || lastmillis - pp->lastaction < 250)) + { + pl->vel.x *= 1.10f; + pl->vel.y *= 1.10f; + } + } if(doublejump && curfullspeed > 0.1f) // more velocity on double jump { pl->vel.mul(1.25f / max(pl->vel.magnitudexy() / curfullspeed, 1.0f)); @@ -569,6 +669,9 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) } const float gravity = 20.0f * (matchgravity ? matchgravity / 100.0f : 1.0f); + // NOTE: the timeinair term below is what makes AssaultCube players come back down at all (the jump + // is a constant upward velocity, so the increasing drop is the "gravity"). Source physics only + // changes the horizontal model - the vertical one has to stay exactly like this, or jumps turn floaty. float dropf = (gravity-1)+pl->timeinair/15.0f; // incorrect, but works fine if(water) { dropf = 5; pl->timeinair = 0; } // float slowly down in water if(pl->onladder) { dropf = 0; pl->timeinair = 0; } diff --git a/source/src/private/entropy.dat b/source/src/private/entropy.dat new file mode 100644 index 00000000..b5e6f04d Binary files /dev/null and b/source/src/private/entropy.dat differ diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index d3a010e7..ce89c35d 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -621,7 +621,7 @@ static const int msgsizes[] = // size inclusive message token, 0 f SV_PAUSEMODE, 1, SV_GETVITA, 2, SV_VITADATA, 0, SV_DEATHBLAST, 2, - SV_MATCHSET, 3, + SV_MATCHSET, 3, SV_MEDKIT, 1, -1 }; diff --git a/source/src/protocol.h b/source/src/protocol.h index c4e1d4dd..fbd81d8a 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -7,7 +7,7 @@ #define CUBE_SERVINFO_PORT(serverport) (serverport+1) #define CUBE_SERVINFO_TO_SERV_PORT(servinfoport) (servinfoport-1) #define CLIENTIPCENSOR 12 // blank out the last 12 bits of client's IPs before telling them to others (compromise between "16" from AC 0.9x and "8" from AC 1.1) -#define PROTOCOL_VERSION 1303 // bump when protocol changes (use negative numbers for mods!) - FORK VERSION +#define PROTOCOL_VERSION 1304 // bump when protocol changes (use negative numbers for mods!) - FORK VERSION #define DEMO_VERSION 2 // bump when demo format changes #define DEMO_MAGIC "ASSAULTCUBE_DEMO" #define DEMO_MINTIME 10000 // don't keep demo recordings with less than 10 seconds @@ -53,6 +53,7 @@ enum SV_GETVITA, SV_VITADATA, SV_DEATHBLAST, SV_MATCHSET, SV_MATCHSETTINGS, + SV_MEDKIT, SV_NUM }; diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index c081cb41..22bb3a8e 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -88,6 +88,7 @@ VARP(hideeditinfopanel, 0, 0, 1); VARP(hidevote, 0, 0, 2); VARP(hidehudmsgs, 0, 0, 1); VARP(hidehudequipment, 0, 0, 1); +VARP(showgrenadebar, 0, 1, 1); VARP(hideconsole, 0, 0, 1); VARP(hidespecthud, 0, 0, 1); VAR(showmap, 0, 0, 1); @@ -322,6 +323,7 @@ void drawktfindicator(playerent *p) } extern int oldfashionedgunstats; +extern int matchmedkit; int getprevweaponsel(playerent *p) // get previous weapon or, if we don't know it, get the primary { @@ -348,6 +350,58 @@ void drawequipicons(playerent *p) glEnable(GL_BLEND); } +// grenade cooking indicator: a thin ring around the crosshair that fills up white->red +// while the fuse burns (same flat-shaded style as the damage direction indicator) +void drawgrenadecookring(playerent *p) +{ + if(!showgrenadebar || p->state != CS_ALIVE) return; + if(!p->weapons[GUN_GRENADE]) return; + grenades *g = (grenades *)p->weapons[GUN_GRENADE]; + extern int lastmillis; + float progress = -1.0f; + if(p == player1) + { + if(g->state == GST_INHAND && g->inhandnade) + progress = clamp((lastmillis - g->inhandnade->millis) / 2000.0f, 0.0f, 1.0f); + } + else if(g->state == GST_INHAND) + { + progress = clamp((lastmillis - g->throwmillis) / 2000.0f, 0.0f, 1.0f); + } + if(progress < 0.0f) return; + const float TAU = 6.2831853f; + float cx = VIRTW/2, cy = VIRTH/2; + float r0 = 30.0f, r1 = 33.5f; + const int segs = 40; + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // faint full-circle track + glColor4f(1.0f, 1.0f, 1.0f, 0.18f); + glBegin(GL_TRIANGLE_STRIP); + loopi(segs+1) + { + float a = -PI/2.0f + (i/(float)segs)*TAU; + float c = cosf(a), s = sinf(a); + glVertex2f(cx + c*r1, cy + s*r1); + glVertex2f(cx + c*r0, cy + s*r0); + } + glEnd(); + // burnt fuse: white -> red as it cooks + int n = max(2, int(progress*segs)); + glColor4f(1.0f, 1.0f - progress*0.85f, 1.0f - progress*0.9f, 0.9f); + glBegin(GL_TRIANGLE_STRIP); + loopi(n+1) + { + float a = -PI/2.0f + (i/(float)segs)*TAU; + float c = cosf(a), s = sinf(a); + glVertex2f(cx + c*r1, cy + s*r1); + glVertex2f(cx + c*r0, cy + s*r0); + } + glEnd(); + glEnable(GL_TEXTURE_2D); +} + void drawradarent(float x, float y, float yaw, int col, int row, float iconsize, bool pulse, const char *label = NULL, ...) PRINTFARGS(8, 9); void drawradarent(float x, float y, float yaw, int col, int row, float iconsize, bool pulse, const char *label, ...) @@ -818,6 +872,14 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat if(damageindicatorplayer == p || (is_spect && getclient(player1->followplayercn) == damageindicatorplayer)) drawdmgindicator(p); if(p->state==CS_ALIVE && !hidehudequipment) drawequipicons(p); + if(p->state==CS_ALIVE && !editmode && !showmap) + { + glLoadIdentity(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + drawgrenadecookring(p); + glLoadIdentity(); + glOrtho(0, VIRTW*2, VIRTH*2, 0, -1, 1); + } if(!editmode) { @@ -1102,6 +1164,12 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat { pushfont("huddigits"); draw_textf("%d", HUDPOS_HEALTH + HUDPOS_NUMBERSPACING, 823, p->health); + if(matchmedkit && p->medkits > 0) // medkit stock, same digits as the health number + { + defformatstring(hp)("%d", p->health); + int hw = text_width(hp); + draw_textf("\f2%d", HUDPOS_HEALTH + HUDPOS_NUMBERSPACING + hw + 14, 823, p->medkits); + } if(p->armour) draw_textf("%d", HUDPOS_ARMOUR + HUDPOS_NUMBERSPACING, 823, p->armour); if(p->weaponsel && valid_weapon(p->weaponsel->type)) { diff --git a/source/src/server.cpp b/source/src/server.cpp index 6623ede2..c06b182d 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -47,6 +47,14 @@ int matchdeathpower = 100; // blast damage, % of the default (75% of a grenade // movement/weapon feel: the clients apply these, the server only hands them out int matchrecoilmul = 100, matchspreadmul = 100, matchspeedmul = 100, matchgravity = 100, matchfriction = 100; +// movement/gadget feel, part 2 +int matchreloadcancel = 1; // allow cancelling reloads by switching weapons (0.2s switch delay) +int matchknifeboost = 0; // knife attack + jump at the same time gives a slight speed boost +int matchmedkit = 0; // health pickups become carryable medkits (use with "usemedkit") +int matchbackwards = 1; // walking backwards moves at 70% speed +int matchheadboost = 1; // bumping your head on a ceiling gives a small speed boost +int matchsourcephysics = 0; // Source/Quake style movement: air strafing, bunny hopping, no air friction + // The order of this table is part of the protocol (SV_MATCHSET sends an index, SV_MATCHSETTINGS all values in this order). matchsettingdef matchsettings[NUMMATCHSETTINGS] = { @@ -63,7 +71,13 @@ matchsettingdef matchsettings[NUMMATCHSETTINGS] = { "matchspawnprimary", &matchspawnprimary, -1, -1, NUMGUNS-1 }, { "matchspawnsecondary", &matchspawnsecondary, -1, -1, NUMGUNS-1 }, { "matchspawntertiary", &matchspawntertiary, -1, -1, NUMGUNS-1 }, - { "matchspawnquaternary", &matchspawnquaternary,-1, -1, NUMGUNS-1 } + { "matchspawnquaternary", &matchspawnquaternary,-1, -1, NUMGUNS-1 }, + { "matchreloadcancel", &matchreloadcancel, 0, 1, 1 }, + { "matchknifeboost", &matchknifeboost, 0, 0, 1 }, + { "matchmedkit", &matchmedkit, 0, 0, 1 }, + { "matchbackwards", &matchbackwards, 0, 1, 1 }, + { "matchheadboost", &matchheadboost, 0, 1, 1 }, + { "matchsourcephysics", &matchsourcephysics, 0, 0, 1 } }; int matchsynced[NUMMATCHSETTINGS]; @@ -3909,6 +3923,18 @@ void process(ENetPacket *packet, int sender, int chan) break; } + case SV_MEDKIT: + { + if(matchmedkit && cl && cl->state.state == CS_ALIVE && cl->state.medkits > 0 && cl->state.health < 100) + { + cl->state.medkits--; + cl->state.health += 33; + if(cl->state.health > 100) cl->state.health = 100; + sendf(-1, 1, "ri7", SV_DAMAGE, sender, sender, GUN_KNIFE, 0, cl->state.armour, cl->state.health); + } + break; + } + case SV_PING: sendf(sender, 1, "ii", SV_PONG, getint(p)); break; diff --git a/source/src/server.h b/source/src/server.h index d7cb0059..8c960036 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -457,7 +457,7 @@ const char *messagenames[SV_NUM] = "SV_EXTENSION", "SV_MAPIDENT", "SV_DEMOCHECKSUM", "SV_DEMOSIGNATURE", "SV_PAUSEMODE", - "SV_GETVITA", "SV_VITADATA", "SV_DEATHBLAST", "SV_MATCHSET", "SV_MATCHSETTINGS" + "SV_GETVITA", "SV_VITADATA", "SV_DEATHBLAST", "SV_MATCHSET", "SV_MATCHSETTINGS", "SV_MEDKIT" }; const char *entnames[] = diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 789834ab..069b4c58 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -127,6 +127,7 @@ void quicknadethrow(bool on) if(player1->state != CS_ALIVE) return; if(on) { + quicknade = false; // a new quick-nade press always starts from a clean state if(player1->weapons[GUN_GRENADE]->mag > 0) { if(player1->weaponsel->type != GUN_GRENADE) selectweapon(player1->weapons[GUN_GRENADE]); @@ -173,6 +174,22 @@ void tryreload(playerent *p) COMMANDF(reload, "", () { tryreload(player1); }); +extern int matchmedkit; + +void tryusemedkit(playerent *p) +{ + if(!p || p->state != CS_ALIVE || ispaused || intermission) return; + if(!matchmedkit || p->medkits <= 0 || p->health >= 100) return; + p->medkits--; + p->health += MEDKIT_HEAL; + if(p->health > 100) p->health = 100; + audiomgr.playsoundc(S_ITEMHEALTH, p); + addmsg(SV_MEDKIT, ""); +} + +COMMANDF(usemedkit, "", () { tryusemedkit(player1); }); +COMMANDF(medkitcount, "", () { intret(player1 ? player1->medkits : 0); }); + void createrays(const vec &from, const vec &to) // create random spread of rays for the shotgun { vec dir = vec(from).sub(to); @@ -1046,6 +1063,7 @@ VARP(righthanded, 0, 1, 1); // flowtron 20090727 // the server, see matchset below) and everybody else's local changes are undone right away, so nobody can give themselves // no recoil or low gravity. In offline/bot games you're the host and may set them freely. extern int matchrecoilmul, matchspreadmul; +extern int matchreloadcancel; static void matchsettingchanged() { @@ -1145,7 +1163,7 @@ void weapon::renderaimhelp(bool teamwarning) int weapon::dynspread() { return int(info.spread * matchspreadmul / 100.0f); } float weapon::dynrecoil() { return info.recoil * matchrecoilmul / 100.0f; } bool weapon::selectable() { return this != owner->weaponsel && owner->state == CS_ALIVE && !owner->weaponchanging; } -bool weapon::deselectable() { return !reloading; } +bool weapon::deselectable() { return !reloading || matchreloadcancel; } void weapon::equipplayer(playerent *pl) { @@ -1400,7 +1418,8 @@ void grenades::thrownade(const vec &vel) updatelastaction(owner); state = GST_THROWING; if(this==owner->weaponsel) owner->attacking = false; - if(quicknade && owner->weaponsel->type == GUN_GRENADE) selectweapon(owner->prevweaponsel); + if(quicknade && owner->weaponsel->type == GUN_GRENADE && owner->prevweaponsel && owner->prevweaponsel != this) + selectweapon(owner->prevweaponsel); } void grenades::dropnade()