diff --git a/config/defaults.cfg b/config/defaults.cfg index a44c337a..047bd776 100644 --- a/config/defaults.cfg +++ b/config/defaults.cfg @@ -71,6 +71,8 @@ alias pf [ votemap 6 $arg1 ] alias lss [ votemap 9 $arg1 ] alias osok [ votemap 10 $arg1 ] alias tosok [ votemap 11 $arg1 ] +alias htf [ votemap 13 $arg1 ] +alias ktf [ votemap 14 $arg1 ] //--------------------- editing ------------------------------- diff --git a/config/menus.cfg b/config/menus.cfg index 634fcef1..8322d213 100755 --- a/config/menus.cfg +++ b/config/menus.cfg @@ -37,7 +37,19 @@ menudirlist "packages/maps" "cgz" "map $arg1" newmenu maps_ctf -// Add maps to the below list to include them in the CTF map menu. +// Add maps to the below list to include them in the CTF map menu. Maps should contain flags here. +genmapitems "ac_depot ac_desert3 ac_elevation ac_keller ac_mines ac_power ac_shine ac_sunset ac_urban" +menuitem """" +menuitem "Show all maps.." [showmenu "all maps"] + +newmenu maps_htf +// Add maps to the below list to include them in the HTF map menu. +genmapitems "ac_arctic ac_complex ac_depot ac_desert ac_desert2 ac_desert3 ac_elevation ac_keller ac_mines ac_power ac_scaffold ac_shine ac_snow ac_sunset ac_toxic ac_urban" +menuitem """" +menuitem "Show all maps..." [ showmenu "all maps" ] + +newmenu maps_ktf +// Add maps to the below list to include them in the KTF map menu. Maps should contain flags here. genmapitems "ac_depot ac_desert3 ac_elevation ac_keller ac_mines ac_power ac_shine ac_sunset ac_urban" menuitem """" menuitem "Show all maps.." [showmenu "all maps"] diff --git a/config/menus_multiplayer.cfg b/config/menus_multiplayer.cfg index f6fe2fbc..7b4d71bb 100644 --- a/config/menus_multiplayer.cfg +++ b/config/menus_multiplayer.cfg @@ -26,6 +26,8 @@ menuitem "Deathmatch" "mode 2; showmenu maps" menuitem "Team Survivor" "mode 4; showmenu maps" menuitem "Survivor" "mode 3; showmenu maps" menuitem "Capture the Flag" "mode 5; showmenu maps_ctf" +menuitem "Hunt the Flag" "mode 13; showmenu maps_htf" +menuitem "Keep the Flag" "mode 14; showmenu maps_ktf" menuitem "Pistol Frenzy" "mode 6; showmenu maps" menuitem "Last Swiss Standing" "mode 9; showmenu maps" menuitem "One Shot, One Kill" "mode 10; showmenu maps" diff --git a/packages/misc/htficons.png b/packages/misc/htficons.png new file mode 100755 index 00000000..7baf68e1 Binary files /dev/null and b/packages/misc/htficons.png differ diff --git a/packages/misc/ktficons.png b/packages/misc/ktficons.png new file mode 100755 index 00000000..ba27c598 Binary files /dev/null and b/packages/misc/ktficons.png differ diff --git a/packages/misc/radaricons.png b/packages/misc/radaricons.png index 5804f474..209c2883 100644 Binary files a/packages/misc/radaricons.png and b/packages/misc/radaricons.png differ diff --git a/packages/models/pickups/flags/CLA_htf/md3.cfg b/packages/models/pickups/flags/CLA_htf/md3.cfg new file mode 100644 index 00000000..27f36645 --- /dev/null +++ b/packages/models/pickups/flags/CLA_htf/md3.cfg @@ -0,0 +1,3 @@ +md3load tris.md3 +md3skin mesh skin.jpg +md3anim flag 0 7 \ No newline at end of file diff --git a/packages/models/pickups/flags/CLA_htf/skin.jpg b/packages/models/pickups/flags/CLA_htf/skin.jpg new file mode 100755 index 00000000..8a30c502 Binary files /dev/null and b/packages/models/pickups/flags/CLA_htf/skin.jpg differ diff --git a/packages/models/pickups/flags/CLA_htf/tris.md3 b/packages/models/pickups/flags/CLA_htf/tris.md3 new file mode 100644 index 00000000..b4849f30 Binary files /dev/null and b/packages/models/pickups/flags/CLA_htf/tris.md3 differ diff --git a/packages/models/pickups/flags/RVSF_htf/md3.cfg b/packages/models/pickups/flags/RVSF_htf/md3.cfg new file mode 100644 index 00000000..27f36645 --- /dev/null +++ b/packages/models/pickups/flags/RVSF_htf/md3.cfg @@ -0,0 +1,3 @@ +md3load tris.md3 +md3skin mesh skin.jpg +md3anim flag 0 7 \ No newline at end of file diff --git a/packages/models/pickups/flags/RVSF_htf/skin.jpg b/packages/models/pickups/flags/RVSF_htf/skin.jpg new file mode 100755 index 00000000..3006bece Binary files /dev/null and b/packages/models/pickups/flags/RVSF_htf/skin.jpg differ diff --git a/packages/models/pickups/flags/RVSF_htf/tris.md3 b/packages/models/pickups/flags/RVSF_htf/tris.md3 new file mode 100644 index 00000000..69aa1f12 Binary files /dev/null and b/packages/models/pickups/flags/RVSF_htf/tris.md3 differ diff --git a/packages/models/pickups/flags/ktf/md3.cfg b/packages/models/pickups/flags/ktf/md3.cfg new file mode 100644 index 00000000..27f36645 --- /dev/null +++ b/packages/models/pickups/flags/ktf/md3.cfg @@ -0,0 +1,3 @@ +md3load tris.md3 +md3skin mesh skin.jpg +md3anim flag 0 7 \ No newline at end of file diff --git a/packages/models/pickups/flags/ktf/skin.jpg b/packages/models/pickups/flags/ktf/skin.jpg new file mode 100755 index 00000000..b6e23a05 Binary files /dev/null and b/packages/models/pickups/flags/ktf/skin.jpg differ diff --git a/packages/models/pickups/flags/ktf/tris.md3 b/packages/models/pickups/flags/ktf/tris.md3 new file mode 100644 index 00000000..69aa1f12 Binary files /dev/null and b/packages/models/pickups/flags/ktf/tris.md3 differ diff --git a/packages/models/pickups/flags/small_CLA_htf/md2.cfg b/packages/models/pickups/flags/small_CLA_htf/md2.cfg new file mode 100644 index 00000000..8185e92d --- /dev/null +++ b/packages/models/pickups/flags/small_CLA_htf/md2.cfg @@ -0,0 +1,2 @@ +mdlscale 60 + diff --git a/packages/models/pickups/flags/small_CLA_htf/skin.jpg b/packages/models/pickups/flags/small_CLA_htf/skin.jpg new file mode 100755 index 00000000..329762d8 Binary files /dev/null and b/packages/models/pickups/flags/small_CLA_htf/skin.jpg differ diff --git a/packages/models/pickups/flags/small_CLA_htf/tris.md2 b/packages/models/pickups/flags/small_CLA_htf/tris.md2 new file mode 100644 index 00000000..06256f47 Binary files /dev/null and b/packages/models/pickups/flags/small_CLA_htf/tris.md2 differ diff --git a/packages/models/pickups/flags/small_RVSF_htf/md2.cfg b/packages/models/pickups/flags/small_RVSF_htf/md2.cfg new file mode 100644 index 00000000..8d01cf63 --- /dev/null +++ b/packages/models/pickups/flags/small_RVSF_htf/md2.cfg @@ -0,0 +1 @@ +mdlscale 60 diff --git a/packages/models/pickups/flags/small_RVSF_htf/skin.jpg b/packages/models/pickups/flags/small_RVSF_htf/skin.jpg new file mode 100755 index 00000000..a3944dc9 Binary files /dev/null and b/packages/models/pickups/flags/small_RVSF_htf/skin.jpg differ diff --git a/packages/models/pickups/flags/small_RVSF_htf/tris.md2 b/packages/models/pickups/flags/small_RVSF_htf/tris.md2 new file mode 100644 index 00000000..06256f47 Binary files /dev/null and b/packages/models/pickups/flags/small_RVSF_htf/tris.md2 differ diff --git a/packages/models/pickups/flags/small_ktf/md2.cfg b/packages/models/pickups/flags/small_ktf/md2.cfg new file mode 100644 index 00000000..8d01cf63 --- /dev/null +++ b/packages/models/pickups/flags/small_ktf/md2.cfg @@ -0,0 +1 @@ +mdlscale 60 diff --git a/packages/models/pickups/flags/small_ktf/skin.jpg b/packages/models/pickups/flags/small_ktf/skin.jpg new file mode 100755 index 00000000..df61188e Binary files /dev/null and b/packages/models/pickups/flags/small_ktf/skin.jpg differ diff --git a/packages/models/pickups/flags/small_ktf/tris.md2 b/packages/models/pickups/flags/small_ktf/tris.md2 new file mode 100644 index 00000000..06256f47 Binary files /dev/null and b/packages/models/pickups/flags/small_ktf/tris.md2 differ diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index c617c88e..2d762357 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -93,7 +93,7 @@ int smallerteam() void changeteam(int team, bool respawn) // force team and respawn { c2sinit = false; - if(m_ctf) tryflagdrop(NULL); + if(m_flags) tryflagdrop(false); filtertext(player1->team, team_string(team), false, MAXTEAMLEN); if(respawn) addmsg(SV_CHANGETEAM, "r"); } @@ -281,8 +281,8 @@ void addsleep(char *msec, char *cmd) s.cmd = newstring(cmd); } -void resetsleep() -{ +void resetsleep() +{ loopv(sleeps) DELETEA(sleeps[i].cmd); sleeps.setsize(0); } @@ -351,7 +351,7 @@ void findplayerstart(playerent *d, bool mapcenter, int arenaspawn) loopi(arenaspawn) x = findentity(PLAYERSTART, x+1, type); if(x >= 0) e = &ents[x]; } - else if(m_teammode || m_arena) + else if((m_teammode || m_arena) && !m_ktf) // ktf uses ffa spawns { loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1, type); if(spawncycle >= 0) e = &ents[spawncycle]; @@ -362,7 +362,7 @@ void findplayerstart(playerent *d, bool mapcenter, int arenaspawn) loopi(r) { - spawncycle = findentity(PLAYERSTART, spawncycle+1); + spawncycle = m_ktf && numspawn[2] > 5 ? findentity(PLAYERSTART, spawncycle+1, 100) : findentity(PLAYERSTART, spawncycle+1); if(spawncycle < 0) continue; float dist = nearestenemy(vec(ents[spawncycle].x, ents[spawncycle].y, ents[spawncycle].z), d->team); if(!e || dist < 0 || (bestdist >= 0 && dist > bestdist)) { e = &ents[spawncycle]; bestdist = dist; } @@ -419,7 +419,7 @@ bool tryrespawn() player1->respawnoffset = lastmillis; // count again } - int respawnmillis = player1->respawnoffset+(m_arena ? 0 : (m_ctf ? 5000 : 2000)); + int respawnmillis = player1->respawnoffset+(m_arena ? 0 : (m_flags ? 5000 : 2000)); if(lastmillis>respawnmillis) { @@ -480,8 +480,6 @@ void dokill(playerent *pl, playerent *act, bool gib) else outf("\f2%s %s %s", aname, death, pname); } - if(m_ctf && pl==player1) tryflagdrop(pl!=act && isteam(act->team, pl->team)); - if(gib) { if(pl!=act && act->weaponsel->type == GUN_SNIPER) playsound(S_HEADSHOT, SP_LOW); @@ -556,9 +554,18 @@ entity flagdummies[2]; // in case the map does not provide flags void preparectf(bool cleanonly=false) { - loopi(2) flaginfos[i].flag = &flagdummies[i]; + loopi(2) flaginfos[i].flagent = &flagdummies[i]; if(!cleanonly) { + loopi(2) + { + flaginfo &f = flaginfos[i]; + f.ack = false; + f.actor = NULL; + f.actor_cn = -1; + f.team = i; + f.state = m_ktf ? CTFF_IDLE : CTFF_INBASE; + } loopv(ents) { entity &e = ents[i]; @@ -567,12 +574,7 @@ void preparectf(bool cleanonly=false) e.spawned = true; if(e.attr2>2) { conoutf("\f3invalid ctf-flag entity (%i)", i); e.attr2 = 0; } flaginfo &f = flaginfos[e.attr2]; - f.ack = false; - f.actor = NULL; - f.actor_cn = -1; - f.team = e.attr2; - f.flag = &e; - f.state = CTFF_INBASE; + f.flagent = &e; f.originalpos.x = (float) e.x; f.originalpos.y = (float) e.y; f.originalpos.z = (float) e.z; @@ -594,7 +596,7 @@ void startmap(const char *name) // called just after a map load // End add by Rick clearbounceents(); resetspawns(); - if(m_ctf) preparectf(); + if(m_flags) preparectf(); particlereset(); suicided = -1; spawncycle = -1; @@ -625,11 +627,8 @@ void startmap(const char *name) // called just after a map load void suicide() { - if(player1->state!=CS_ALIVE) return; - if(!m_mp(gamemode)) dokill(player1, player1); - else if(suicided!=player1->lifesequence) + if(player1->state == CS_ALIVE && suicided!=player1->lifesequence) { - tryflagdrop(false); addmsg(SV_SUICIDE, "r"); suicided = player1->lifesequence; } @@ -644,7 +643,7 @@ void flagmsg(int flag, int action) flaginfo &f = flaginfos[flag]; if(!f.actor || !f.ack) return; bool own = flag == team_int(player1->team), firstperson = f.actor == player1; - const char *teamstr = flag == team_int(player1->team) ? "your" : "the enemy"; + const char *teamstr = m_ktf ? "the" : own ? "your" : "the enemy"; switch(action) { @@ -653,53 +652,77 @@ void flagmsg(int flag, int action) playsound(S_FLAGPICKUP, SP_HIGH); if(firstperson) { - conoutf("\f2you got the enemy flag"); - musicsuggest(M_FLAGGRAB, 90*1000, true); + conoutf("\f2you got the %sflag", m_ctf ? "enemy " : ""); + musicsuggest(M_FLAGGRAB, m_ctf ? 90*1000 : 900*1000, true); } else conoutf("\f2%s got %s flag", colorname(f.actor), teamstr); break; } case SV_FLAGDROP: + case SV_FLAGLOST: { + const char *droplost = action == SV_FLAGLOST ? "lost" : "dropped"; playsound(S_FLAGDROP, SP_HIGH); if(firstperson) { - conoutf("\f2you lost the flag"); + conoutf("\f2you %s the flag", droplost); musicfadeout(M_FLAGGRAB); } - else conoutf("\f2%s lost %s flag", colorname(f.actor), teamstr); + else conoutf("\f2%s %s %s flag", colorname(f.actor), droplost, teamstr); break; } case SV_FLAGRETURN: { playsound(S_FLAGRETURN, SP_HIGH); - if(firstperson) conoutf("\f2you returned your flag"); - else conoutf("\f2%s returned %s flag", colorname(f.actor), teamstr); + if(m_ctf) + { + if(firstperson) conoutf("\f2you returned your flag"); + else conoutf("\f2%s returned %s flag", colorname(f.actor), teamstr); + } + else if(m_htf) + { + conoutf("\f2%s failed to score (own team flag not taken)", firstperson ? "you" : colorname(f.actor)); + } + else + { //ktf + conoutf("\f2%s lost the flag", firstperson ? "you" : colorname(f.actor)); + if(firstperson) musicfadeout(M_FLAGGRAB); + } break; } case SV_FLAGSCORE: { - playsound(S_FLAGSCORE, SP_HIGH); - if(firstperson) + playsound(m_ktf ? S_VOTEPASS : S_FLAGSCORE, SP_HIGH); // need better ktf sound here + if(m_ktf) + { + const char *ta = firstperson ? "you have" : colorname(f.actor); + const char *tb = firstperson ? "" : " has"; + int m = f.stolentime / 60; + if(m) + conoutf("%s%s been keeping the flag for %d minute%s %d seconds now", ta, tb, m, m == 1 ? "" : "s", f.stolentime % 60); + else + conoutf("%s%s been keeping the flag for %d seconds now", ta, tb, f.stolentime); + } + else if(firstperson) { conoutf("\f2you scored"); - addmsg(SV_FLAGS, "ri", ++player1->flagscore); - musicfadeout(M_FLAGGRAB); + if(m_ctf) musicfadeout(M_FLAGGRAB); } else conoutf("\f2%s scored for %s team", colorname(f.actor), (own ? "the enemy" : "your")); break; } - case SV_FLAGRESET: - { - playsound(S_FLAGRETURN, SP_HIGH); - conoutf("the server reset the flag"); - break; - } + case SV_FLAGRESET: + { + playsound(S_FLAGRETURN, SP_HIGH); + conoutf("the server reset the flag"); + if(firstperson) musicfadeout(M_FLAGGRAB); + break; + } default: break; } } -void dropflag() { tryflagdrop(false); } +void dropflag() { tryflagdrop(true); } COMMAND(dropflag, ARG_NONE); char *votestring(int type, char *arg1, char *arg2) diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 915d8e58..08b0a652 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -268,7 +268,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) getstring(text, p); filtertext(d->team, text, false, MAXTEAMLEN); setskin(d, getint(p)); - if(m_ctf) loopi(2) + if(m_flags) loopi(2) { flaginfo &f = flaginfos[i]; if(!f.actor) f.actor = getclient(f.actor_cn); @@ -539,37 +539,43 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) flaginfo &f = flaginfos[flag]; f.state = getint(p); int action = getint(p); + int actor = f.state==CTFF_STOLEN || action==SV_FLAGRETURN ? getint(p) : -1; switch(f.state) { case CTFF_STOLEN: { - flagstolen(flag, action, getint(p)); + if(action==SV_FLAGSCORE) f.stolentime = getint(p); + flagstolen(flag, action, actor); break; } case CTFF_DROPPED: { - short x = (ushort) (getint(p)/DMF); - short y = (ushort) (getint(p)/DMF); - short z = ((ushort) (getint(p)/DMF))-((short)player1->eyeheight); // correct z offset, assumes all players do have the same eyeheight - flagdropped(flag, action, x, y, z); + if(action != SV_FLAGRETURN) + { + short x = (ushort) (getint(p)/DMF); + short y = (ushort) (getint(p)/DMF); + short z = ((ushort) (getint(p)/DMF))-((short)player1->eyeheight); // correct z offset, assumes all players do have the same eyeheight + flagdropped(flag, action, x, y, z); + } break; } case CTFF_INBASE: - { - int actor = -1; - if(action == SV_FLAGRETURN) actor = getint(p); flaginbase(flag, action, actor); break; - } + case CTFF_IDLE: + f.flagent->spawned = false; + f.ack = true; + break; } break; } case SV_FLAGS: { - if(!d) return; - d->flagscore = getint(p); + int fcn = getint(p), flags = getint(p); + playerent *p = (fcn == getclientnum() ? player1 : getclient(fcn)); + if(p) p->flagscore = flags; break; } diff --git a/source/src/command.cpp b/source/src/command.cpp index 1df7221d..7c6cbf91 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -424,7 +424,7 @@ bool nickcomplete(char *s) char *cp = s; for(int i = strlen(s) - 2; i > 0; i--) - if (s[i] == ' ') { cp = s + i + 1; break; } + if(s[i] == ' ') { cp = s + i + 1; break; } if(!completesize) { completesize = (int)strlen(cp); completeidx = 0; } int idx = 0; @@ -460,7 +460,7 @@ void commandcomplete(char *s) if(!s[1]) return; char *cp = s; for(int i = strlen(s) - 2; i > 0; i--) - if (s[i] == ';' || s[i] == ' ') { cp = s + i; break; } + if(s[i] == ';' || s[i] == ' ') { cp = s + i; break; } if(!completesize) { completesize = (int)strlen(cp)-1; diff --git a/source/src/entities.cpp b/source/src/entities.cpp index 393c86fb..88927179 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -8,15 +8,15 @@ const int physent::crouchtime = 200; vector ents; const char *entnames[] = -{ +{ "none?", "light", "playerstart", "clips", "ammobox","grenades", "health", "armour", "akimbo", - "mapmodel", "trigger", + "mapmodel", "trigger", "ladder", "ctf-flag", "sound", "?", "?", }; -const char *entmdlnames[] = +const char *entmdlnames[] = { "pickups/pistolclips", "pickups/ammobox", "pickups/nades", "pickups/health", "pickups/kevlar", "pickups/akimbo", }; @@ -28,13 +28,13 @@ void renderent(entity &e, const char *mdlname, float z, float yaw, int anim = AN void renderentities() { - if(editmode) + if(editmode) { loopv(ents) { entity &e = ents[i]; if(e.type==NOTUSED) continue; - vec v(e.x, e.y, e.z); + vec v(e.x, e.y, e.z); particle_splash(2, 2, 40, v); } } @@ -47,21 +47,6 @@ void renderentities() if(!&mmi) continue; rendermodel(mmi.name, ANIM_MAPMODEL|ANIM_LOOP, e.attr4, mmi.rad ? (float)mmi.rad : 1.1f, vec(e.x, e.y, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3), (float)((e.attr1+7)-(e.attr1+7)%15), 0, 10.0f); } - else if(e.type==CTF_FLAG && (m_ctf || editmode)) - { - flaginfo &f = flaginfos[e.attr2]; - if(f.state==CTFF_STOLEN) - { - if(!f.actor || f.actor == player1) continue; - s_sprintfd(path)("pickups/flags/small_%s", team_string(e.attr2)); - rendermodel(path, ANIM_FLAG|ANIM_START, 0, 1.1f, vec(f.actor->o).add(vec(0, 0, 0.3f+(sinf(lastmillis/100.0f)+1)/10)), lastmillis/2.5f, 0, 120.0f); - } - else - { - s_sprintfd(path)("pickups/flags/%s", team_string(e.attr2)); - rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 4, vec(e.x, e.y, f.state==CTFF_INBASE ? (float)S(e.x, e.y)->floor : e.z), (float)((e.attr1+7)-(e.attr1+7)%15), 0, 120.0f); - } - } else if(isitem(e.type)) { if((!OUTBORD(e.x, e.y) && e.spawned) | editmode) @@ -70,6 +55,30 @@ void renderentities() } } } + if(m_flags || editmode) loopi(2) + { + flaginfo &f = flaginfos[i]; + switch(f.state) + { + case CTFF_STOLEN: + if(f.actor && f.actor != player1) + { + s_sprintfd(path)("pickups/flags/small_%s%s", m_ktf ? "" : team_string(i), m_htf ? "_htf" : m_ktf ? "ktf" : ""); + rendermodel(path, ANIM_FLAG|ANIM_START, 0, 1.1f, vec(f.actor->o).add(vec(0, 0, 0.3f+(sinf(lastmillis/100.0f)+1)/10)), lastmillis/2.5f, 0, 120.0f); + } + break; + case CTFF_INBASE: + case CTFF_DROPPED: + { + entity &e = *f.flagent; + s_sprintfd(path)("pickups/flags/%s%s", m_ktf ? "" : team_string(i), m_htf ? "_htf" : m_ktf ? "ktf" : ""); + rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 4, vec(e.x, e.y, f.state==CTFF_INBASE ? (float)S(e.x, e.y)->floor : e.z), (float)((e.attr1+7)-(e.attr1+7)%15), 0, 120.0f); + break; + } + case CTFF_IDLE: + break; + } + } } // these two functions are called when the server acknowledges that you really @@ -129,13 +138,33 @@ void trypickup(int n, playerent *d) flaginfo &f = flaginfos[flag]; flaginfo &of = flaginfos[team_opposite(flag)]; if(f.state == CTFF_STOLEN) break; + bool own = flag == team_int(d->team); - if(flag == team_int(d->team)) // it's the own flag + if(m_ctf) { - if(f.state == CTFF_DROPPED) flagreturn(); - else if(f.state == CTFF_INBASE && of.state == CTFF_STOLEN && of.actor == d && of.ack) flagscore(); + if(own) // it's the own flag + { + if(f.state == CTFF_DROPPED) flagreturn(flag); + else if(f.state == CTFF_INBASE && of.state == CTFF_STOLEN && of.actor == d && of.ack) flagscore(of.team); + } + else flagpickup(flag); + } + else if(m_htf) + { + if(own) + { + flagpickup(flag); + } + else + { + if(f.state == CTFF_DROPPED) flagscore(f.team); // may not count! + } + } + else if(m_ktf) + { + if(f.state == CTFF_IDLE) break; + flagpickup(flag); } - else flagpickup(); } break; } @@ -160,7 +189,7 @@ void checkitems(playerent *d) if(dist1<1.5f && dist2attr2); if(!&mmi || !mmi.h) return false; - + float lo = (float)(S(e->x, e->y)->floor+mmi.zoff+e->attr3); float hi = lo+mmi.h; - vec v = to, w(e->x, e->y, lo + (fabs(hi-lo)/2.0f)), *p; + vec v = to, w(e->x, e->y, lo + (fabs(hi-lo)/2.0f)), *p; v.sub(from); w.sub(from); float c1 = w.dot(v); @@ -250,7 +279,7 @@ bool intersect(entity *e, vec &from, vec &to, vec *end) // if lineseg hits entit p = &v; } } - + if (p->x <= e->x+mmi.rad && p->x >= e->x-mmi.rad && p->y <= e->y+mmi.rad @@ -269,11 +298,11 @@ bool intersect(entity *e, vec &from, vec &to, vec *end) // if lineseg hits entit int flagdropmillis = 0; -void flagpickup() +void flagpickup(int fln) { if(flagdropmillis && flagdropmillis>lastmillis) return; - flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; - f.flag->spawned = false; + flaginfo &f = flaginfos[fln]; + f.flagent->spawned = false; f.state = CTFF_STOLEN; f.actor = player1; // do this although we don't know if we picked the flag to avoid getting it after a possible respawn f.actor_cn = getclientnum(); @@ -281,32 +310,35 @@ void flagpickup() addmsg(SV_FLAGPICKUP, "ri", f.team); } -void tryflagdrop(bool reset) +void tryflagdrop(bool manual) { - flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; - if(f.state==CTFF_STOLEN && f.actor==player1) + loopi(2) { - f.flag->spawned = false; - f.state = CTFF_DROPPED; - f.ack = false; - flagdropmillis = lastmillis+3000; - addmsg(reset ? SV_FLAGRESET : SV_FLAGDROP, "ri", f.team); + flaginfo &f = flaginfos[i]; + if(f.state==CTFF_STOLEN && f.actor==player1) + { + f.flagent->spawned = false; + f.state = CTFF_DROPPED; + f.ack = false; + flagdropmillis = lastmillis+3000; + addmsg(manual ? SV_FLAGDROP : SV_FLAGLOST, "ri", f.team); + } } } -void flagreturn() +void flagreturn(int fln) { - flaginfo &f = flaginfos[team_int(player1->team)]; - f.flag->spawned = false; + flaginfo &f = flaginfos[fln]; + f.flagent->spawned = false; f.ack = false; addmsg(SV_FLAGRETURN, "ri", f.team); } -void flagscore() +void flagscore(int fln) { - flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; + flaginfo &f = flaginfos[fln]; f.ack = false; - addmsg(SV_FLAGSCORE, "ri", f.team); + addmsg(SV_FLAGSCORE, "ri", fln); } // flag ent actions from the net @@ -318,7 +350,7 @@ void flagstolen(int flag, int action, int act) flaginfo &f = flaginfos[flag]; f.actor = actor; f.actor_cn = act; - f.flag->spawned = false; + f.flagent->spawned = false; f.ack = true; flagmsg(flag, action); } @@ -334,7 +366,7 @@ void flagdropped(int flag, int action, short x, short y, short z) p.o.z = z; p.vel.z = -0.8f; p.aboveeye = p.eyeheight = p.radius = 0.0f; - + bool oldcancollide = false; if(f.actor) { @@ -348,11 +380,11 @@ void flagdropped(int flag, int action, short x, short y, short z) } if(f.actor) f.actor->cancollide = oldcancollide; // restore settings - f.flag->x = (short)round(p.o.x); - f.flag->y = (short)round(p.o.y); - f.flag->z = (short)round(p.o.z); - if(f.flag->z < hdr.waterlevel) f.flag->z = (short) hdr.waterlevel; - f.flag->spawned = true; + f.flagent->x = (short)round(p.o.x); + f.flagent->y = (short)round(p.o.y); + f.flagent->z = (short)round(p.o.z); + if(f.flagent->z < hdr.waterlevel) f.flagent->z = (short) hdr.waterlevel; + f.flagent->spawned = true; f.ack = true; flagmsg(flag, action); } @@ -362,10 +394,10 @@ void flaginbase(int flag, int action, int act) flaginfo &f = flaginfos[flag]; playerent *actor = act == getclientnum() ? player1 : getclient(act); if(actor) { f.actor = actor; f.actor_cn = act; } - f.flag->x = (ushort) f.originalpos.x; - f.flag->y = (ushort) f.originalpos.y; - f.flag->z = (ushort) f.originalpos.z; - f.flag->spawned = true; + f.flagent->x = (ushort) f.originalpos.x; + f.flagent->y = (ushort) f.originalpos.y; + f.flagent->z = (ushort) f.originalpos.z; + f.flagent->spawned = true; f.ack = true; flagmsg(flag, action); } diff --git a/source/src/entity.h b/source/src/entity.h index 780c260f..93ae9c7e 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -465,18 +465,19 @@ struct botent : playerent int deaths() { return lifesequence; } }; -enum { CTFF_INBASE = 0, CTFF_STOLEN, CTFF_DROPPED }; +enum { CTFF_INBASE = 0, CTFF_STOLEN, CTFF_DROPPED, CTFF_IDLE }; struct flaginfo { int team; - entity *flag; + entity *flagent; int actor_cn; playerent *actor; vec originalpos; int state; // one of CTFF_* bool ack; - flaginfo() : flag(0), actor(0), state(CTFF_INBASE), ack(false) {} + int stolentime; + flaginfo() : flagent(0), actor(0), state(CTFF_INBASE), ack(false) {} }; enum { BT_NONE, BT_NADE, BT_GIB }; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index c9222f15..14f479a9 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -97,13 +97,13 @@ const char *modefullnames[] = { "team deathmatch", "coopedit", "deathmatch", "survivor", "team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing", - "one shot, one kill", "team one shot, one kill", "bot one shot, one kill" + "one shot, one kill", "team one shot, one kill", "bot one shot, one kill", "hunt the flag", "keep the flag" }; const char *modeacronymnames[] = { "TDM", "coop", "DM", "SURV", "TSURV", "CTF", "PF", "BTDM", "BDM", "LSS", - "OSOK", "TOSOK", "BOSOK" + "OSOK", "TOSOK", "BOSOK", "HTF", "CTF" }; const char *voteerrors[] = { "voting is currently disabled", "there is already a vote pending", "already voted", "can't vote that often", "this vote is not allowed in the current environment (singleplayer/multiplayer)", "no permission", "invalid vote" }; @@ -125,7 +125,7 @@ char msgsizesl[] = // size inclusive message token, 0 for variable SV_EDITMODE, 2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, SV_PRIMARYWEAP, 2, SV_MODELSKIN, 2, - SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGRESET, 2, SV_FLAGINFO, 0, SV_FLAGS, 2, + SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGLOST, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGRESET, 2, SV_FLAGINFO, 0, SV_FLAGS, 3, SV_ARENAWIN, 2, SV_SETADMIN, 0, SV_SERVOPINFO, 3, SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2, diff --git a/source/src/protocol.h b/source/src/protocol.h index d8624bb9..7140c160 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -23,7 +23,7 @@ enum SV_EDITMODE, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE, SV_SENDMAP, SV_RECVMAP, SV_SERVMSG, SV_ITEMLIST, SV_WEAPCHANGE, SV_PRIMARYWEAP, SV_MODELSKIN, - SV_FLAGPICKUP, SV_FLAGDROP, SV_FLAGRETURN, SV_FLAGSCORE, SV_FLAGRESET, SV_FLAGINFO, SV_FLAGS, + SV_FLAGPICKUP, SV_FLAGDROP, SV_FLAGLOST, SV_FLAGRETURN, SV_FLAGSCORE, SV_FLAGRESET, SV_FLAGINFO, SV_FLAGS, SV_ARENAWIN, SV_SETADMIN, SV_SERVOPINFO, SV_CALLVOTE, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTE, SV_VOTERESULT, @@ -69,11 +69,13 @@ enum GMODE_CTF, GMODE_PISTOLFRENZY, GMODE_BOTTEAMDEATHMATCH, - GMODE_GOTDEATHMATCH, + GMODE_BOTDEATHMATCH, GMODE_LASTSWISSSTANDING, GMODE_ONESHOTONEKILL, GMODE_TEAMONESHOTONEKILL, - GMODE_BOTONESHOTONEKILL + GMODE_BOTONESHOTONEKILL, + GMODE_HUNTTHEFLAG, // 13 + GMODE_KEEPTHEFLAG }; #define m_lms (gamemode==3 || gamemode==4) @@ -81,6 +83,8 @@ enum #define m_pistol (gamemode==6) #define m_lss (gamemode==9) #define m_osok (gamemode>=10 && gamemode<=12) +#define m_htf (gamemode==13) +#define m_ktf (gamemode==14) #define m_noitems (m_lms || m_osok) #define m_noitemsnade (m_lss) @@ -88,9 +92,10 @@ enum #define m_noprimary (m_pistol || m_lss) #define m_noguns (m_nopistol && m_noprimary) #define m_arena (m_lms || m_lss || m_osok) -#define m_teammode (gamemode==0 || gamemode==4 || gamemode==5 || gamemode==7 || gamemode==11) +#define m_teammode (gamemode==0 || gamemode==4 || gamemode==5 || gamemode==7 || gamemode==11 || gamemode==13 || gamemode==14) #define m_tarena (m_arena && m_teammode) #define m_botmode (gamemode==7 || gamemode == 8 || gamemode==12) -#define m_valid(mode) (((mode)>=0 && (mode)<=12) || (mode) == -3) +#define m_valid(mode) (((mode)>=0 && (mode)<=14) || (mode) == -3) #define m_mp(mode) (m_valid(mode) && (mode)!=7 && (mode)!=8 && (mode)!=12) #define m_demo (gamemode==-3) +#define m_flags (m_ctf || m_htf || m_ktf) diff --git a/source/src/protos.h b/source/src/protos.h index e6dcbde1..962be9d0 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -302,10 +302,10 @@ extern void setskin(playerent *pl, uint skin); extern void callvote(int type, char *arg1 = NULL, char *arg2 = NULL); extern void resetsleep(); //game mode extras -extern void flagpickup(); -extern void tryflagdrop(bool reset = false); -extern void flagreturn(); -extern void flagscore(); +extern void flagpickup(int fln); +extern void tryflagdrop(bool manual = false); +extern void flagreturn(int fln); +extern void flagscore(int fln); extern void flagstolen(int flag, int action, int act); extern void flagdropped(int flag, int action, short x, short y, short z); extern void flaginbase(int flag, int action, int act); diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index b96553f7..d382ad18 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -34,9 +34,22 @@ void drawradaricon(float x, float y, float s, int col, int row) void drawctficon(float x, float y, float s, int col, int row, float ts) { - static Texture *tex = NULL; - if(!tex) tex = textureload("packages/misc/ctficons.png"); - if(tex) drawicon(tex, x, y, s, col, row, ts); + static Texture *ctftex = NULL, *htftex = NULL, *ktftex = NULL; + if(!ctftex) ctftex = textureload("packages/misc/ctficons.png"); + if(!htftex) htftex = textureload("packages/misc/htficons.png"); + if(!ktftex) ktftex = textureload("packages/misc/ktficons.png"); + if(m_htf) + { + if(htftex) drawicon(htftex, x, y, s, col, row, ts); + } + else if(m_ktf) + { + if(ktftex) drawicon(ktftex, x, y, s, col, row, ts); + } + else + { + if(ctftex) drawicon(ctftex, x, y, s, col, row, ts); + } } void drawvoteicon(float x, float y, int col, int row, bool noblend) @@ -332,22 +345,26 @@ void drawradar(playerent *p, int w, int h) if(!pl || pl==p || !isteam(p->team, pl->team) || !insideradar(centerpos, res/2, pl->o)) continue; drawradarent(pl->o.x*coordtrans, pl->o.y*coordtrans, pl->yaw, pl->state==CS_ALIVE ? (isattacking(pl) ? 2 : 0) : 1, team_int(pl->team), iconsize, isattacking(pl), colorname(pl)); } - if(m_ctf) + if(m_flags) { glColor4f(1.0f, 1.0f, 1.0f, (sinf(lastmillis / 100.0f) + 1.0f) / 2.0f); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); loopi(2) // flag items { flaginfo &f = flaginfos[i]; - entity *e = f.flag; + entity *e = f.flagent; if(!e) continue; float yaw = showmap ? 0 : p->yaw; + if(m_ktf && f.state == CTFF_IDLE) continue; if(f.state==CTFF_STOLEN) { - if(f.actor && i != team_int(p->team) && insideradar(centerpos, res/2, f.actor->o)) - drawradarent(f.actor->o.x*coordtrans+iconsize/2, f.actor->o.y*coordtrans+iconsize/2, yaw, 3, f.team, iconsize, true); // draw near flag thief + bool tm = i != team_int(p->team); + if(m_htf) tm = !tm; + else if(m_ktf) tm = true; + if(f.actor && tm && insideradar(centerpos, res/2, f.actor->o)) + drawradarent(f.actor->o.x*coordtrans+iconsize/2, f.actor->o.y*coordtrans+iconsize/2, yaw, 3, m_ktf ? 2 : f.team, iconsize, true); // draw near flag thief } - else if(insideradar(centerpos, res/2, vec(e->x, e->y, centerpos.z))) drawradarent(e->x*coordtrans, e->y*coordtrans, yaw, 3, f.team, iconsize, false); // draw on entitiy pos + else if(insideradar(centerpos, res/2, vec(e->x, e->y, centerpos.z))) drawradarent(e->x*coordtrans, e->y*coordtrans, yaw, 3, m_ktf ? 2 : f.team, iconsize, false); // draw on entitiy pos } } @@ -512,7 +529,7 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat if(p->weaponsel && p->weaponsel->type>=GUN_KNIFE && p->weaponsel->typeweaponsel->renderstats(); // flowtron } - if(m_ctf && !hidectfhud) + if(m_flags && !hidectfhud) { glLoadIdentity(); glOrtho(0, VIRTW, VIRTH, 0, -1, 1); @@ -526,12 +543,13 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat } // big flag-stolen icon - flaginfo &f = flaginfos[team_opposite(team_int(p->team))]; - if(f.state==CTFF_STOLEN && f.actor == p && f.ack) + int ft = 0; + if((flaginfos[0].state==CTFF_STOLEN && flaginfos[0].actor == p && flaginfos[0].ack) || + (flaginfos[1].state==CTFF_STOLEN && flaginfos[1].actor == p && flaginfos[1].ack && ++ft)) { glColor4f(1.0f, 1.0f, 1.0f, (sinf(lastmillis/100.0f)+1.0f) / 2.0f); glEnable(GL_BLEND); - drawctficon(VIRTW-225-10, VIRTH*5/8, 225, team_opposite(team_int(p->team)), 1, 1/2.0f); + drawctficon(VIRTW-225-10, VIRTH*5/8, 225, m_ktf ? team_int(p->team) : ft, 1, 1/2.0f); glDisable(GL_BLEND); } } diff --git a/source/src/scoreboard.cpp b/source/src/scoreboard.cpp index 2ed163ba..cde04ff7 100644 --- a/source/src/scoreboard.cpp +++ b/source/src/scoreboard.cpp @@ -7,7 +7,7 @@ void *scoremenu = NULL, *teammenu = NULL, *ctfmenu = NULL; void showscores(bool on) { - menuset(on ? (m_ctf ? ctfmenu : (m_teammode ? teammenu : scoremenu)) : NULL); + menuset(on ? (m_flags ? ctfmenu : (m_teammode ? teammenu : scoremenu)) : NULL); } COMMAND(showscores, ARG_DOWN); @@ -33,7 +33,7 @@ struct teamscore teammembers.add(d); frags += d->frags; deaths += d->deaths; - if(m_ctf) flagscore += d->flagscore; + if(m_flags) flagscore += d->flagscore; } }; @@ -92,7 +92,7 @@ void renderscore(void *menu, playerent *d) string &s = line.s; scoreratio sr; sr.calc(d->frags, d->deaths); - if(m_ctf) s_sprintf(s)("%d\t%d\t%d\t%.*f\t%s\t%s\t%d\t%s%s", d->flagscore, d->frags, d->deaths, sr.precision, sr.ratio, d->state==CS_LAGGED ? "LAG" : lag, colorping(d->ping), d->clientnum, status, colorname(d)); + if(m_flags) s_sprintf(s)("%d\t%d\t%d\t%.*f\t%s\t%s\t%d\t%s%s", d->flagscore, d->frags, d->deaths, sr.precision, sr.ratio, d->state==CS_LAGGED ? "LAG" : lag, colorping(d->ping), d->clientnum, status, colorname(d)); else if(m_teammode) s_sprintf(s)("%d\t%d\t%.*f\t%s\t%s\t%d\t%s%s", d->frags, d->deaths, sr.precision, sr.ratio, d->state==CS_LAGGED ? "LAG" : lag, colorping(d->ping), d->clientnum, status, colorname(d)); else s_sprintf(s)("%d\t%d\t%.*f\t%s\t%s\t%d\t%s%s", d->frags, d->deaths, sr.precision, sr.ratio, d->state==CS_LAGGED ? "LAG" : lag, colorping(d->ping), d->clientnum, status, colorname(d)); } @@ -108,7 +108,7 @@ void renderteamscore(void *menu, teamscore *t) s_sprintfd(plrs)("(%d %s)", t->teammembers.length(), t->teammembers.length() == 1 ? "player" : "players"); scoreratio sr; sr.calc(t->frags, t->deaths); - if(m_ctf) s_sprintf(line.s)("%d\t%d\t%d\t%.*f\t\t\t\t%s\t\t%s", t->flagscore, t->frags, t->deaths, sr.precision, sr.ratio, team_string(t->team), plrs); + if(m_flags) s_sprintf(line.s)("%d\t%d\t%d\t%.*f\t\t\t\t%s\t\t%s", t->flagscore, t->frags, t->deaths, sr.precision, sr.ratio, team_string(t->team), plrs); else if(m_teammode) s_sprintf(line.s)("%d\t%d\t%.*f\t\t\t\t%s\t\t%s", t->frags, t->deaths, sr.precision, sr.ratio, team_string(t->team), plrs); static color teamcolors[2] = { color(1.0f, 0, 0, 0.2f), color(0, 0, 1.0f, 0.2f) }; line.bgcolor = &teamcolors[t->team]; diff --git a/source/src/server.cpp b/source/src/server.cpp index 92a557e5..ed1d2671 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -820,6 +820,7 @@ struct sflaginfo int actor_cn; float pos[3]; int lastupdate; + int stolentime; } sflaginfos[2]; void sendflaginfo(int flag, int action, int cn = -1) @@ -833,6 +834,7 @@ void sendflaginfo(int flag, int action, int cn = -1) putint(p, action); if(f.state==CTFF_STOLEN || action==SV_FLAGRETURN) putint(p, f.actor_cn); else if(f.state==CTFF_DROPPED) loopi(3) putint(p, int(f.pos[i]*DMF)); + if(f.state==CTFF_STOLEN && action==SV_FLAGSCORE) putint(p, (gamemillis - f.stolentime) / 1000); enet_packet_resize(packet, p.length()); sendpacket(cn, 1, packet); if(packet->referenceCount==0) enet_packet_destroy(packet); @@ -842,7 +844,8 @@ void flagaction(int flag, int action, int sender) { if(!valid_flag(flag)) return; sflaginfo &f = sflaginfos[flag]; - + sflaginfo &of = sflaginfos[team_opposite(flag)]; + bool ktf_switch = false; switch(action) { case SV_FLAGPICKUP: @@ -850,15 +853,18 @@ void flagaction(int flag, int action, int sender) if(f.state == CTFF_STOLEN) return; f.state = CTFF_STOLEN; f.actor_cn = sender; + f.stolentime = gamemillis; logger->writeline(log::info,"[%s] %s stole the flag", clients[sender]->hostname, clients[sender]->name); break; } case SV_FLAGDROP: + case SV_FLAGLOST: { if(f.state!=CTFF_STOLEN || (sender != -1 && f.actor_cn != sender)) return; + ktf_switch = true; f.state = CTFF_DROPPED; - loopi(3) f.pos[i] = clients[sender]->state.o[i]; - logger->writeline(log::info,"[%s] %s dropped the flag", clients[sender]->hostname, clients[sender]->name); + loopi(3) f.pos[i] = clients[f.actor_cn]->state.o[i]; + logger->writeline(log::info,"[%s] %s %s the flag", clients[f.actor_cn]->hostname, clients[f.actor_cn]->name, action == SV_FLAGLOST ? "lost" : "dropped"); break; } case SV_FLAGRETURN: @@ -872,19 +878,50 @@ void flagaction(int flag, int action, int sender) case SV_FLAGRESET: { if(sender != -1 && f.actor_cn != sender) return; + ktf_switch = true; f.state = CTFF_INBASE; logger->writeline(log::info,"The server reset the flag"); break; } case SV_FLAGSCORE: { - if(f.state != CTFF_STOLEN) return; - f.state = CTFF_INBASE; - logger->writeline(log::info, "[%s] %s scored with the flag for %s", clients[sender]->hostname, clients[sender]->name, clients[sender]->team); + bool score = true; + if(m_ctf) + { //ctf f: actor carrying, of: unstolen own + if(f.state != CTFF_STOLEN || f.actor_cn != sender || of.state != CTFF_INBASE) return; + f.state = CTFF_INBASE; + } + else if(m_htf) + { //htf of: own team carrying, f: other team dropped + if(f.state != CTFF_DROPPED) return; + f.state = CTFF_INBASE; + if(of.state != CTFF_STOLEN) + { + action = SV_FLAGRETURN; + score = false; // sry, fail + } + } + if(score) + { + clients[sender]->state.flagscore += 1; + sendf(-1, 1, "riii", SV_FLAGS, sender, clients[sender]->state.flagscore); + logger->writeline(log::info, "[%s] %s scored with the flag for %s, new score %d", clients[sender]->hostname, clients[sender]->name, clients[sender]->team, clients[sender]->state.flagscore); + } + else + { + logger->writeline(log::info, "[%s] %s failed to score", clients[sender]->hostname, clients[sender]->name); + } break; } default: return; } + if(m_ktf && ktf_switch) + { + f.state = CTFF_IDLE; + of.state = CTFF_INBASE; + sendflaginfo(team_opposite(flag), SV_FLAGRETURN); + action = 0; + } f.lastupdate = gamemillis; sendflaginfo(flag, action); @@ -892,14 +929,54 @@ void flagaction(int flag, int action, int sender) void ctfreset() { + int idleflag = m_ktf ? rnd(2) : -1; loopi(2) { sflaginfos[i].actor_cn = 0; - sflaginfos[i].state = CTFF_INBASE; + sflaginfos[i].state = i == idleflag ? CTFF_IDLE : CTFF_INBASE; sflaginfos[i].lastupdate = -1; } } +void htf_forceflag(int flag) +{ + sflaginfo &f = sflaginfos[flag]; + int besthealth = 0, numbesthealth = 0; + loopv(clients) if(clients[i]->type!=ST_EMPTY) + { + if(clients[i]->state.state == CS_ALIVE && team_int(clients[i]->team) == flag) + { + if(clients[i]->state.health == besthealth) + numbesthealth++; + else + { + if(clients[i]->state.health > besthealth) + { + besthealth = clients[i]->state.health; + numbesthealth = 1; + } + } + } + } + if(numbesthealth) + { + int pick = rnd(numbesthealth); + loopv(clients) if(clients[i]->type!=ST_EMPTY) + { + if(clients[i]->state.state == CS_ALIVE && team_int(clients[i]->team) == flag && --pick < 0) + { + f.state = CTFF_STOLEN; + f.actor_cn = i; + sendflaginfo(flag, SV_FLAGPICKUP); + logger->writeline(log::info,"[%s] %s got forced to pickup the flag", clients[i]->hostname, clients[i]->name); + break; + } + } + } + f.lastupdate = gamemillis; +} + + bool canspawn(client *c, bool connecting = false) { if(m_arena) @@ -1015,7 +1092,7 @@ void arenacheck() if(!dead) return; sendf(-1, 1, "ri2", SV_ARENAWIN, !alive ? -1 : alive->clientnum); arenaround = gamemillis+5000; - if (autoteam && m_teammode) refillteams(true); + if(autoteam && m_teammode) refillteams(true); } #define SPAMREPEATINTERVAL 15 // detect doubled lines only if interval < 15 seconds @@ -1141,7 +1218,7 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, { clientstate &ts = target->state; ts.dodamage(damage); - actor->state.damage += damage; + actor->state.damage += damage != 1000 ? damage : 0; sendf(-1, 1, "ri6", gib ? SV_GIBDAMAGE : SV_DAMAGE, target->clientnum, actor->clientnum, damage, ts.armour, ts.health); if(target!=actor && !hitpush.iszero()) { @@ -1152,6 +1229,9 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, } if(ts.health<=0) { + int targethasflag = -1; + bool flagtk = false, suic = false; + loopi(2) { if(sflaginfos[i].state == CTFF_STOLEN && sflaginfos[i].actor_cn == target->clientnum) targethasflag = i; } target->state.deaths++; if(target!=actor) { @@ -1160,14 +1240,29 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, { actor->state.frags--; actor->state.teamkills++; + flagtk = true; } } - else actor->state.frags--; + else + { // suicide + actor->state.frags--; + flagtk = true; + suic = true; + } sendf(-1, 1, "ri4", gib ? SV_GIBDIED : SV_DIED, target->clientnum, actor->clientnum, actor->state.frags); + if(flagtk && (m_htf || m_ktf) && targethasflag >= 0) + { + actor->state.flagscore--; + sendf(-1, 1, "riii", SV_FLAGS, actor->clientnum, actor->state.flagscore); + } target->position.setsizenodelete(0); ts.state = CS_DEAD; ts.lastdeath = gamemillis; logger->writeline(log::info, "[%s] %s %s %s", actor->hostname, actor->name, gib ? "gibbed" : "fragged", target->name); + if(m_flags && targethasflag >= 0) + { + flagaction(targethasflag, suic ? SV_FLAGLOST : SV_FLAGRESET, -1); + } // don't issue respawn yet until DEATHMILLIS has elapsed // ts.respawn(); @@ -1237,7 +1332,7 @@ void readscfg(const char *name) par[3] = par[4] = 0; // default values for(i = 0; i < CONFIG_MAXPAR; i++) { - if ((l = strtok(NULL, sep)) != NULL) + if((l = strtok(NULL, sep)) != NULL) par[i] = atoi(l); else break; @@ -1264,7 +1359,7 @@ int cmpipmatch(const void *a, const void * b) { return - (((struct iprange *)a)- enet_uint32 atoip(const char *s) { unsigned int d[3], res; - if (sscanf(s, "%u.%u.%u.%u", &res, d, d + 1, d + 2) != 4) return 0; + if(sscanf(s, "%u.%u.%u.%u", &res, d, d + 1, d + 2) != 4) return 0; loopi(3) { if(d[i] > 255) return 0; @@ -1445,7 +1540,7 @@ void forceteam(int client, int team, bool respawn, bool notify = false) void calcscores() { - loopv(clients) if (clients[i]->type!=ST_EMPTY) + loopv(clients) if(clients[i]->type!=ST_EMPTY) { clients[i]->at3_score = clients[i]->state.frags * 100 / (clients[i]->state.deaths ? clients[i]->state.deaths : 1) + clients[i]->state.flagscore < 3 ? 66 * clients[i]->state.flagscore : 66 + 33 * clients[i]->state.flagscore; @@ -1490,7 +1585,7 @@ bool refillteams(bool now, bool notify) // force only minimal amounts of player bool switched = false; calcscores(); - loopv(clients) if (clients[i]->type!=ST_EMPTY) // playerlist stocktaking + loopv(clients) if(clients[i]->type!=ST_EMPTY) // playerlist stocktaking { clients[i]->at3_dontmove = true; if(!clients[i]->awaitdisc) @@ -1500,8 +1595,8 @@ bool refillteams(bool now, bool notify) // force only minimal amounts of player { teamsize[t]++; teamscore[t] += clients[i]->at3_score; - if(!m_ctf || !((sflaginfos[0].state==CTFF_STOLEN && sflaginfos[0].actor_cn==i) || - (sflaginfos[1].state==CTFF_STOLEN && sflaginfos[1].actor_cn==i) )) + if(!m_flags || !((sflaginfos[0].state==CTFF_STOLEN && sflaginfos[0].actor_cn==i) || + (sflaginfos[1].state==CTFF_STOLEN && sflaginfos[1].actor_cn==i) )) { clients[i]->at3_dontmove = false; moveable[t]++; @@ -1519,7 +1614,7 @@ bool refillteams(bool now, bool notify) // force only minimal amounts of player if(now || gamemillis - lasttime_eventeams > 8000 + allplayers * 1000 || diffnum > 2 + allplayers / 10) { // time to even out teams - loopv(clients) if (clients[i]->type!=ST_EMPTY && team_int(clients[i]->team) != bigteam) clients[i]->at3_dontmove = true; // dont move small team players + loopv(clients) if(clients[i]->type!=ST_EMPTY && team_int(clients[i]->team) != bigteam) clients[i]->at3_dontmove = true; // dont move small team players while(diffnum > 1 && moveable[bigteam] > 0) { // pick best fitting cn @@ -1528,7 +1623,7 @@ bool refillteams(bool now, bool notify) // force only minimal amounts of player int bestfit = 1000000000; int targetscore = diffscore / (diffnum & ~1); s_sprintf(atlog)("at-target: %d, ", targetscore); - loopv(clients) if (clients[i]->type!=ST_EMPTY && !clients[i]->at3_dontmove) // try all still movable players + loopv(clients) if(clients[i]->type!=ST_EMPTY && !clients[i]->at3_dontmove) // try all still movable players { int fit = targetscore - clients[i]->at3_score; if(fit < 0 ) fit = -(fit * 15) / 10; // avoid too good players @@ -1607,7 +1702,7 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify) { if(!lastteammode) shuffleteams(false); - else if (autoteam) + else if(autoteam) refillteams(true, false); } // send spawns @@ -1624,6 +1719,7 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify) demonextmatch = false; setupdemorecord(); } + if(m_ktf) { sendflaginfo(0, 0); sendflaginfo(1, 0); } } void nextcfgset(bool notify = true) // load next maprotation set @@ -1813,7 +1909,7 @@ const char *disc_reason(int reason) void disconnect_client(int n, int reason) { if(!clients.inrange(n) || clients[n]->type!=ST_TCPIP) return; - if(m_ctf) loopi(2) if(sflaginfos[i].state==CTFF_STOLEN && sflaginfos[i].actor_cn==n) flagaction(i, SV_FLAGDROP, n); + if(m_flags) loopi(2) if(sflaginfos[i].state==CTFF_STOLEN && sflaginfos[i].actor_cn==n) flagaction(i, SV_FLAGLOST, n); client &c = *clients[n]; savedscore *sc = findscore(c, true); if(sc) sc->save(c.state); @@ -2069,7 +2165,7 @@ int checktype(int type, client *cl) static int edittypes[] = { SV_EDITENT, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE }; if(cl && smode!=1) loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return -1; // server only messages - static int servtypes[] = { SV_INITS2C, SV_MAPRELOAD, SV_SERVMSG, SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_DIED, SV_SPAWNSTATE, SV_FORCEDEATH, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_CDIS, SV_PONG, SV_RESUME, SV_FLAGINFO, SV_ARENAWIN, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_CLIENT, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT, SV_WHOISINFO }; + static int servtypes[] = { SV_INITS2C, SV_MAPRELOAD, SV_SERVMSG, SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_DIED, SV_SPAWNSTATE, SV_FORCEDEATH, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_CDIS, SV_PONG, SV_RESUME, SV_FLAGINFO, SV_ARENAWIN, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_CLIENT, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT, SV_WHOISINFO, SV_FLAGS, SV_FLAGRESET }; if(cl) loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1; return type; } @@ -2142,7 +2238,7 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 welcomepacket(p, sender); enet_packet_resize(packet, p.length()); sendpacket(sender, 1, packet); - if(smapname[0] && m_ctf) loopi(2) sendflaginfo(i, -1, sender); + if(smapname[0] && m_flags) loopi(2) sendflaginfo(i, -1, sender); if(clientrole != CR_DEFAULT) changeclientrole(sender, clientrole, NULL, true); } @@ -2443,19 +2539,16 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 case SV_FLAGPICKUP: case SV_FLAGDROP: + case SV_FLAGLOST: case SV_FLAGRETURN: case SV_FLAGSCORE: case SV_FLAGRESET: { + if(!m_flags) { disconnect_client(sender, DISC_TAGT); return; } flagaction(getint(p), type, sender); break; } - case SV_FLAGS: - cl->state.flagscore = getint(p); - QUEUE_MSG; - break; - case SV_SETADMIN: { bool claim = getint(p) != 0; @@ -2681,10 +2774,18 @@ void serverslice(uint timeout) // main server update, called from cube main lo { processevents(); checkitemspawns(diff); - if(m_ctf) loopi(2) + if(m_flags) loopi(2) { sflaginfo &f = sflaginfos[i]; - if(f.state==CTFF_DROPPED && gamemillis-f.lastupdate>30000) flagaction(i, SV_FLAGRESET, -1); + if(f.state == CTFF_DROPPED && gamemillis-f.lastupdate > (m_ctf ? 30000 : 10000)) flagaction(i, SV_FLAGRESET, -1); + if(m_htf && f.state == CTFF_INBASE && gamemillis-f.lastupdate > 10000) + { + htf_forceflag(i); + } + if(m_ktf && f.state == CTFF_STOLEN && gamemillis-f.lastupdate > 15000) + { + flagaction(i, SV_FLAGSCORE, f.actor_cn); + } } if(m_arena) arenacheck(); } @@ -2750,20 +2851,20 @@ void serverslice(uint timeout) // main server update, called from cube main lo { cvector teams; - bool flag; + bool addteam; loopv(clients) { if(clients[i]->type == ST_EMPTY || !clients[i]->name[0]) continue; - flag = true; + addteam = true; loopvj(teams) { if(strcmp(clients[i]->team,teams[j])==0 || !clients[i]->team[0]) { - flag = false; + addteam = false; break; } } - if(flag) teams.add(clients[i]->team); + if(addteam) teams.add(clients[i]->team); } loopv(teams) @@ -2778,7 +2879,7 @@ void serverslice(uint timeout) // main server update, called from cube main lo flagscore += clients[j]->state.flagscore; } logger->writeline(log::info, "Team %5s: %3d frags", teams[i], fragscore); - if(m_ctf) logger->writeline(log::info, "Team %5s: %3d flags", teams[i], flagscore); // ctf only + if(m_flags) logger->writeline(log::info, "Team %5s: %3d flags", teams[i], flagscore); // ctf only } } @@ -2910,19 +3011,19 @@ void extinfo_teamscorebuf(ucharbuf &p) if(!m_teammode) return; cvector teams; - bool flag; + bool addteam; loopv(clients) if(clients[i]->type!=ST_EMPTY) { - flag = true; + addteam = true; loopvj(teams) { if(strcmp(clients[i]->team,teams[j])==0 || !clients[i]->team[0]) { - flag = false; + addteam = false; break; } } - if(flag) teams.add(clients[i]->team); + if(addteam) teams.add(clients[i]->team); } loopv(teams) @@ -2937,7 +3038,7 @@ void extinfo_teamscorebuf(ucharbuf &p) flagscore += clients[j]->state.flagscore; } putint(p,fragscore); //add fragscore per team - if(m_ctf) //when capture mode + if(m_flags) //when capture mode { putint(p,flagscore); //add flagscore per team } diff --git a/source/src/serverevents.h b/source/src/serverevents.h index c5af7db5..a085b42d 100644 --- a/source/src/serverevents.h +++ b/source/src/serverevents.h @@ -3,7 +3,7 @@ void processevent(client *c, explodeevent &e) { clientstate &gs = c->state; - switch(e.gun) + switch(e.gun) { case GUN_GRENADE: if(!gs.grenades.remove(e.id)) return; @@ -79,14 +79,7 @@ void processevent(client *c, shotevent &e) void processevent(client *c, suicideevent &e) { - clientstate &gs = c->state; - if(gs.state!=CS_ALIVE) return; - gs.frags--; - gs.deaths++; - sendf(-1, 1, "ri4", SV_DIED, c->clientnum, c->clientnum, gs.frags); - c->position.setsizenodelete(0); - gs.state = CS_DEAD; - gs.respawn(); + serverdamage(c, c, 1000, GUN_KNIFE, false); logger->writeline(log::info, "[%s] %s suicided", c->hostname, c->name); } @@ -102,7 +95,7 @@ void processevent(client *c, reloadevent &e) clientstate &gs = c->state; if(!gs.isalive(gamemillis) || e.gun=NUMGUNS || - !reloadable_gun(e.gun) || + !reloadable_gun(e.gun) || gs.ammo[e.gun]<=0) return; @@ -148,7 +141,7 @@ void processevents() while(c->events.length()) { gameevent &e = c->events[0]; - if(e.typegamemillis) break; if(e.shot.millislastevent) { clearevent(c); continue; } diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index ebabbdc9..92ff9313 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -299,7 +299,7 @@ bool load_world(char *mname) // still supports all map formats that have if(e.attr1>32) e.attr1 = 32; // 12_03 and below } - if (hdr.version