From b03c776b8183a84aea5bb4be4ab3f491619bfd73 Mon Sep 17 00:00:00 2001 From: q66 Date: Thu, 16 Apr 2020 23:18:05 +0200 Subject: [PATCH] rip out most of gamecode --- config/sound.cfg | 6 - config/ui.cfg | 43 +- config/ui/edithud.cfg | 2 +- config/ui/scoreboard.cfg | 222 ----- src/client/meson.build | 3 - src/engine/physics.cc | 2 +- src/engine/server.cc | 1 - src/game/ai.cc | 1441 --------------------------------- src/game/ai.hh | 317 -------- src/game/aiman.hh | 276 ------- src/game/client.cc | 932 +-------------------- src/game/entities.cc | 2 - src/game/extinfo.hh | 145 ---- src/game/game.cc | 199 +---- src/game/game.hh | 98 +-- src/game/render.cc | 5 - src/game/scoreboard.cc | 216 ----- src/game/server.cc | 1652 +------------------------------------- src/game/waypoint.cc | 806 ------------------- src/game/weapon.cc | 13 +- src/shared/igame.hh | 1 - 21 files changed, 91 insertions(+), 6291 deletions(-) delete mode 100644 config/ui/scoreboard.cfg delete mode 100644 src/game/ai.cc delete mode 100644 src/game/ai.hh delete mode 100644 src/game/aiman.hh delete mode 100644 src/game/extinfo.hh delete mode 100644 src/game/scoreboard.cc delete mode 100644 src/game/waypoint.cc diff --git a/config/sound.cfg b/config/sound.cfg index e5aa274..bf7f2dc 100644 --- a/config/sound.cfg +++ b/config/sound.cfg @@ -21,12 +21,6 @@ defaultsoundpack = [ registersound // pain2 registersound "uphys/die1" 150 registersound "uphys/die2" 150 - registersound "ctf/flagpickup" 100 - registersound "ctf/flagdrop" 100 - registersound "ctf/flagreturn" 100 - registersound "ctf/flagscore" 100 - registersound "ctf/flagreturn" 100 - registersound "ctf/flagfail" 100 ] loadsoundpack = [ diff --git a/config/ui.cfg b/config/ui.cfg index a921335..66f07e3 100644 --- a/config/ui.cfg +++ b/config/ui.cfg @@ -3,7 +3,6 @@ exec "config/ui/lib.cfg" // UI library exec "config/ui/style.cfg" // Styles -exec "config/ui/scoreboard.cfg" // Scoreboard exec "config/ui/edithud.cfg" // Edit HUD exec "config/ui/fkey.cfg" // F# Key Menus exec "config/ui/serverbrowser.cfg" // Server Browser @@ -36,46 +35,6 @@ UImenu "main" [ uifill 0.2 0.02 [UIbar 1] UIbutton "hold2" [uitext "Quit" 0.65] 0.2 0.04 [quit] ] - if (! $mainmenu) [ - uifill 0.02 0.24 [UIbar 0 1] - uivlist 0 [ - if (isspectator $getclientnum) [ - if $scoreboardmultiplayer [ - if (ismaster $getclientnum) [ - UIbutton "hold2" [uitext "Play" 0.65] 0.2 0.04 [hideui "main" ; spectator 0] - ] [ - if (> $getmastermode 1) [ - uifill 0 0.04 [uitext "^f4Play" 0.65] - ] [ - UIbutton "hold2" [uitext "Play" 0.65] 0.2 0.04 [hideui "main" ; spectator 0] - ] - ] - ] [ - UIbutton "hold2" [uitext "Play" 0.65] 0.2 0.04 [hideui "main" ; spectator 0] - ] - ] [ - UIbutton "hold2" [uitext "Spectate" 0.65] 0.2 0.04 [hideui "main" ; spectator 1] - ] - case $getteam [ - 0] [uifill 0 0.04] [ - 1] [UIbutton "hold2" [uitext "Join ^f3Rojo" 0.65] 0.2 0.04 [team rojo]] [ - 2] [UIbutton "hold2" [uitext "Join ^f1Azul" 0.65] 0.2 0.04 [team azul]] - uifill 0.2 0.02 [UIbar 1] - if $scoreboardmultiplayer [ - UIbutton "hold2" [uitext "Master" 0.65] 0.2 0.04 [hideui "main" ; showui "master"] - if (ismaster $getclientnum) [ - UIbutton "hold2" [uitext "Bots" 0.65] 0.2 0.04 [hideui "main" ; showui "bots"] - ] [ - uifill 0 0.04 - ] - ] [ - uifill 0 0.04 - UIbutton "hold2" [uitext "Bots" 0.65] 0.2 0.04 [hideui "main" ; showui "bots"] - ] - uifill 0.2 0.02 [UIbar 1] - UIbutton "hold2" [uitext "Disconnect" 0.65] 0.2 0.04 [disconnect] - ] - ] ] ] @@ -234,7 +193,7 @@ UImenu "credits" [ /////////////////////////////////////////////////////////////////////////////// // Master // /////////////////////////////////////////////////////////////////////////////// -menu_master = [if $isconnected [if $scoreboardmultiplayer [toggleui "master"]]] +menu_master = [] UImenu "master" [ uifill 0 0 [ diff --git a/config/ui/edithud.cfg b/config/ui/edithud.cfg index d53b512..6bc8f41 100644 --- a/config/ui/edithud.cfg +++ b/config/ui/edithud.cfg @@ -57,7 +57,7 @@ newui "varicons" [ uialign 1 1 uispace 0.01 0.1 [ uivlist 0 [ - looplist i (concatword "allfaces entselsnap entediting fullbright showmat " (? $scoreboardmultiplayer "nompedit")) [ + looplist i (concatword "allfaces entselsnap entediting fullbright showmat") [ uifont "default_outline" [ uialign 1 if $$i [ diff --git a/config/ui/scoreboard.cfg b/config/ui/scoreboard.cfg deleted file mode 100644 index 1a0dd8a..0000000 --- a/config/ui/scoreboard.cfg +++ /dev/null @@ -1,222 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Scoreboard // -/////////////////////////////////////////////////////////////////////////////// -newui "scoreboard" [ - if $mainmenu [hideui "scoreboard"] - uiallowinput 0 - refreshscoreboard - uicolor (? $sbtransparent $c_menu_a $c_menu) 0 0 [ - uivlist 0 [ - UI_sbheader - uifill 0 0.005 - if (> $getmode 2) UI_playertableteam UI_playertablesolo - UI_spectatorlist - ] - ] -] [if $mainmenu [hideui "scoreboard"]] - -/////////////////////////////////////////////////////////////////////////////// -UI_sbwsolo = 0.68 -UI_sbwteam = 1.18 -UI_sbheader = [ - uifill 0 0.005 - uifill (? (> $getmode 2) $UI_sbwteam $UI_sbwsolo) 0.05 [ - uifill (-f (? (> $getmode 2) $UI_sbwteam $UI_sbwsolo) (? (= $showip 1) 0.18 0)) 0 [ - uialign -1 - uiclip (-f (? (> $getmode 2) $UI_sbwteam $UI_sbwsolo) (? (= $showip 1) 0.18 0)) 0 [ - uialign -1 - uispace 0.01 0 [ - uivlist 0 [ - if $scoreboardservinfo [ - uitext $scoreboardservinfo 0.65 - ] [ - uitext "^f4Tesseract" 0.65 - ] - uihlist 0.015 [ - uifill - uihlist 0.003 [ - uitext "^fs[" 0.52 - uitext (concatword (at ["^f0" "^f0" "^f2" "^f3"] $getmastermode) (getmastermodename $getmastermode)) 0.52 - uitext "^fS]" 0.52 - ] - UItriangle 0x606060 0.01 0.01 270 - uitext (getmodeprettyname $getmode) 0.52 - UItriangle 0x606060 0.01 0.01 270 - uitext $scoreboardmap 0.52 - if (m_timed $getmode) [ - UItriangle 0x606060 0.01 0.01 270 - uitext (concatword (? (|| $intermission $paused) "^f3" "^f8") $scoreboardtime) 0.52 - ] - ] - uialign* -1 - ] - ] - ] - ] - if $scoreboardmultiplayer [ - if $showip [ - uifill 0.18 0 [ - uialign 1 - UIbar 0 1; uialign- -1 - uiclip 0.18 0 [ - uialign 1 - uispace 0.01 0 [ - uivlist 0.004 [ - uicolortext $connectedip 0xA0A0A0 0.52 - uicolortext $connectedport 0xA0A0A0 0.52 - uialign* 1 - ] - ] - uialign- 1 - ] - ] - ] - ] - ] -] -//------------------------------------------------------------------------------------------------- -UI_cw_s = 0.042 -UI_cw_k = 0.054 -UI_cw_d = 0.054 -UI_cw_pj = 0.050 -UI_cw_p = 0.050 -UI_cw_cn = 0.040 -UI_cw_n = [-f (? (> $getmode 2) (*f $UI_sbwteam 0.5) $UI_sbwsolo) (? (> $getmode 2) (? $showscore $UI_cw_s)) (? $showkills $UI_cw_k) (? $showdeaths $UI_cw_d) (? $scoreboardmultiplayer (? $showpj $UI_cw_pj)) (? $scoreboardmultiplayer (? $showping $UI_cw_p)) (? $scoreboardmultiplayer (? $showclientnum $UI_cw_cn)) 0.03] -// ^ blarg - -UI_playertablesolo = [ - uifill 0 0 [ - uitable 0 0 [ - uitableheader [ - uifill 0.015 - uifill (UI_cw_n) 0.022 - if $showkills [uifill $UI_cw_k 0.022 [uicolortext "K" 0xBBCC8B 0.45 ; uialign- 1 1]] - if $showdeaths [uifill $UI_cw_d 0.022 [uicolortext "D" 0xE56767 0.45 ; uialign- 1 1]] - if $scoreboardmultiplayer [ - if $showpj [uifill $UI_cw_pj 0.022 [uicolortext "PJ" 0x77A1D9 0.45 ; uialign- 1 1]] - if $showping [uifill $UI_cw_p 0.022 [uicolortext "P" 0x77A1D9 0.45 ; uialign- 1 1]] - if $showclientnum [uifill $UI_cw_cn 0.022 [uicolortext "#" 0xA0A0A0 0.45 ; uialign- 1 1]] - ] - uifill 0.015 - ] [uicolor 0x88161616 0 0 [uiclamp 1 1 1 1]] - UI_sbtc = 0 - loopscoreboard cn 0 [ - UI_sbtc = (! $UI_sbtc) - uitablerow [ - uifill 0.015 - uifill (UI_cw_n) 0.026 [uicolortext (getclientcolorname $cn) (scoreboardstatus $cn) 0.57 ; uialign- -1] - if $showkills [uifill $UI_cw_k 0.026 [uicolortext (getclientfrags $cn) 0xBBCC8B 0.52 ; uialign- 1]] - if $showdeaths [uifill $UI_cw_d 0.026 [uicolortext (getclientdeaths $cn) 0xE56767 0.52 ; uialign- 1]] - if $scoreboardmultiplayer [ - if $showpj [uifill $UI_cw_pj 0.026 [uicolortext (? (isai $cn) "^f4-" (scoreboardpj $cn)) 0x77A1D9 0.52 ; uialign- 1]] - if $showping [uifill $UI_cw_p 0.026 [uicolortext (? (isai $cn) "^f4-" (scoreboardping $cn)) 0x77A1D9 0.52 ; uialign- 1]] - if $showclientnum [uifill $UI_cw_cn 0.026 [uicolortext (? (isai $cn) "^f4-" $cn) 0xA0A0A0 0.52 ; uialign- 1]] - ] - uifill 0.015 - ] [ - uicolor (? $UI_sbtc 0x99323232 0x99262626) 0 0 [uiclamp 1 1 1 1] - pushif highlight (scoreboardhighlight $cn) [uioutline 0xA0A0A0 (-f $UI_sbwsolo 0.002) 0.024] - ] - ] - ] - ] -] -UI_playertableteam = [ - uifill 0 0 [ - uihlist 0 [ - uitable 0 0 [ - uialign 0 -1 - uitableheader [ - uifill 0.015 - if $scoreboardmultiplayer [ - if $showclientnum [uifill $UI_cw_cn 0.042 [uicolortext "#" 0xA0A0A0 0.45 ; uialign- -1 1]] - if $showping [uifill $UI_cw_p 0.042 [uicolortext "P" 0x77A1D9 0.45 ; uialign- -1 1]] - if $showpj [uifill $UI_cw_pj 0.042 [uicolortext "PJ" 0x77A1D9 0.45 ; uialign- -1 1]] - ] - if $showdeaths [uifill $UI_cw_d 0.042 [uicolortext "D" 0xE56767 0.45 ; uialign- -1 1]] - if $showkills [uifill $UI_cw_k 0.042 [uicolortext "K" 0xBBCC8B 0.45 ; uialign- -1 1]] - if $showscore [uifill $UI_cw_s 0.042 [uicolortext "Score" 0x62B370 0.45 ; uialign- -1 1]] - uifill (UI_cw_n) 0.042 [uicolortext (getteamscore 1) 0x4060D0 1 ; uialign- 1] - uifill 0.015 - ] [uicolor 0x99202860 0 0 [uiclamp 1 1 1 1]] - UI_sbtc = 0 - loopscoreboard cn 1 [ - UI_sbtc = (! $UI_sbtc) - uitablerow [ - uifill 0.015 - if $scoreboardmultiplayer [ - if $showclientnum [uifill $UI_cw_cn 0.026 [uicolortext (? (isai $cn) "^f4-" $cn) 0xA0A0A0 0.52 ; uialign- -1]] - if $showping [uifill $UI_cw_p 0.026 [uicolortext (? (isai $cn) "^f4-" (scoreboardping $cn)) 0x77A1D9 0.52 ; uialign- -1]] - if $showpj [uifill $UI_cw_pj 0.026 [uicolortext (? (isai $cn) "^f4-" (scoreboardpj $cn)) 0x77A1D9 0.52 ; uialign- -1]] - ] - if $showdeaths [uifill $UI_cw_d 0.026 [uicolortext (getclientdeaths $cn) 0xE56767 0.52 ; uialign- -1]] - if $showkills [uifill $UI_cw_k 0.026 [uicolortext (getclientfrags $cn) 0xBBCC8B 0.52 ; uialign- -1]] - if $showscore [uifill $UI_cw_s 0.026 [uicolortext (getclientflags $cn) 0x62B370 0.52 ; uialign- -1]] - uifill (UI_cw_n) 0.026 [uicolortext (getclientcolorname $cn) (scoreboardstatus $cn) 0.57 ; uialign- 1] - uifill 0.015 - ] [ - uicolor (? $UI_sbtc 0x99333b40 0x99262b33) 0 0 [uiclamp 1 1 1 1] - pushif highlight (scoreboardhighlight $cn) [uioutline 0xA0A0A0 (-f (*f $UI_sbwteam 0.5) 0.002) 0.024] - ] - ] - ] - uitable 0 0 [ - uialign 0 -1 - uitableheader [ - uifill 0.015 - uifill (UI_cw_n) 0.042 [uicolortext (getteamscore 2) 0xD04040 1 ; uialign- -1] - if $showscore [uifill $UI_cw_s 0.042 [uicolortext "Score" 0x62B370 0.45 ; uialign- 1 1]] - if $showkills [uifill $UI_cw_k 0.042 [uicolortext "K" 0xBBCC8B 0.45 ; uialign- 1 1]] - if $showdeaths [uifill $UI_cw_d 0.042 [uicolortext "D" 0xE56767 0.45 ; uialign- 1 1]] - if $scoreboardmultiplayer [ - if $showpj [uifill $UI_cw_pj 0.042 [uicolortext "PJ" 0x77A1D9 0.45 ; uialign- 1 1]] - if $showping [uifill $UI_cw_p 0.042 [uicolortext "P" 0x77A1D9 0.45 ; uialign- 1 1]] - if $showclientnum [uifill $UI_cw_cn 0.042 [uicolortext "#" 0xA0A0A0 0.45 ; uialign- 1 1]] - ] - uifill 0.015 - ] [uicolor 0x99602020 0 0 [uiclamp 1 1 1 1]] - UI_sbtc = 0 - loopscoreboard cn 2 [ - UI_sbtc = (! $UI_sbtc) - uitablerow [ - uifill 0.015 - uifill (UI_cw_n) 0.026 [uicolortext (getclientcolorname $cn) (scoreboardstatus $cn) 0.57 ; uialign- -1] - if $showscore [uifill $UI_cw_s 0.026 [uicolortext (getclientflags $cn) 0x62B370 0.52 ; uialign- 1]] - if $showkills [uifill $UI_cw_k 0.026 [uicolortext (getclientfrags $cn) 0xBBCC8B 0.52 ; uialign- 1]] - if $showdeaths [uifill $UI_cw_d 0.026 [uicolortext (getclientdeaths $cn) 0xE56767 0.52 ; uialign- 1]] - if $scoreboardmultiplayer [ - if $showpj [uifill $UI_cw_pj 0.026 [uicolortext (? (isai $cn) "^f4-" (scoreboardpj $cn)) 0x77A1D9 0.52 ; uialign- 1]] - if $showping [uifill $UI_cw_p 0.026 [uicolortext (? (isai $cn) "^f4-" (scoreboardping $cn)) 0x77A1D9 0.52 ; uialign- 1]] - if $showclientnum [uifill $UI_cw_cn 0.026 [uicolortext (? (isai $cn) "^f4-" $cn) 0xA0A0A0 0.52 ; uialign- 1]] - ] - uifill 0.015 - ] [ - uicolor (? $UI_sbtc 0x99403333 0x99332626) 0 0 [uiclamp 1 1 1 1] - pushif highlight (scoreboardhighlight $cn) [uioutline 0xA0A0A0 (-f (*f $UI_sbwteam 0.5) 0.002) 0.024] - ] - ] - ] - ] - ] -] -//------------------------------------------------------------------------------------------------- -UI_spectatorlist = [ - uicolor 0x88161616 0 0.022 [ - uiclamp 1 1 1 1 - if $showspectators [ - uigrid (? (> $getmode 2) 3 2) 0 0 [ - loopscoreboard cn -1 [ - uispace 0.01 0.01 [ - pushif sbhigh (scoreboardhighlight $cn) [ - uioutline 0xA0A0A0 ; uiclamp- 1 1 1 1 - ] - uihlist 0.01 [ - uicolortext (getclientcolorname $cn) (scoreboardstatus $cn) 0.57 - if $scoreboardmultiplayer [if $showclientnum [uitext $cn 0.57]] - ] - ] - ] - ] - ] - ] -] diff --git a/src/client/meson.build b/src/client/meson.build index 28d4a2c..aab799d 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -40,14 +40,11 @@ client_src = [ '../engine/water.cc', '../engine/world.cc', '../engine/worldio.cc', - '../game/ai.cc', '../game/client.cc', '../game/entities.cc', '../game/game.cc', '../game/render.cc', - '../game/scoreboard.cc', '../game/server.cc', - '../game/waypoint.cc', '../game/weapon.cc' ] diff --git a/src/engine/physics.cc b/src/engine/physics.cc index be523ce..b869286 100644 --- a/src/engine/physics.cc +++ b/src/engine/physics.cc @@ -1890,7 +1890,7 @@ bool moveplayer(physent *pl, int moveres, bool local, int curtime) else if(pl->inwater && !water) game::physicstrigger(pl, local, 0, 1, pl->inwater); pl->inwater = water ? material&MATF_VOLUME : MAT_AIR; - if(pl->state==CS_ALIVE && (pl->o.z < 0 || material&MAT_DEATH)) game::suicide(pl); + //if(pl->state==CS_ALIVE && (pl->o.z < 0 || material&MAT_DEATH)) game::suicide(pl); return true; } diff --git a/src/engine/server.cc b/src/engine/server.cc index 58c5f70..0a269bb 100644 --- a/src/engine/server.cc +++ b/src/engine/server.cc @@ -194,7 +194,6 @@ void sendpacket(int n, int chan, ENetPacket *packet, int exclude) { if(n<0) { - server::recordpacket(chan, packet->data, packet->dataLength); loopv(clients) if(i!=exclude && server::allowbroadcast(i)) sendpacket(i, chan, packet); return; } diff --git a/src/game/ai.cc b/src/game/ai.cc deleted file mode 100644 index 8ea83bc..0000000 --- a/src/game/ai.cc +++ /dev/null @@ -1,1441 +0,0 @@ -#include "game.hh" - -extern int fog; - -namespace ai -{ - using namespace game; - - avoidset obstacles; - int updatemillis = 0, iteration = 0, itermillis = 0, forcegun = -1; - vec aitarget(0, 0, 0); - - VAR(aidebug, 0, 0, 6); - VAR(aiforcegun, -1, -1, NUMGUNS-1); - - ICOMMAND(addbot, "s", (char *s), addmsg(N_ADDBOT, "ri", *s ? clamp(parseint(s), 1, 101) : -1)); - ICOMMAND(delbot, "", (), addmsg(N_DELBOT, "r")); - ICOMMAND(botlimit, "i", (int *n), addmsg(N_BOTLIMIT, "ri", *n)); - ICOMMAND(botbalance, "i", (int *n), addmsg(N_BOTBALANCE, "ri", *n)); - - float viewdist(int x) - { - return x <= 100 ? clamp((SIGHTMIN+(SIGHTMAX-SIGHTMIN))/100.f*float(x), float(SIGHTMIN), float(fog)) : float(fog); - } - - float viewfieldx(int x) - { - return x <= 100 ? clamp((VIEWMIN+(VIEWMAX-VIEWMIN))/100.f*float(x), float(VIEWMIN), float(VIEWMAX)) : float(VIEWMAX); - } - - float viewfieldy(int x) - { - return viewfieldx(x)*3.f/4.f; - } - - bool canmove(gameent *d) - { - return d->state != CS_DEAD && !intermission; - } - - float attackmindist(int atk) - { - return max(int(attacks[atk].exprad), 2); - } - - float attackmaxdist(int atk) - { - return attacks[atk].range + 4; - } - - bool attackrange(gameent *d, int atk, float dist) - { - float mindist = attackmindist(atk), maxdist = attackmaxdist(atk); - return dist >= mindist*mindist && dist <= maxdist*maxdist; - } - - bool targetable(gameent *d, gameent *e) - { - if(d == e || !canmove(d)) return false; - return e->state == CS_ALIVE && !isteam(d->team, e->team); - } - - bool getsight(vec &o, float yaw, float pitch, vec &q, vec &v, float mdist, float fovx, float fovy) - { - float dist = o.dist(q); - - if(dist <= mdist) - { - float x = fmod(fabs(asin((q.z-o.z)/dist)/RAD-pitch), 360); - float y = fmod(fabs(-atan2(q.x-o.x, q.y-o.y)/RAD-yaw), 360); - if(min(x, 360-x) <= fovx && min(y, 360-y) <= fovy) return raycubelos(o, q, v); - } - return false; - } - - bool cansee(gameent *d, vec &x, vec &y, vec &targ) - { - aistate &b = d->ai->getstate(); - if(canmove(d) && b.type != AI_S_WAIT) - return getsight(x, d->yaw, d->pitch, y, targ, d->ai->views[2], d->ai->views[0], d->ai->views[1]); - return false; - } - - bool canshoot(gameent *d, int atk, gameent *e) - { - if(attackrange(d, atk, e->o.squaredist(d->o)) && targetable(d, e)) - return d->ammo[attacks[atk].gun] > 0 && lastmillis - d->lastaction >= d->gunwait; - return false; - } - - bool canshoot(gameent *d, int atk) - { - return !d->ai->becareful && d->ammo[attacks[atk].gun] > 0 && lastmillis - d->lastaction >= d->gunwait; - } - - bool hastarget(gameent *d, int atk, aistate &b, gameent *e, float yaw, float pitch, float dist) - { // add margins of error - if(attackrange(d, atk, dist) || (d->skill <= 100 && !rnd(d->skill))) - { - float skew = clamp(float(lastmillis-d->ai->enemymillis)/float((d->skill*attacks[atk].attackdelay/200.f)), 0.f, attacks[atk].projspeed ? 0.25f : 1e16f), - offy = yaw-d->yaw, offp = pitch-d->pitch; - if(offy > 180) offy -= 360; - else if(offy < -180) offy += 360; - if(fabs(offy) <= d->ai->views[0]*skew && fabs(offp) <= d->ai->views[1]*skew) return true; - } - return false; - } - - vec getaimpos(gameent *d, int atk, gameent *e) - { - vec o = e->o; - if(atk == ATK_PULSE_SHOOT) o.z += (e->aboveeye*0.2f)-(0.8f*d->eyeheight); - else o.z += (e->aboveeye-e->eyeheight)*0.5f; - if(d->skill <= 100) - { - if(lastmillis >= d->ai->lastaimrnd) - { - int aiskew = 1; - switch(atk) - { - case ATK_RAIL_SHOOT: aiskew = 5; break; - case ATK_PULSE_SHOOT: aiskew = 20; break; - default: break; - } - #define rndaioffset(r) ((rnd(int(r*aiskew*2)+1)-(r*aiskew))*(1.f/float(max(d->skill, 1)))) - loopk(3) d->ai->aimrnd[k] = rndaioffset(e->radius); - int dur = (d->skill+10)*10; - d->ai->lastaimrnd = lastmillis+dur+rnd(dur); - } - loopk(3) o[k] += d->ai->aimrnd[k]; - } - return o; - } - - void create(gameent *d) - { - if(!d->ai) d->ai = new aiinfo; - } - - void destroy(gameent *d) - { - if(d->ai) DELETEP(d->ai); - } - - void init(gameent *d, int at, int ocn, int sk, int bn, int pm, int col, const char *name, int team) - { - loadwaypoints(); - - gameent *o = newclient(ocn); - - d->aitype = at; - - bool resetthisguy = false; - if(!d->name[0]) - { - if(aidebug) conoutf("%s assigned to %s at skill %d", colorname(d, name), o ? colorname(o) : "?", sk); - else conoutf("\f0join:\f7 %s", colorname(d, name)); - resetthisguy = true; - } - else - { - if(d->ownernum != ocn) - { - if(aidebug) conoutf("%s reassigned to %s", colorname(d, name), o ? colorname(o) : "?"); - resetthisguy = true; - } - if(d->skill != sk && aidebug) conoutf("%s changed skill to %d", colorname(d, name), sk); - } - - copystring(d->name, name, MAXNAMELEN+1); - d->team = validteam(team) ? team : 0; - d->ownernum = ocn; - d->plag = 0; - d->skill = sk; - d->playermodel = chooserandomplayermodel(pm); - d->playercolor = col; - - if(resetthisguy) removeweapons(d); - if(d->ownernum >= 0 && player1->clientnum == d->ownernum) - { - create(d); - if(d->ai) - { - d->ai->views[0] = viewfieldx(d->skill); - d->ai->views[1] = viewfieldy(d->skill); - d->ai->views[2] = viewdist(d->skill); - } - } - else if(d->ai) destroy(d); - } - - void update() - { - if(intermission) { loopv(players) if(players[i]->ai) players[i]->stopmoving(); } - else // fixed rate logic done out-of-sequence at 1 frame per second for each ai - { - if(totalmillis-updatemillis > 1000) - { - avoid(); - forcegun = multiplayer(false) ? -1 : aiforcegun; - updatemillis = totalmillis; - } - if(!iteration && totalmillis-itermillis > 1000) - { - iteration = 1; - itermillis = totalmillis; - } - int count = 0; - loopv(players) if(players[i]->ai) think(players[i], ++count == iteration ? true : false); - if(++iteration > count) iteration = 0; - } - } - - bool checkothers(vector &targets, gameent *d, int state, int targtype, int target, bool teams, int *members) - { // checks the states of other ai for a match - targets.setsize(0); - loopv(players) - { - gameent *e = players[i]; - if(targets.find(e->clientnum) >= 0) continue; - if(teams && d && !isteam(d->team, e->team)) continue; - if(members) (*members)++; - if(e == d || !e->ai || e->state != CS_ALIVE) continue; - aistate &b = e->ai->getstate(); - if(state >= 0 && b.type != state) continue; - if(target >= 0 && b.target != target) continue; - if(targtype >=0 && b.targtype != targtype) continue; - targets.add(e->clientnum); - } - return !targets.empty(); - } - - bool makeroute(gameent *d, aistate &b, int node, bool changed, int retries) - { - if(!iswaypoint(d->lastnode)) return false; - if(changed && d->ai->route.length() > 1 && d->ai->route[0] == node) return true; - if(route(d, d->lastnode, node, d->ai->route, obstacles, retries)) - { - b.override = false; - return true; - } - // retry fails: 0 = first attempt, 1 = try ignoring obstacles, 2 = try ignoring prevnodes too - if(retries <= 1) return makeroute(d, b, node, false, retries+1); - return false; - } - - bool makeroute(gameent *d, aistate &b, const vec &pos, bool changed, int retries) - { - int node = closestwaypoint(pos, SIGHTMIN, true); - return makeroute(d, b, node, changed, retries); - } - - bool randomnode(gameent *d, aistate &b, const vec &pos, float guard, float wander) - { - static vector candidates; - candidates.setsize(0); - findwaypointswithin(pos, guard, wander, candidates); - - while(!candidates.empty()) - { - int w = rnd(candidates.length()), n = candidates.removeunordered(w); - if(n != d->lastnode && !d->ai->hasprevnode(n) && !obstacles.find(n, d) && makeroute(d, b, n)) return true; - } - return false; - } - - bool randomnode(gameent *d, aistate &b, float guard, float wander) - { - return randomnode(d, b, d->feetpos(), guard, wander); - } - - bool badhealth(gameent *d) - { - //if(d->skill <= 100) return d->health <= (111-d->skill)/4; - return false; - } - - bool enemy(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, int pursue = 0) - { - gameent *t = NULL; - vec dp = d->headpos(); - float mindist = guard*guard, bestdist = 1e16f; - int atk = guns[d->gunselect].attacks[ACT_SHOOT]; - loopv(players) - { - gameent *e = players[i]; - if(e == d || !targetable(d, e)) continue; - vec ep = getaimpos(d, atk, e); - float dist = ep.squaredist(dp); - if(dist < bestdist && (cansee(d, dp, ep) || dist <= mindist)) - { - t = e; - bestdist = dist; - } - } - if(t && violence(d, b, t, pursue)) return true; - return false; - } - - bool patrol(gameent *d, aistate &b, const vec &pos, float guard, float wander, int walk, bool retry) - { - vec feet = d->feetpos(); - if(walk == 2 || b.override || (walk && feet.squaredist(pos) <= guard*guard) || !makeroute(d, b, pos)) - { // run away and back to keep ourselves busy - if(!b.override && randomnode(d, b, pos, guard, wander)) - { - b.override = true; - return true; - } - else if(d->ai->route.empty()) - { - if(!retry) - { - b.override = false; - return patrol(d, b, pos, guard, wander, walk, true); - } - b.override = false; - return false; - } - } - b.override = false; - return true; - } - - bool defend(gameent *d, aistate &b, const vec &pos, float guard, float wander, int walk) - { - bool hasenemy = enemy(d, b, pos, wander); - if(!walk) - { - if(d->feetpos().squaredist(pos) <= guard*guard) - { - b.idle = hasenemy ? 2 : 1; - return true; - } - walk++; - } - return patrol(d, b, pos, guard, wander, walk); - } - - bool violence(gameent *d, aistate &b, gameent *e, int pursue) - { - if(e && targetable(d, e)) - { - if(pursue) - { - if((b.targtype != AI_T_AFFINITY || !(pursue%2)) && makeroute(d, b, e->lastnode)) - d->ai->switchstate(b, AI_S_PURSUE, AI_T_PLAYER, e->clientnum); - else if(pursue >= 3) return false; // can't pursue - } - if(d->ai->enemy != e->clientnum) - { - d->ai->enemyseen = d->ai->enemymillis = lastmillis; - d->ai->enemy = e->clientnum; - } - return true; - } - return false; - } - - bool target(gameent *d, aistate &b, int pursue = 0, bool force = false, float mindist = 0.f) - { - static vector hastried; hastried.setsize(0); - vec dp = d->headpos(); - while(true) - { - float dist = 1e16f; - gameent *t = NULL; - int atk = guns[d->gunselect].attacks[ACT_SHOOT]; - loopv(players) - { - gameent *e = players[i]; - if(e == d || hastried.find(e) >= 0 || !targetable(d, e)) continue; - vec ep = getaimpos(d, atk, e); - float v = ep.squaredist(dp); - if((!t || v < dist) && (mindist <= 0 || v <= mindist) && (force || cansee(d, dp, ep))) - { - t = e; - dist = v; - } - } - if(t) - { - if(violence(d, b, t, pursue)) return true; - hastried.add(t); - } - else break; - } - return false; - } - - int isgoodammo(int gun) { return gun == GUN_PULSE || gun == GUN_RAIL; } - - bool hasgoodammo(gameent *d) - { - static const int goodguns[] = { GUN_PULSE, GUN_RAIL }; - loopi(sizeof(goodguns)/sizeof(goodguns[0])) if(d->hasammo(goodguns[0])) return true; - return false; - } - - void assist(gameent *d, aistate &b, vector &interests, bool all, bool force) - { - loopv(players) - { - gameent *e = players[i]; - if(e == d || (!all && e->aitype != AI_NONE) || !isteam(d->team, e->team)) continue; - interest &n = interests.add(); - n.state = AI_S_DEFEND; - n.node = e->lastnode; - n.target = e->clientnum; - n.targtype = AI_T_PLAYER; - n.score = e->o.squaredist(d->o)/(hasgoodammo(d) ? 1e8f : (force ? 1e4f : 1e2f)); - } - } - - static void tryitem(gameent *d, extentity &e, int id, aistate &b, vector &interests, bool force = false) - { - float score = 0; - switch(e.type) - { - } - if(score != 0) - { - interest &n = interests.add(); - n.state = AI_S_INTEREST; - n.node = closestwaypoint(e.o, SIGHTMIN, true); - n.target = id; - n.targtype = AI_T_ENTITY; - n.score = d->feetpos().squaredist(e.o)/(force ? -1 : score); - } - } - - void items(gameent *d, aistate &b, vector &interests, bool force = false) - { - loopv(entities::ents) - { - extentity &e = *(extentity *)entities::ents[i]; - if(!e.spawned() || e.nopickup() || !d->canpickup(e.type)) continue; - tryitem(d, e, i, b, interests, force); - } - } - - static vector targets; - - bool parseinterests(gameent *d, aistate &b, vector &interests, bool override, bool ignore) - { - while(!interests.empty()) - { - int q = interests.length()-1; - loopi(interests.length()-1) if(interests[i].score < interests[q].score) q = i; - interest n = interests.removeunordered(q); - bool proceed = true; - if(!ignore) switch(n.state) - { - case AI_S_DEFEND: // don't get into herds - { - int members = 0; - proceed = !checkothers(targets, d, n.state, n.targtype, n.target, true, &members) && members > 1; - break; - } - default: break; - } - if(proceed && makeroute(d, b, n.node)) - { - d->ai->switchstate(b, n.state, n.targtype, n.target); - return true; - } - } - return false; - } - - bool find(gameent *d, aistate &b, bool override = false) - { - static vector interests; - interests.setsize(0); -#if 0 - if(!hasgoodammo(d) || d->health < min(d->skill - 15, 75)) - items(d, b, interests); - else - { - static vector nearby; - nearby.setsize(0); - findents(I_FIRST, I_LAST, false, d->feetpos(), vec(32, 32, 24), nearby); - loopv(nearby) - { - int id = nearby[i]; - extentity &e = *(extentity *)entities::ents[id]; - if(d->canpickup(e.type)) tryitem(d, e, id, b, interests); - } - } -#endif - if(cmode) cmode->aifind(d, b, interests); - if(m_teammode) assist(d, b, interests); - return parseinterests(d, b, interests, override); - } - - bool findassist(gameent *d, aistate &b, bool override = false) - { - static vector interests; - interests.setsize(0); - assist(d, b, interests); - while(!interests.empty()) - { - int q = interests.length()-1; - loopi(interests.length()-1) if(interests[i].score < interests[q].score) q = i; - interest n = interests.removeunordered(q); - bool proceed = true; - switch(n.state) - { - case AI_S_DEFEND: // don't get into herds - { - int members = 0; - proceed = !checkothers(targets, d, n.state, n.targtype, n.target, true, &members) && members > 1; - break; - } - default: break; - } - if(proceed && makeroute(d, b, n.node)) - { - d->ai->switchstate(b, n.state, n.targtype, n.target); - return true; - } - } - return false; - } - - void damaged(gameent *d, gameent *e) - { - if(d->ai && canmove(d) && targetable(d, e)) // see if this ai is interested in a grudge - { - aistate &b = d->ai->getstate(); - if(violence(d, b, e)) return; - } - if(checkothers(targets, d, AI_S_DEFEND, AI_T_PLAYER, d->clientnum, true)) - { - loopv(targets) - { - gameent *t = getclient(targets[i]); - if(!t->ai || !canmove(t) || !targetable(t, e)) continue; - aistate &c = t->ai->getstate(); - if(violence(t, c, e)) return; - } - } - } - - void findorientation(vec &o, float yaw, float pitch, vec &pos) - { - vec dir; - vecfromyawpitch(yaw, pitch, 1, 0, dir); - if(raycubepos(o, dir, pos, 0, RAY_CLIPMAT|RAY_SKIPFIRST) == -1) - pos = dir.mul(2*getworldsize()).add(o); //otherwise 3dgui won't work when outside of map - } - - void setup(gameent *d) - { - d->ai->clearsetup(); - d->ai->reset(true); - d->ai->lastrun = lastmillis; - if(forcegun >= 0 && forcegun < NUMGUNS) d->ai->weappref = forcegun; - else d->ai->weappref = rnd(NUMGUNS); - vec dp = d->headpos(); - findorientation(dp, d->yaw, d->pitch, d->ai->target); - } - - void spawned(gameent *d) - { - if(d->ai) setup(d); - } - - void killed(gameent *d, gameent *e) - { - if(d->ai) d->ai->reset(); - } - - void itemspawned(int ent) - { - if(!entities::ents.inrange(ent)) return; - extentity &e = *entities::ents[ent]; - if(validitem(e.type)) - { - loopv(players) if(players[i] && players[i]->ai && players[i]->aitype == AI_BOT && players[i]->canpickup(e.type)) - { - gameent *d = players[i]; - bool wantsitem = false; - switch(e.type) - { - } - if(wantsitem) - { - aistate &b = d->ai->getstate(); - if(b.targtype == AI_T_AFFINITY) continue; - if(b.type == AI_S_INTEREST && b.targtype == AI_T_ENTITY) - { - if(entities::ents.inrange(b.target)) - { - if(d->o.squaredist(entities::ents[ent]->o) < d->o.squaredist(entities::ents[b.target]->o)) - d->ai->switchstate(b, AI_S_INTEREST, AI_T_ENTITY, ent); - } - continue; - } - d->ai->switchstate(b, AI_S_INTEREST, AI_T_ENTITY, ent); - } - } - } - } - - bool check(gameent *d, aistate &b) - { - if(cmode && cmode->aicheck(d, b)) return true; - return false; - } - - int dowait(gameent *d, aistate &b) - { - d->ai->clear(true); // ensure they're clean - if(check(d, b) || find(d, b)) return 1; - if(target(d, b, 4, false)) return 1; - if(target(d, b, 4, true)) return 1; - if(randomnode(d, b, SIGHTMIN, 1e16f)) - { - d->ai->switchstate(b, AI_S_INTEREST, AI_T_NODE, d->ai->route[0]); - return 1; - } - return 0; // but don't pop the state - } - - int dodefend(gameent *d, aistate &b) - { - if(d->state == CS_ALIVE) - { - switch(b.targtype) - { - case AI_T_NODE: - if(check(d, b)) return 1; - if(iswaypoint(b.target)) return defend(d, b, waypoints[b.target].o) ? 1 : 0; - break; - case AI_T_ENTITY: - if(check(d, b)) return 1; - if(entities::ents.inrange(b.target)) return defend(d, b, entities::ents[b.target]->o) ? 1 : 0; - break; - case AI_T_AFFINITY: - if(cmode) return cmode->aidefend(d, b) ? 1 : 0; - break; - case AI_T_PLAYER: - { - if(check(d, b)) return 1; - gameent *e = getclient(b.target); - if(e && e->state == CS_ALIVE) return defend(d, b, e->feetpos()) ? 1 : 0; - break; - } - default: break; - } - } - return 0; - } - - int dointerest(gameent *d, aistate &b) - { - if(d->state != CS_ALIVE) return 0; - switch(b.targtype) - { - case AI_T_NODE: // this is like a wait state without sitting still.. - if(check(d, b) || find(d, b)) return 1; - if(target(d, b, 4, true)) return 1; - if(iswaypoint(b.target) && vec(waypoints[b.target].o).sub(d->feetpos()).magnitude() > CLOSEDIST) - return makeroute(d, b, waypoints[b.target].o) ? 1 : 0; - break; - case AI_T_ENTITY: - if(entities::ents.inrange(b.target)) - { - extentity &e = *(extentity *)entities::ents[b.target]; - if(!e.spawned() || e.nopickup() || !validitem(e.type)) return 0; - //if(d->feetpos().squaredist(e.o) <= CLOSEDIST*CLOSEDIST) - //{ - // b.idle = 1; - // return true; - //} - return makeroute(d, b, e.o) ? 1 : 0; - } - break; - } - return 0; - } - - int dopursue(gameent *d, aistate &b) - { - if(d->state == CS_ALIVE) - { - switch(b.targtype) - { - case AI_T_NODE: - { - if(check(d, b)) return 1; - if(iswaypoint(b.target)) - return defend(d, b, waypoints[b.target].o) ? 1 : 0; - break; - } - - case AI_T_AFFINITY: - { - if(cmode) return cmode->aipursue(d, b) ? 1 : 0; - break; - } - - case AI_T_PLAYER: - { - //if(check(d, b)) return 1; - gameent *e = getclient(b.target); - if(e && e->state == CS_ALIVE) - { - int atk = guns[d->gunselect].attacks[ACT_SHOOT]; - float guard = SIGHTMIN, wander = attacks[atk].range; - return patrol(d, b, e->feetpos(), guard, wander) ? 1 : 0; - } - break; - } - default: break; - } - } - return 0; - } - - int closenode(gameent *d) - { - vec pos = d->feetpos(); - int node1 = -1, node2 = -1; - float mindist1 = CLOSEDIST*CLOSEDIST, mindist2 = CLOSEDIST*CLOSEDIST; - loopv(d->ai->route) if(iswaypoint(d->ai->route[i])) - { - vec epos = waypoints[d->ai->route[i]].o; - float dist = epos.squaredist(pos); - if(dist > FARDIST*FARDIST) continue; - int entid = obstacles.remap(d, d->ai->route[i], epos); - if(entid >= 0) - { - if(entid != i) dist = epos.squaredist(pos); - if(dist < mindist1) { node1 = i; mindist1 = dist; } - } - else if(dist < mindist2) { node2 = i; mindist2 = dist; } - } - return node1 >= 0 ? node1 : node2; - } - - int wpspot(gameent *d, int n, bool check = false) - { - if(iswaypoint(n)) loopk(2) - { - vec epos = waypoints[n].o; - int entid = obstacles.remap(d, n, epos, k!=0); - if(iswaypoint(entid)) - { - d->ai->spot = epos; - d->ai->targnode = entid; - return !check || d->feetpos().squaredist(epos) > MINWPDIST*MINWPDIST ? 1 : 2; - } - } - return 0; - } - - int randomlink(gameent *d, int n) - { - if(iswaypoint(n) && waypoints[n].haslinks()) - { - waypoint &w = waypoints[n]; - static vector linkmap; linkmap.setsize(0); - loopi(MAXWAYPOINTLINKS) - { - if(!w.links[i]) break; - if(iswaypoint(w.links[i]) && !d->ai->hasprevnode(w.links[i]) && d->ai->route.find(w.links[i]) < 0) - linkmap.add(w.links[i]); - } - if(!linkmap.empty()) return linkmap[rnd(linkmap.length())]; - } - return -1; - } - - bool anynode(gameent *d, aistate &b, int len = NUMPREVNODES) - { - if(iswaypoint(d->lastnode)) loopk(2) - { - d->ai->clear(k ? true : false); - int n = randomlink(d, d->lastnode); - if(wpspot(d, n)) - { - d->ai->route.add(n); - d->ai->route.add(d->lastnode); - loopi(len) - { - n = randomlink(d, n); - if(iswaypoint(n)) d->ai->route.insert(0, n); - else break; - } - return true; - } - } - return false; - } - - bool checkroute(gameent *d, int n) - { - if(d->ai->route.empty() || !d->ai->route.inrange(n)) return false; - int last = d->ai->lastcheck ? lastmillis-d->ai->lastcheck : 0; - if(last < 500 || n < 3) return false; // route length is too short - d->ai->lastcheck = lastmillis; - int w = iswaypoint(d->lastnode) ? d->lastnode : d->ai->route[n], c = min(n-1, NUMPREVNODES); - loopj(c) // check ahead to see if we need to go around something - { - int p = n-j-1, v = d->ai->route[p]; - if(d->ai->hasprevnode(v) || obstacles.find(v, d)) // something is in the way, try to remap around it - { - int m = p-1; - if(m < 3) return false; // route length is too short from this point - loopirev(m) - { - int t = d->ai->route[i]; - if(!d->ai->hasprevnode(t) && !obstacles.find(t, d)) - { - static vector remap; remap.setsize(0); - if(route(d, w, t, remap, obstacles)) - { // kill what we don't want and put the remap in - while(d->ai->route.length() > i) d->ai->route.pop(); - loopvk(remap) d->ai->route.add(remap[k]); - return true; - } - return false; // we failed - } - } - return false; - } - } - return false; - } - - bool hunt(gameent *d, aistate &b) - { - if(!d->ai->route.empty()) - { - int n = closenode(d); - if(d->ai->route.inrange(n) && checkroute(d, n)) n = closenode(d); - if(d->ai->route.inrange(n)) - { - if(!n) - { - switch(wpspot(d, d->ai->route[n], true)) - { - case 2: d->ai->clear(false); - case 1: return true; // not close enough to pop it yet - case 0: default: break; - } - } - else - { - while(d->ai->route.length() > n+1) d->ai->route.pop(); // waka-waka-waka-waka - int m = n-1; // next, please! - if(d->ai->route.inrange(m) && wpspot(d, d->ai->route[m])) return true; - } - } - } - b.override = false; - return anynode(d, b); - } - - void jumpto(gameent *d, aistate &b, const vec &pos) - { - vec off = vec(pos).sub(d->feetpos()), dir(off.x, off.y, 0); - bool sequenced = d->ai->blockseq || d->ai->targseq, offground = d->timeinair && !d->inwater, - jump = !offground && lastmillis >= d->ai->jumpseed && (sequenced || off.z >= JUMPMIN || lastmillis >= d->ai->jumprand); - if(jump) - { - vec old = d->o; - d->o = vec(pos).addz(d->eyeheight); - if(collide(d, vec(0, 0, 1))) jump = false; - d->o = old; - if(jump) - { - float radius = 18*18; - loopv(entities::ents) if(entities::ents[i]->type == JUMPPAD) - { - gameentity &e = *(gameentity *)entities::ents[i]; - if(e.o.squaredist(pos) <= radius) { jump = false; break; } - } - } - } - if(jump) - { - d->jumping = true; - int seed = (111-d->skill)*(d->inwater ? 3 : 5); - d->ai->jumpseed = lastmillis+seed+rnd(seed); - seed *= b.idle ? 50 : 25; - d->ai->jumprand = lastmillis+seed+rnd(seed); - } - } - - void fixfullrange(float &yaw, float &pitch, float &roll, bool full) - { - if(full) - { - while(pitch < -180.0f) pitch += 360.0f; - while(pitch >= 180.0f) pitch -= 360.0f; - while(roll < -180.0f) roll += 360.0f; - while(roll >= 180.0f) roll -= 360.0f; - } - else - { - if(pitch > 89.9f) pitch = 89.9f; - if(pitch < -89.9f) pitch = -89.9f; - if(roll > 89.9f) roll = 89.9f; - if(roll < -89.9f) roll = -89.9f; - } - while(yaw < 0.0f) yaw += 360.0f; - while(yaw >= 360.0f) yaw -= 360.0f; - } - - void fixrange(float &yaw, float &pitch) - { - float r = 0.f; - fixfullrange(yaw, pitch, r, false); - } - - void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch) - { - float dist = from.dist(pos); - yaw = -atan2(pos.x-from.x, pos.y-from.y)/RAD; - pitch = asin((pos.z-from.z)/dist)/RAD; - } - - void scaleyawpitch(float &yaw, float &pitch, float targyaw, float targpitch, float frame, float scale) - { - if(yaw < targyaw-180.0f) yaw += 360.0f; - if(yaw > targyaw+180.0f) yaw -= 360.0f; - float offyaw = fabs(targyaw-yaw)*frame, offpitch = fabs(targpitch-pitch)*frame*scale; - if(targyaw > yaw) - { - yaw += offyaw; - if(targyaw < yaw) yaw = targyaw; - } - else if(targyaw < yaw) - { - yaw -= offyaw; - if(targyaw > yaw) yaw = targyaw; - } - if(targpitch > pitch) - { - pitch += offpitch; - if(targpitch < pitch) pitch = targpitch; - } - else if(targpitch < pitch) - { - pitch -= offpitch; - if(targpitch > pitch) pitch = targpitch; - } - fixrange(yaw, pitch); - } - - bool lockon(gameent *d, int atk, gameent *e, float maxdist) - { - if(attacks[atk].action == ACT_MELEE && !d->blocked && !d->timeinair) - { - vec dir = vec(e->o).sub(d->o); - float xydist = dir.x*dir.x+dir.y*dir.y, zdist = dir.z*dir.z, mdist = maxdist*maxdist, ddist = d->radius*d->radius+e->radius*e->radius; - if(zdist <= ddist && xydist >= ddist+4 && xydist <= mdist+ddist) return true; - } - return false; - } - - int process(gameent *d, aistate &b) - { - int result = 0, stupify = d->skill <= 10+rnd(15) ? rnd(d->skill*1000) : 0, skmod = 101-d->skill; - float frame = d->skill <= 100 ? float(lastmillis-d->ai->lastrun)/float(max(skmod,1)*10) : 1; - vec dp = d->headpos(); - - bool idle = b.idle == 1 || (stupify && stupify <= skmod); - d->ai->dontmove = false; - if(idle) - { - d->ai->lastaction = d->ai->lasthunt = lastmillis; - d->ai->dontmove = true; - d->ai->spot = vec(0, 0, 0); - } - else if(hunt(d, b)) - { - getyawpitch(dp, vec(d->ai->spot).addz(d->eyeheight), d->ai->targyaw, d->ai->targpitch); - d->ai->lasthunt = lastmillis; - } - else - { - idle = d->ai->dontmove = true; - d->ai->spot = vec(0, 0, 0); - } - - if(!d->ai->dontmove) jumpto(d, b, d->ai->spot); - - gameent *e = getclient(d->ai->enemy); - bool enemyok = e && targetable(d, e); - if(!enemyok || d->skill >= 50) - { - gameent *f = (gameent *)intersectclosest(dp, d->ai->target, d, 1); - if(f) - { - if(targetable(d, f)) - { - if(!enemyok) violence(d, b, f); - enemyok = true; - e = f; - } - else enemyok = false; - } - else if(!enemyok && target(d, b, 0, false, SIGHTMIN)) - enemyok = (e = getclient(d->ai->enemy)) != NULL; - } - if(enemyok) - { - int atk = guns[d->gunselect].attacks[ACT_SHOOT]; - vec ep = getaimpos(d, atk, e); - float yaw, pitch; - getyawpitch(dp, ep, yaw, pitch); - fixrange(yaw, pitch); - bool insight = cansee(d, dp, ep), hasseen = d->ai->enemyseen && lastmillis-d->ai->enemyseen <= (d->skill*10)+3000, - quick = d->ai->enemyseen && lastmillis-d->ai->enemyseen <= skmod+30; - if(insight) d->ai->enemyseen = lastmillis; - if(idle || insight || hasseen || quick) - { - float sskew = insight || d->skill > 100 ? 1.5f : (hasseen ? 1.f : 0.5f); - if(insight && lockon(d, atk, e, 16)) - { - d->ai->targyaw = yaw; - d->ai->targpitch = pitch; - if(!idle) frame *= 2; - d->ai->becareful = false; - } - scaleyawpitch(d->yaw, d->pitch, yaw, pitch, frame, sskew); - if(insight || quick) - { - if(canshoot(d, atk, e) && hastarget(d, atk, b, e, yaw, pitch, dp.squaredist(ep))) - { - d->attacking = attacks[atk].action; - d->ai->lastaction = lastmillis; - result = 3; - } - else result = 2; - } - else result = 1; - } - else - { - if(!d->ai->enemyseen || lastmillis-d->ai->enemyseen > (d->skill*50)+3000) - { - d->ai->enemy = -1; - d->ai->enemyseen = d->ai->enemymillis = 0; - } - enemyok = false; - result = 0; - } - } - else - { - if(!enemyok) - { - d->ai->enemy = -1; - d->ai->enemyseen = d->ai->enemymillis = 0; - } - enemyok = false; - result = 0; - } - - fixrange(d->ai->targyaw, d->ai->targpitch); - if(!result) scaleyawpitch(d->yaw, d->pitch, d->ai->targyaw, d->ai->targpitch, frame*0.25f, 1.f); - - if(d->ai->becareful && d->physstate == PHYS_FALL) - { - float offyaw, offpitch; - vectoyawpitch(d->vel, offyaw, offpitch); - offyaw -= d->yaw; offpitch -= d->pitch; - if(fabs(offyaw)+fabs(offpitch) >= 135) d->ai->becareful = false; - else if(d->ai->becareful) d->ai->dontmove = true; - } - else d->ai->becareful = false; - - if(d->ai->dontmove) d->move = d->strafe = 0; - else - { // our guys move one way.. but turn another?! :) - const struct aimdir { int move, strafe, offset; } aimdirs[8] = - { - { 1, 0, 0 }, - { 1, -1, 45 }, - { 0, -1, 90 }, - { -1, -1, 135 }, - { -1, 0, 180 }, - { -1, 1, 225 }, - { 0, 1, 270 }, - { 1, 1, 315 } - }; - float yaw = d->ai->targyaw-d->yaw; - while(yaw < 0.0f) yaw += 360.0f; - while(yaw >= 360.0f) yaw -= 360.0f; - int r = clamp(((int)floor((yaw+22.5f)/45.0f))&7, 0, 7); - const aimdir &ad = aimdirs[r]; - d->move = ad.move; - d->strafe = ad.strafe; - } - findorientation(dp, d->yaw, d->pitch, d->ai->target); - return result; - } - - bool hasrange(gameent *d, gameent *e, int weap) - { - if(!e) return true; - if(targetable(d, e)) - { - int atk = guns[weap].attacks[ACT_SHOOT]; - vec ep = getaimpos(d, atk, e); - float dist = ep.squaredist(d->headpos()); - if(attackrange(d, atk, dist)) return true; - } - return false; - } - - bool request(gameent *d, aistate &b) - { - gameent *e = getclient(d->ai->enemy); - if(!d->hasammo(d->gunselect) || !hasrange(d, e, d->gunselect) || (d->gunselect != d->ai->weappref && (!isgoodammo(d->gunselect) || d->hasammo(d->ai->weappref)))) - { - static const int gunprefs[] = { GUN_PULSE, GUN_RAIL }; - int gun = -1; - if(d->hasammo(d->ai->weappref) && hasrange(d, e, d->ai->weappref)) gun = d->ai->weappref; - else - { - loopi(sizeof(gunprefs)/sizeof(gunprefs[0])) if(d->hasammo(gunprefs[i]) && hasrange(d, e, gunprefs[i])) - { - gun = gunprefs[i]; - break; - } - } - if(gun >= 0 && gun != d->gunselect) gunselect(gun, d); - } - return process(d, b) >= 2; - } - - void timeouts(gameent *d, aistate &b) - { - if(d->blocked) - { - d->ai->blocktime += lastmillis-d->ai->lastrun; - if(d->ai->blocktime > (d->ai->blockseq+1)*1000) - { - d->ai->blockseq++; - switch(d->ai->blockseq) - { - case 1: case 2: case 3: - if(entities::ents.inrange(d->ai->targnode)) d->ai->addprevnode(d->ai->targnode); - d->ai->clear(false); - break; - case 4: d->ai->reset(true); break; - case 5: d->ai->reset(false); break; - case 6: default: suicide(d); return; break; // this is our last resort.. - } - } - } - else d->ai->blocktime = d->ai->blockseq = 0; - - if(d->ai->targnode == d->ai->targlast) - { - d->ai->targtime += lastmillis-d->ai->lastrun; - if(d->ai->targtime > (d->ai->targseq+1)*1000) - { - d->ai->targseq++; - switch(d->ai->targseq) - { - case 1: case 2: case 3: - if(entities::ents.inrange(d->ai->targnode)) d->ai->addprevnode(d->ai->targnode); - d->ai->clear(false); - break; - case 4: d->ai->reset(true); break; - case 5: d->ai->reset(false); break; - case 6: default: suicide(d); return; break; // this is our last resort.. - } - } - } - else - { - d->ai->targtime = d->ai->targseq = 0; - d->ai->targlast = d->ai->targnode; - } - - if(d->ai->lasthunt) - { - int millis = lastmillis-d->ai->lasthunt; - if(millis <= 1000) { d->ai->tryreset = false; d->ai->huntseq = 0; } - else if(millis > (d->ai->huntseq+1)*1000) - { - d->ai->huntseq++; - switch(d->ai->huntseq) - { - case 1: d->ai->reset(true); break; - case 2: d->ai->reset(false); break; - case 3: default: suicide(d); return; break; // this is our last resort.. - } - } - } - } - - void logic(gameent *d, aistate &b, bool run) - { - bool allowmove = canmove(d) && b.type != AI_S_WAIT; - if(d->state != CS_ALIVE || !allowmove) d->stopmoving(); - if(d->state == CS_ALIVE) - { - if(allowmove) - { - if(!request(d, b)) target(d, b, 0, b.idle ? true : false); - shoot(d, d->ai->target); - } - if(!intermission) - { - if(d->ragdoll) cleanragdoll(d); - moveplayer(d, 10, true); - if(allowmove && !b.idle) timeouts(d, b); - entities::checkitems(d); - if(cmode) cmode->checkitems(d); - } - } - else if(d->state == CS_DEAD) - { - if(d->ragdoll) moveragdoll(d); - else if(lastmillis-d->lastpain<2000) - { - d->move = d->strafe = 0; - moveplayer(d, 10, false); - } - } - d->attacking = ACT_IDLE; - d->jumping = false; - } - - void avoid() - { - // guess as to the radius of ai and other critters relying on the avoid set for now - float guessradius = player1->radius; - obstacles.clear(); - loopv(players) - { - dynent *d = players[i]; - if(d->state != CS_ALIVE) continue; - obstacles.avoidnear(d, d->o.z + d->aboveeye + 1, d->feetpos(), guessradius + d->radius); - } - extern avoidset wpavoid; - obstacles.add(wpavoid); - avoidweapons(obstacles, guessradius); - } - - void think(gameent *d, bool run) - { - // the state stack works like a chain of commands, certain commands simply replace each other - // others spawn new commands to the stack the ai reads the top command from the stack and executes - // it or pops the stack and goes back along the history until it finds a suitable command to execute - bool cleannext = false; - if(d->ai->state.empty()) d->ai->addstate(AI_S_WAIT); - loopvrev(d->ai->state) - { - aistate &c = d->ai->state[i]; - if(cleannext) - { - c.millis = lastmillis; - c.override = false; - cleannext = false; - } - if(d->state == CS_DEAD && d->respawned!=d->lifesequence && (!cmode || cmode->respawnwait(d, 250) <= 0) && lastmillis - d->lastpain >= 500) - { - addmsg(N_TRYSPAWN, "rc", d); - d->respawned = d->lifesequence; - } - else if(d->state == CS_ALIVE && run) - { - int result = 0; - c.idle = 0; - switch(c.type) - { - case AI_S_WAIT: result = dowait(d, c); break; - case AI_S_DEFEND: result = dodefend(d, c); break; - case AI_S_PURSUE: result = dopursue(d, c); break; - case AI_S_INTEREST: result = dointerest(d, c); break; - default: result = 0; break; - } - if(result <= 0) - { - if(c.type != AI_S_WAIT) - { - switch(result) - { - case 0: default: d->ai->removestate(i); cleannext = true; break; - case -1: i = d->ai->state.length()-1; break; - } - continue; // shouldn't interfere - } - } - } - logic(d, c, run); - break; - } - if(d->ai->trywipe) d->ai->wipe(); - d->ai->lastrun = lastmillis; - } - - void drawroute(gameent *d, float amt = 1.f) - { - int last = -1; - loopvrev(d->ai->route) - { - if(d->ai->route.inrange(last)) - { - int index = d->ai->route[i], prev = d->ai->route[last]; - if(iswaypoint(index) && iswaypoint(prev)) - { - waypoint &e = waypoints[index], &f = waypoints[prev]; - vec fr = f.o, dr = e.o; - fr.z += amt; dr.z += amt; - particle_flare(fr, dr, 1, PART_STREAK, 0xFFFFFF); - } - } - last = i; - } - if(aidebug >= 5) - { - vec pos = d->feetpos(); - if(d->ai->spot != vec(0, 0, 0)) particle_flare(pos, d->ai->spot, 1, PART_LIGHTNING, 0x00FFFF); - if(iswaypoint(d->ai->targnode)) - particle_flare(pos, waypoints[d->ai->targnode].o, 1, PART_LIGHTNING, 0xFF00FF); - if(iswaypoint(d->lastnode)) - particle_flare(pos, waypoints[d->lastnode].o, 1, PART_LIGHTNING, 0xFFFF00); - loopi(NUMPREVNODES) if(iswaypoint(d->ai->prevnodes[i])) - { - particle_flare(pos, waypoints[d->ai->prevnodes[i]].o, 1, PART_LIGHTNING, 0x884400); - pos = waypoints[d->ai->prevnodes[i]].o; - } - } - } - - VAR(showwaypoints, 0, 0, 1); - VAR(showwaypointsradius, 0, 200, 10000); - - const char *stnames[AI_S_MAX] = { - "wait", "defend", "pursue", "interest" - }, *sttypes[AI_T_MAX+1] = { - "none", "node", "player", "affinity", "entity" - }; - void render() - { - if(aidebug > 1) - { - int total = 0, alive = 0; - loopv(players) if(players[i]->ai) total++; - loopv(players) if(players[i]->state == CS_ALIVE && players[i]->ai) - { - gameent *d = players[i]; - vec pos = d->abovehead(); - pos.z += 3; - alive++; - if(aidebug >= 4) drawroute(d, 4.f*(float(alive)/float(total))); - if(aidebug >= 3) - { - defformatstring(q, "node: %d route: %d (%d)", - d->lastnode, - !d->ai->route.empty() ? d->ai->route[0] : -1, - d->ai->route.length() - ); - particle_textcopy(pos, q, PART_TEXT, 1); - pos.z += 2; - } - bool top = true; - loopvrev(d->ai->state) - { - aistate &b = d->ai->state[i]; - defformatstring(s, "%s%s (%d ms) %s:%d", - top ? "\fg" : "\fy", - stnames[b.type], - lastmillis-b.millis, - sttypes[b.targtype+1], b.target - ); - particle_textcopy(pos, s, PART_TEXT, 1); - pos.z += 2; - if(top) - { - if(aidebug >= 3) top = false; - else break; - } - } - if(aidebug >= 3) - { - if(d->ai->weappref >= 0 && d->ai->weappref < NUMGUNS) - { - particle_textcopy(pos, guns[d->ai->weappref].name, PART_TEXT, 1); - pos.z += 2; - } - gameent *e = getclient(d->ai->enemy); - if(e) - { - particle_textcopy(pos, colorname(e), PART_TEXT, 1); - pos.z += 2; - } - } - } - if(aidebug >= 4) - { - int cur = 0; - loopv(obstacles.obstacles) - { - const avoidset::obstacle &ob = obstacles.obstacles[i]; - int next = cur + ob.numwaypoints; - for(; cur < next; cur++) - { - int ent = obstacles.waypoints[cur]; - if(iswaypoint(ent)) - regular_particle_splash(PART_EDIT, 2, 40, waypoints[ent].o, 0xFF6600, 1.5f); - } - cur = next; - } - } - } - if(showwaypoints || aidebug >= 6) - { - vector close; - int len = waypoints.length(); - if(showwaypointsradius) - { - findwaypointswithin(camera1->o, 0, showwaypointsradius, close); - len = close.length(); - } - loopi(len) - { - waypoint &w = waypoints[showwaypointsradius ? close[i] : i]; - loopj(MAXWAYPOINTLINKS) - { - int link = w.links[j]; - if(!link) break; - particle_flare(w.o, waypoints[link].o, 1, PART_STREAK, 0x0000FF); - } - } - - } - } -} - diff --git a/src/game/ai.hh b/src/game/ai.hh deleted file mode 100644 index 56b420a..0000000 --- a/src/game/ai.hh +++ /dev/null @@ -1,317 +0,0 @@ -struct gameent; - -#define MAXBOTS 32 - -enum { AI_NONE = 0, AI_BOT, AI_MAX }; -#define isaitype(a) (a >= 0 && a <= AI_MAX-1) - -namespace ai -{ - const int MAXWAYPOINTS = USHRT_MAX - 2; - const int MAXWAYPOINTLINKS = 6; - const int WAYPOINTRADIUS = 16; - - const float MINWPDIST = 4.f; // is on top of - const float CLOSEDIST = 32.f; // is close - const float FARDIST = 128.f; // too far to remap close - const float JUMPMIN = 4.f; // decides to jump - const float JUMPMAX = 32.f; // max jump - const float SIGHTMIN = 64.f; // minimum line of sight - const float SIGHTMAX = 1024.f; // maximum line of sight - const float VIEWMIN = 90.f; // minimum field of view - const float VIEWMAX = 180.f; // maximum field of view - - struct waypoint - { - vec o; - float curscore, estscore; - int weight; - ushort route, prev; - ushort links[MAXWAYPOINTLINKS]; - - waypoint() {} - waypoint(const vec &o, int weight = 0) : o(o), weight(weight), route(0) { memset(links, 0, sizeof(links)); } - - int score() const { return int(curscore) + int(estscore); } - - int find(int wp) - { - loopi(MAXWAYPOINTLINKS) if(links[i] == wp) return i; - return -1; - } - - bool haslinks() { return links[0]!=0; } - }; - extern vector waypoints; - - static inline bool iswaypoint(int n) - { - return n > 0 && n < waypoints.length(); - } - - extern int showwaypoints, dropwaypoints; - extern int closestwaypoint(const vec &pos, float mindist, bool links, gameent *d = NULL); - extern void findwaypointswithin(const vec &pos, float mindist, float maxdist, vector &results); - extern void inferwaypoints(gameent *d, const vec &o, const vec &v, float mindist = ai::CLOSEDIST); - - struct avoidset - { - struct obstacle - { - void *owner; - int numwaypoints; - float above; - - obstacle(void *owner, float above = -1) : owner(owner), numwaypoints(0), above(above) {} - }; - - vector obstacles; - vector waypoints; - - void clear() - { - obstacles.setsize(0); - waypoints.setsize(0); - } - - void add(void *owner, float above) - { - obstacles.add(obstacle(owner, above)); - } - - void add(void *owner, float above, int wp) - { - if(obstacles.empty() || owner != obstacles.last().owner) add(owner, above); - obstacles.last().numwaypoints++; - waypoints.add(wp); - } - - void add(avoidset &avoid) - { - waypoints.put(avoid.waypoints.getbuf(), avoid.waypoints.length()); - loopv(avoid.obstacles) - { - obstacle &o = avoid.obstacles[i]; - if(obstacles.empty() || o.owner != obstacles.last().owner) add(o.owner, o.above); - obstacles.last().numwaypoints += o.numwaypoints; - } - } - - void avoidnear(void *owner, float above, const vec &pos, float limit); - - #define loopavoid(v, d, body) \ - if(!(v).obstacles.empty()) \ - { \ - int cur = 0; \ - loopv((v).obstacles) \ - { \ - const ai::avoidset::obstacle &ob = (v).obstacles[i]; \ - int next = cur + ob.numwaypoints; \ - if(ob.owner != d) \ - { \ - for(; cur < next; cur++) \ - { \ - int wp = (v).waypoints[cur]; \ - body; \ - } \ - } \ - cur = next; \ - } \ - } - - bool find(int n, gameent *d) const - { - loopavoid(*this, d, { if(wp == n) return true; }); - return false; - } - - int remap(gameent *d, int n, vec &pos, bool retry = false); - }; - - extern bool route(gameent *d, int node, int goal, vector &route, const avoidset &obstacles, int retries = 0); - extern void navigate(); - extern void clearwaypoints(bool full = false); - extern void seedwaypoints(); - extern void loadwaypoints(bool force = false, const char *mname = NULL); - extern void savewaypoints(bool force = false, const char *mname = NULL); - - // ai state information for the owner client - enum - { - AI_S_WAIT = 0, // waiting for next command - AI_S_DEFEND, // defend goal target - AI_S_PURSUE, // pursue goal target - AI_S_INTEREST, // interest in goal entity - AI_S_MAX - }; - - enum - { - AI_T_NODE, - AI_T_PLAYER, - AI_T_AFFINITY, - AI_T_ENTITY, - AI_T_MAX - }; - - struct interest - { - int state, node, target, targtype; - float score; - interest() : state(-1), node(-1), target(-1), targtype(-1), score(0.f) {} - ~interest() {} - }; - - struct aistate - { - int type, millis, targtype, target, idle; - bool override; - - aistate(int m, int t, int r = -1, int v = -1) : type(t), millis(m), targtype(r), target(v) - { - reset(); - } - ~aistate() {} - - void reset() - { - idle = 0; - override = false; - } - }; - - const int NUMPREVNODES = 6; - - struct aiinfo - { - vector state; - vector route; - vec target, spot; - int enemy, enemyseen, enemymillis, weappref, prevnodes[NUMPREVNODES], targnode, targlast, targtime, targseq, - lastrun, lasthunt, lastaction, lastcheck, jumpseed, jumprand, blocktime, huntseq, blockseq, lastaimrnd; - float targyaw, targpitch, views[3], aimrnd[3]; - bool dontmove, becareful, tryreset, trywipe; - - aiinfo() - { - clearsetup(); - reset(); - loopk(3) views[k] = 0.f; - } - ~aiinfo() {} - - void clearsetup() - { - weappref = GUN_RAIL; - spot = target = vec(0, 0, 0); - lastaction = lasthunt = lastcheck = enemyseen = enemymillis = blocktime = huntseq = blockseq = targtime = targseq = lastaimrnd = 0; - lastrun = jumpseed = lastmillis; - jumprand = lastmillis+5000; - targnode = targlast = enemy = -1; - } - - void clear(bool prev = false) - { - if(prev) memset(prevnodes, -1, sizeof(prevnodes)); - route.setsize(0); - } - - void wipe(bool prev = false) - { - clear(prev); - state.setsize(0); - addstate(AI_S_WAIT); - trywipe = false; - } - - void clean(bool tryit = false) - { - if(!tryit) becareful = dontmove = false; - targyaw = rnd(360); - targpitch = 0.f; - tryreset = tryit; - } - - void reset(bool tryit = false) { wipe(); clean(tryit); } - - bool hasprevnode(int n) const - { - loopi(NUMPREVNODES) if(prevnodes[i] == n) return true; - return false; - } - - void addprevnode(int n) - { - if(prevnodes[0] != n) - { - memmove(&prevnodes[1], prevnodes, sizeof(prevnodes) - sizeof(prevnodes[0])); - prevnodes[0] = n; - } - } - - aistate &addstate(int t, int r = -1, int v = -1) - { - return state.add(aistate(lastmillis, t, r, v)); - } - - void removestate(int index = -1) - { - if(index < 0) state.pop(); - else if(state.inrange(index)) state.remove(index); - if(!state.length()) addstate(AI_S_WAIT); - } - - aistate &getstate(int idx = -1) - { - if(state.inrange(idx)) return state[idx]; - return state.last(); - } - - aistate &switchstate(aistate &b, int t, int r = -1, int v = -1) - { - if((b.type == t && b.targtype == r) || (b.type == AI_S_INTEREST && b.targtype == AI_T_NODE)) - { - b.millis = lastmillis; - b.target = v; - b.reset(); - return b; - } - return addstate(t, r, v); - } - }; - - extern avoidset obstacles; - extern vec aitarget; - - extern float viewdist(int x = 101); - extern float viewfieldx(int x = 101); - extern float viewfieldy(int x = 101); - extern bool targetable(gameent *d, gameent *e); - extern bool cansee(gameent *d, vec &x, vec &y, vec &targ = aitarget); - - extern void init(gameent *d, int at, int on, int sk, int bn, int pm, int col, const char *name, int team); - extern void update(); - extern void avoid(); - extern void think(gameent *d, bool run); - - extern bool badhealth(gameent *d); - extern bool checkothers(vector &targets, gameent *d = NULL, int state = -1, int targtype = -1, int target = -1, bool teams = false, int *members = NULL); - extern bool makeroute(gameent *d, aistate &b, int node, bool changed = true, int retries = 0); - extern bool makeroute(gameent *d, aistate &b, const vec &pos, bool changed = true, int retries = 0); - extern bool randomnode(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX); - extern bool randomnode(gameent *d, aistate &b, float guard = SIGHTMIN, float wander = SIGHTMAX); - extern bool violence(gameent *d, aistate &b, gameent *e, int pursue = 0); - extern bool patrol(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX, int walk = 1, bool retry = false); - extern bool defend(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX, int walk = 1); - extern void assist(gameent *d, aistate &b, vector &interests, bool all = false, bool force = false); - extern bool parseinterests(gameent *d, aistate &b, vector &interests, bool override = false, bool ignore = false); - - extern void spawned(gameent *d); - extern void damaged(gameent *d, gameent *e); - extern void killed(gameent *d, gameent *e); - extern void itemspawned(int ent); - - extern void render(); -} - - diff --git a/src/game/aiman.hh b/src/game/aiman.hh deleted file mode 100644 index e721cac..0000000 --- a/src/game/aiman.hh +++ /dev/null @@ -1,276 +0,0 @@ -// server-side ai manager -namespace aiman -{ - bool dorefresh = false, botbalance = true; - VARN(serverbotlimit, botlimit, 0, 8, MAXBOTS); - VAR(serverbotbalance, 0, 1, 1); - - void calcteams(vector &teams) - { - loopv(clients) - { - clientinfo *ci = clients[i]; - if(ci->state.state==CS_SPECTATOR || !validteam(ci->team)) continue; - teamscore *t = NULL; - loopvj(teams) if(teams[j].team == ci->team) { t = &teams[j]; break; } - if(t) t->score++; - else teams.add(teamscore(ci->team, 1)); - } - teams.sort(teamscore::compare); - if(teams.length() < MAXTEAMS) - { - loopi(MAXTEAMS) if(teams.htfind(1+i) < 0) teams.add(teamscore(1+i, 0)); - } - } - - void balanceteams() - { - vector teams; - calcteams(teams); - vector reassign; - loopv(bots) if(bots[i]) reassign.add(bots[i]); - while(reassign.length() && teams.length() && teams[0].score > teams.last().score + 1) - { - teamscore &t = teams.last(); - clientinfo *bot = NULL; - loopv(reassign) if(reassign[i] && reassign[i]->team != teams[0].team) - { - bot = reassign.removeunordered(i); - teams[0].score--; - t.score++; - for(int j = teams.length() - 2; j >= 0; j--) - { - if(teams[j].score >= teams[j+1].score) break; - swap(teams[j], teams[j+1]); - } - break; - } - if(bot) - { - if(smode && bot->state.state==CS_ALIVE) smode->changeteam(bot, bot->team, t.team); - bot->team = t.team; - sendf(-1, 1, "riiii", N_SETTEAM, bot->clientnum, bot->team, 0); - } - else teams.remove(0, 1); - } - } - - int chooseteam() - { - vector teams; - calcteams(teams); - return teams.length() ? teams.last().team : 0; - } - - static inline bool validaiclient(clientinfo *ci) - { - return ci->clientnum >= 0 && ci->state.aitype == AI_NONE && (ci->state.state!=CS_SPECTATOR || ci->local || (ci->privilege && !ci->warned)); - } - - clientinfo *findaiclient(clientinfo *exclude = NULL) - { - clientinfo *least = NULL; - loopv(clients) - { - clientinfo *ci = clients[i]; - if(!validaiclient(ci) || ci==exclude) continue; - if(!least || ci->bots.length() < least->bots.length()) least = ci; - } - return least; - } - - bool addai(int skill, int limit) - { - int numai = 0, cn = -1, maxai = limit >= 0 ? min(limit, MAXBOTS) : MAXBOTS; - loopv(bots) - { - clientinfo *ci = bots[i]; - if(!ci || ci->ownernum < 0) { if(cn < 0) cn = i; continue; } - numai++; - } - if(numai >= maxai) return false; - if(bots.inrange(cn)) - { - clientinfo *ci = bots[cn]; - if(ci) - { // reuse a slot that was going to removed - - clientinfo *owner = findaiclient(); - ci->ownernum = owner ? owner->clientnum : -1; - if(owner) owner->bots.add(ci); - ci->aireinit = 2; - dorefresh = true; - return true; - } - } - else { cn = bots.length(); bots.add(NULL); } - int team = m_teammode ? chooseteam() : 0; - if(!bots[cn]) bots[cn] = new clientinfo; - clientinfo *ci = bots[cn]; - ci->clientnum = MAXCLIENTS + cn; - ci->state.aitype = AI_BOT; - clientinfo *owner = findaiclient(); - ci->ownernum = owner ? owner->clientnum : -1; - if(owner) owner->bots.add(ci); - ci->state.skill = skill <= 0 ? rnd(50) + 51 : clamp(skill, 1, 101); - clients.add(ci); - ci->state.lasttimeplayed = lastmillis; - copystring(ci->name, "bot", MAXNAMELEN+1); - ci->state.state = CS_DEAD; - ci->team = team; - ci->playermodel = rnd(128); - ci->playercolor = rnd(0x8000); - ci->aireinit = 2; - ci->connected = true; - dorefresh = true; - return true; - } - - void deleteai(clientinfo *ci) - { - int cn = ci->clientnum - MAXCLIENTS; - if(!bots.inrange(cn)) return; - if(ci->ownernum >= 0 && !ci->aireinit && smode) smode->leavegame(ci, true); - sendf(-1, 1, "ri2", N_CDIS, ci->clientnum); - clientinfo *owner = (clientinfo *)getclientinfo(ci->ownernum); - if(owner) owner->bots.removeobj(ci); - clients.removeobj(ci); - DELETEP(bots[cn]); - dorefresh = true; - } - - bool deleteai() - { - loopvrev(bots) if(bots[i] && bots[i]->ownernum >= 0) - { - deleteai(bots[i]); - return true; - } - return false; - } - - void reinitai(clientinfo *ci) - { - if(ci->ownernum < 0) deleteai(ci); - else if(ci->aireinit >= 1) - { - sendf(-1, 1, "ri8s", N_INITAI, ci->clientnum, ci->ownernum, ci->state.aitype, ci->state.skill, ci->playermodel, ci->playercolor, ci->team, ci->name); - if(ci->aireinit == 2) - { - ci->reassign(); - if(ci->state.state==CS_ALIVE) sendspawn(ci); - else sendresume(ci); - } - ci->aireinit = 0; - } - } - - void shiftai(clientinfo *ci, clientinfo *owner = NULL) - { - if(ci->ownernum >= 0 && !ci->aireinit && smode) smode->leavegame(ci, true); - clientinfo *prevowner = (clientinfo *)getclientinfo(ci->ownernum); - if(prevowner) prevowner->bots.removeobj(ci); - if(!owner) { ci->aireinit = 0; ci->ownernum = -1; } - else if(ci->ownernum != owner->clientnum) { ci->aireinit = 2; ci->ownernum = owner->clientnum; owner->bots.add(ci); } - dorefresh = true; - } - - void removeai(clientinfo *ci) - { // either schedules a removal, or someone else to assign to - - loopvrev(ci->bots) shiftai(ci->bots[i], findaiclient(ci)); - } - - bool reassignai() - { - clientinfo *hi = NULL, *lo = NULL; - loopv(clients) - { - clientinfo *ci = clients[i]; - if(!validaiclient(ci)) continue; - if(!lo || ci->bots.length() < lo->bots.length()) lo = ci; - if(!hi || ci->bots.length() > hi->bots.length()) hi = ci; - } - if(hi && lo && hi->bots.length() - lo->bots.length() > 1) - { - loopvrev(hi->bots) - { - shiftai(hi->bots[i], lo); - return true; - } - } - return false; - } - - - void checksetup() - { - if(m_teammode && botbalance) balanceteams(); - loopvrev(bots) if(bots[i]) reinitai(bots[i]); - } - - void clearai() - { // clear and remove all ai immediately - loopvrev(bots) if(bots[i]) deleteai(bots[i]); - } - - void checkai() - { - if(!dorefresh) return; - dorefresh = false; - if(m_botmode && numclients(-1, false, true)) - { - checksetup(); - while(reassignai()); - } - else clearai(); - } - - void reqadd(clientinfo *ci, int skill) - { - if(!ci->local && !ci->privilege) return; - if(!addai(skill, !ci->local && ci->privilege < PRIV_ADMIN ? botlimit : -1)) sendf(ci->clientnum, 1, "ris", N_SERVMSG, "failed to create or assign bot"); - } - - void reqdel(clientinfo *ci) - { - if(!ci->local && !ci->privilege) return; - if(!deleteai()) sendf(ci->clientnum, 1, "ris", N_SERVMSG, "failed to remove any bots"); - } - - void setbotlimit(clientinfo *ci, int limit) - { - if(ci && !ci->local && ci->privilege < PRIV_ADMIN) return; - botlimit = clamp(limit, 0, MAXBOTS); - dorefresh = true; - defformatstring(msg, "bot limit is now %d", botlimit); - sendservmsg(msg); - } - - void setbotbalance(clientinfo *ci, bool balance) - { - if(ci && !ci->local && !ci->privilege) return; - botbalance = balance ? 1 : 0; - dorefresh = true; - defformatstring(msg, "bot team balancing is now %s", botbalance ? "enabled" : "disabled"); - sendservmsg(msg); - } - - - void changemap() - { - dorefresh = true; - loopv(clients) if(clients[i]->local || clients[i]->privilege) return; - if(botbalance != (serverbotbalance != 0)) setbotbalance(NULL, serverbotbalance != 0); - } - - void addclient(clientinfo *ci) - { - if(ci->state.aitype == AI_NONE) dorefresh = true; - } - - void changeteam(clientinfo *ci) - { - if(ci->state.aitype == AI_NONE) dorefresh = true; - } -} diff --git a/src/game/client.cc b/src/game/client.cc index 444933d..1912741 100644 --- a/src/game/client.cc +++ b/src/game/client.cc @@ -133,136 +133,16 @@ namespace game int lastping = 0; bool connected = false, remote = false, demoplayback = false, gamepaused = false; - int sessionid = 0, mastermode = MM_OPEN, gamespeed = 100; - string servdesc = "", servauth = "", connectpass = ""; + int sessionid = 0, gamespeed = 100; + string servdesc = "", servauth = ""; VARP(deadpush, 1, 2, 20); - void switchname(const char *name) - { - filtertext(player1->name, name, false, false, MAXNAMELEN); - if(!player1->name[0]) copystring(player1->name, "unnamed"); - addmsg(N_SWITCHNAME, "rs", player1->name); - } - void printname() - { - conoutf("your name is: %s", colorname(player1)); - } - ICOMMAND(name, "sN", (char *s, int *numargs), - { - if(*numargs > 0) switchname(s); - else if(!*numargs) printname(); - else result(colorname(player1)); - }); - ICOMMAND(getname, "", (), result(player1->name)); - - void switchteam(const char *team) - { - int num = isdigit(team[0]) ? parseint(team) : teamnumber(team); - if(!validteam(num)) return; - if(player1->clientnum < 0) player1->team = num; - else addmsg(N_SWITCHTEAM, "ri", num); - } - void printteam() - { - if((player1->clientnum >= 0 && !m_teammode) || !validteam(player1->team)) conoutf("you are not in a team"); - else conoutf("your team is: \fs%s%s\fr", teamtextcode[player1->team], teamnames[player1->team]); - } - ICOMMAND(team, "sN", (char *s, int *numargs), - { - if(*numargs > 0) switchteam(s); - else if(!*numargs) printteam(); - else if((player1->clientnum < 0 || m_teammode) && validteam(player1->team)) result(tempformatstring("\fs%s%s\fr", teamtextcode[player1->team], teamnames[player1->team])); - }); - ICOMMAND(getteam, "", (), intret((player1->clientnum < 0 || m_teammode) && validteam(player1->team) ? player1->team : 0)); - ICOMMAND(getteamname, "i", (int *num), result(teamname(*num))); - - struct authkey - { - char *name, *key, *desc; - int lastauth; - - authkey(const char *name, const char *key, const char *desc) - : name(newstring(name)), key(newstring(key)), desc(newstring(desc)), - lastauth(0) - { - } - - ~authkey() - { - DELETEA(name); - DELETEA(key); - DELETEA(desc); - } - }; - vector authkeys; - - authkey *findauthkey(const char *desc = "") - { - loopv(authkeys) if(!strcmp(authkeys[i]->desc, desc) && !strcasecmp(authkeys[i]->name, player1->name)) return authkeys[i]; - loopv(authkeys) if(!strcmp(authkeys[i]->desc, desc)) return authkeys[i]; - return NULL; - } - - VARP(autoauth, 0, 1, 1); - - void addauthkey(const char *name, const char *key, const char *desc) - { - loopvrev(authkeys) if(!strcmp(authkeys[i]->desc, desc) && !strcmp(authkeys[i]->name, name)) delete authkeys.remove(i); - if(name[0] && key[0]) authkeys.add(new authkey(name, key, desc)); - } - ICOMMAND(authkey, "sss", (char *name, char *key, char *desc), addauthkey(name, key, desc)); - - bool hasauthkey(const char *name, const char *desc) - { - if(!name[0] && !desc[0]) return authkeys.length() > 0; - loopvrev(authkeys) if(!strcmp(authkeys[i]->desc, desc) && !strcmp(authkeys[i]->name, name)) return true; - return false; - } - - ICOMMAND(hasauthkey, "ss", (char *name, char *desc), intret(hasauthkey(name, desc) ? 1 : 0)); - - void genauthkey(const char *secret) - { - if(!secret[0]) { conoutf(CON_ERROR, "you must specify a secret password"); return; } - vector privkey, pubkey; - genprivkey(secret, privkey, pubkey); - conoutf("private key: %s", privkey.getbuf()); - conoutf("public key: %s", pubkey.getbuf()); - result(privkey.getbuf()); - } - COMMAND(genauthkey, "s"); - - void getpubkey(const char *desc) - { - authkey *k = findauthkey(desc); - if(!k) { if(desc[0]) conoutf("no authkey found: %s", desc); else conoutf("no global authkey found"); return; } - vector pubkey; - if(!calcpubkey(k->key, pubkey)) { conoutf("failed calculating pubkey"); return; } - result(pubkey.getbuf()); - } - COMMAND(getpubkey, "s"); - - void saveauthkeys() - { - string fname = "config/auth.cfg"; - stream *f = openfile(path(fname), "w"); - if(!f) { conoutf(CON_ERROR, "failed to open %s for writing", fname); return; } - loopv(authkeys) - { - authkey *a = authkeys[i]; - f->printf("authkey %s %s %s\n", escapestring(a->name), escapestring(a->key), escapestring(a->desc)); - } - conoutf("saved authkeys to %s", fname); - delete f; - } - COMMAND(saveauthkeys, ""); - void sendmapinfo() { if(!connected) return; sendcrc = true; - if(player1->state!=CS_SPECTATOR || player1->privilege || !remote) senditemstoserver = true; + senditemstoserver = true; } void writeclientinfo(stream *f) @@ -285,300 +165,16 @@ namespace game { addmsg(N_EDITMODE, "ri", on ? 1 : 0); if(player1->state==CS_DEAD) deathstate(player1, true); - else if(player1->state==CS_EDITING && player1->editstate==CS_DEAD) showscores(false); disablezoom(); player1->suicided = player1->respawned = -2; checkfollow(); } - const char *getclientname(int cn) - { - gameent *d = getclient(cn); - return d ? d->name : ""; - } - ICOMMAND(getclientname, "i", (int *cn), result(getclientname(*cn))); - - ICOMMAND(getclientcolorname, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) result(colorname(d)); - }); - - int getclientteam(int cn) - { - gameent *d = getclient(cn); - return m_teammode && d && validteam(d->team) ? d->team : 0; - } - ICOMMAND(getclientteam, "i", (int *cn), intret(getclientteam(*cn))); - - int getclientmodel(int cn) - { - gameent *d = getclient(cn); - return d ? d->playermodel : -1; - } - ICOMMAND(getclientmodel, "i", (int *cn), intret(getclientmodel(*cn))); - - const char *getclienticon(int cn) - { - gameent *d = getclient(cn); - if(!d || d->state==CS_SPECTATOR) return "spectator"; - const playermodelinfo &mdl = getplayermodelinfo(d); - return m_teammode && validteam(d->team) ? mdl.icon[d->team] : mdl.icon[0]; - } - ICOMMAND(getclienticon, "i", (int *cn), result(getclienticon(*cn))); - - int getclientcolor(int cn) - { - gameent *d = getclient(cn); - return d && d->state!=CS_SPECTATOR ? getplayercolor(d, m_teammode && validteam(d->team) ? d->team : 0) : 0xFFFFFF; - } - ICOMMAND(getclientcolor, "i", (int *cn), intret(getclientcolor(*cn))); - - ICOMMAND(getclientfrags, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) intret(d->frags); - }); - - ICOMMAND(getclientflags, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) intret(d->flags); - }); - - ICOMMAND(getclientdeaths, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) intret(d->deaths); - }); - - bool ismaster(int cn) - { - gameent *d = getclient(cn); - return d && d->privilege >= PRIV_MASTER; - } - ICOMMAND(ismaster, "i", (int *cn), intret(ismaster(*cn) ? 1 : 0)); - - bool isauth(int cn) - { - gameent *d = getclient(cn); - return d && d->privilege >= PRIV_AUTH; - } - ICOMMAND(isauth, "i", (int *cn), intret(isauth(*cn) ? 1 : 0)); - - bool isadmin(int cn) - { - gameent *d = getclient(cn); - return d && d->privilege >= PRIV_ADMIN; - } - ICOMMAND(isadmin, "i", (int *cn), intret(isadmin(*cn) ? 1 : 0)); - - ICOMMAND(getmastermode, "", (), intret(mastermode)); - ICOMMAND(getmastermodename, "i", (int *mm), result(server::mastermodename(*mm, ""))); - - bool isspectator(int cn) - { - gameent *d = getclient(cn); - return d && d->state==CS_SPECTATOR; - } - ICOMMAND(isspectator, "i", (int *cn), intret(isspectator(*cn) ? 1 : 0)); - - ICOMMAND(islagged, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) intret(d->state==CS_LAGGED ? 1 : 0); - }); - - ICOMMAND(isdead, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) intret(d->state==CS_DEAD ? 1 : 0); - }); - - bool isai(int cn, int type) - { - gameent *d = getclient(cn); - int aitype = type > 0 && type < AI_MAX ? type : AI_BOT; - return d && d->aitype==aitype; - } - ICOMMAND(isai, "ii", (int *cn, int *type), intret(isai(*cn, *type) ? 1 : 0)); - - int parseplayer(const char *arg) - { - char *end; - int n = strtol(arg, &end, 10); - if(*arg && !*end) - { - if(n!=player1->clientnum && !clients.inrange(n)) return -1; - return n; - } - // try case sensitive first - loopv(players) - { - gameent *o = players[i]; - if(!strcmp(arg, o->name)) return o->clientnum; - } - // nothing found, try case insensitive - loopv(players) - { - gameent *o = players[i]; - if(!strcasecmp(arg, o->name)) return o->clientnum; - } - return -1; - } - ICOMMAND(getclientnum, "s", (char *name), intret(name[0] ? parseplayer(name) : player1->clientnum)); - - void listclients(bool local, bool bots) - { - vector buf; - string cn; - int numclients = 0; - if(local && connected) - { - formatstring(cn, "%d", player1->clientnum); - buf.put(cn, strlen(cn)); - numclients++; - } - loopv(clients) if(clients[i] && (bots || clients[i]->aitype == AI_NONE)) - { - formatstring(cn, "%d", clients[i]->clientnum); - if(numclients++) buf.add(' '); - buf.put(cn, strlen(cn)); - } - buf.add('\0'); - result(buf.getbuf()); - } - ICOMMAND(listclients, "bb", (int *local, int *bots), listclients(*local>0, *bots!=0)); - - void clearbans() - { - addmsg(N_CLEARBANS, "r"); - } - COMMAND(clearbans, ""); - - void kick(const char *victim, const char *reason) - { - int vn = parseplayer(victim); - if(vn>=0 && vn!=player1->clientnum) addmsg(N_KICK, "ris", vn, reason); - } - COMMAND(kick, "ss"); - - void authkick(const char *desc, const char *victim, const char *reason) - { - authkey *a = findauthkey(desc); - int vn = parseplayer(victim); - if(a && vn>=0 && vn!=player1->clientnum) - { - a->lastauth = lastmillis; - addmsg(N_AUTHKICK, "rssis", a->desc, a->name, vn, reason); - } - } - ICOMMAND(authkick, "ss", (const char *victim, const char *reason), authkick("", victim, reason)); - ICOMMAND(sauthkick, "ss", (const char *victim, const char *reason), if(servauth[0]) authkick(servauth, victim, reason)); - ICOMMAND(dauthkick, "sss", (const char *desc, const char *victim, const char *reason), if(desc[0]) authkick(desc, victim, reason)); - - vector ignores; - - void ignore(int cn) - { - gameent *d = getclient(cn); - if(!d || d == player1) return; - conoutf("ignoring %s", d->name); - if(ignores.find(cn) < 0) ignores.add(cn); - } - - void unignore(int cn) - { - if(ignores.find(cn) < 0) return; - gameent *d = getclient(cn); - if(d) conoutf("stopped ignoring %s", d->name); - ignores.removeobj(cn); - } - - bool isignored(int cn) { return ignores.find(cn) >= 0; } - - ICOMMAND(ignore, "s", (char *arg), ignore(parseplayer(arg))); - ICOMMAND(unignore, "s", (char *arg), unignore(parseplayer(arg))); - ICOMMAND(isignored, "s", (char *arg), intret(isignored(parseplayer(arg)) ? 1 : 0)); - - void setteam(const char *who, const char *team) - { - int i = parseplayer(who); - if(i < 0) return; - int num = isdigit(team[0]) ? parseint(team) : teamnumber(team); - if(!validteam(num)) return; - addmsg(N_SETTEAM, "rii", i, num); - } - COMMAND(setteam, "ss"); - - void hashpwd(const char *pwd) - { - if(player1->clientnum<0) return; - string hash; - server::hashpassword(player1->clientnum, sessionid, pwd, hash); - result(hash); - } - COMMAND(hashpwd, "s"); - - void setmaster(const char *arg, const char *who) - { - if(!arg[0]) return; - int val = 1, cn = player1->clientnum; - if(who[0]) - { - cn = parseplayer(who); - if(cn < 0) return; - } - string hash = ""; - if(!arg[1] && isdigit(arg[0])) val = parseint(arg); - else - { - if(cn != player1->clientnum) return; - server::hashpassword(player1->clientnum, sessionid, arg, hash); - } - addmsg(N_SETMASTER, "riis", cn, val, hash); - } - COMMAND(setmaster, "ss"); - ICOMMAND(mastermode, "i", (int *val), addmsg(N_MASTERMODE, "ri", *val)); - - bool tryauth(const char *desc) - { - authkey *a = findauthkey(desc); - if(!a) return false; - a->lastauth = lastmillis; - addmsg(N_AUTHTRY, "rss", a->desc, a->name); - return true; - } - ICOMMAND(auth, "s", (char *desc), tryauth(desc)); - ICOMMAND(sauth, "", (), if(servauth[0]) tryauth(servauth)); - ICOMMAND(dauth, "s", (char *desc), if(desc[0]) tryauth(desc)); - - ICOMMAND(getservauth, "", (), result(servauth)); - - void togglespectator(int val, const char *who) - { - int i = who[0] ? parseplayer(who) : player1->clientnum; - if(i>=0) addmsg(N_SPECTATOR, "rii", i, val); - } - ICOMMAND(spectator, "is", (int *val, char *who), togglespectator(*val, who)); - - ICOMMAND(checkmaps, "", (), addmsg(N_CHECKMAPS, "r")); - - int gamemode = INT_MAX, nextmode = INT_MAX; string clientmap = ""; void changemapserv(const char *name, int mode) // forced map change from the server { - if(multiplayer(false) && !m_mp(mode)) - { - conoutf(CON_ERROR, "mode %s (%d) not supported in multiplayer", server::modeprettyname(gamemode), gamemode); - loopi(NUMGAMEMODES) if(m_mp(STARTGAMEMODE + i)) { mode = STARTGAMEMODE + i; break; } - } - - gamemode = mode; - nextmode = mode; if(editmode) toggleedit(); - if(m_demo) { entities::resetspawns(); return; } if((m_edit && !name[0]) || !load_world(name)) { emptymap(0, true, name); @@ -587,50 +183,15 @@ namespace game startgame(); } - void setmode(int mode) - { - if(multiplayer(false) && !m_mp(mode)) - { - conoutf(CON_ERROR, "mode %s (%d) not supported in multiplayer", server::modeprettyname(mode), mode); - intret(0); - return; - } - nextmode = mode; - intret(1); - } - ICOMMAND(mode, "i", (int *val), setmode(*val)); - ICOMMAND(getmode, "", (), intret(gamemode)); - ICOMMAND(getnextmode, "", (), intret(m_valid(nextmode) ? nextmode : (remote ? 1 : 0))); - ICOMMAND(getmodename, "i", (int *mode), result(server::modename(*mode, ""))); - ICOMMAND(getmodeprettyname, "i", (int *mode), result(server::modeprettyname(*mode, ""))); - ICOMMAND(timeremaining, "i", (int *formatted), - { - int val = max(maplimit - lastmillis + 999, 0)/1000; - if(*formatted) result(tempformatstring("%d:%02d", val/60, val%60)); - else intret(val); - }); - ICOMMAND(intermission, "", (), intret(intermission ? 1 : 0)); - - ICOMMANDS("m_teammode", "i", (int *mode), { int gamemode = *mode; intret(m_teammode); }); - ICOMMANDS("m_rail", "i", (int *mode), { int gamemode = *mode; intret(m_rail); }); - ICOMMANDS("m_pulse", "i", (int *mode), { int gamemode = *mode; intret(m_pulse); }); - ICOMMANDS("m_demo", "i", (int *mode), { int gamemode = *mode; intret(m_demo); }); - ICOMMANDS("m_edit", "i", (int *mode), { int gamemode = *mode; intret(m_edit); }); - ICOMMANDS("m_lobby", "i", (int *mode), { int gamemode = *mode; intret(m_lobby); }); - ICOMMANDS("m_timed", "i", (int *mode), { int gamemode = *mode; intret(m_timed); }); void changemap(const char *name, int mode) // request map change, server may ignore { - if(!remote) - { - server::forcemap(name, mode); - if(!isconnected()) localconnect(); - } - else if(player1->state!=CS_SPECTATOR || player1->privilege) addmsg(N_MAPVOTE, "rsi", name, mode); + server::forcemap(name, mode); + if(!isconnected()) localconnect(); } void changemap(const char *name) { - changemap(name, m_valid(nextmode) ? nextmode : (remote ? 1 : 0)); + changemap(name, 0); } ICOMMAND(map, "s", (char *name), changemap(name)); @@ -863,7 +424,7 @@ namespace game static uchar buf[MAXTRANS]; ucharbuf p(buf, sizeof(buf)); putint(p, type); - int numi = 1, numf = 0, nums = 0, mcn = -1; + int numi = 1, numf = 0, nums = 0; bool reliable = false; if(fmt) { @@ -874,8 +435,6 @@ namespace game case 'r': reliable = true; break; case 'c': { - gameent *d = va_arg(args, gameent *); - mcn = !d || d == player1 ? -1 : d->clientnum; break; } case 'v': @@ -908,27 +467,16 @@ namespace game int num = nums || numf ? 0 : numi, msgsize = server::msgsizelookup(type); if(msgsize && num!=msgsize) { fatal("inconsistent msg size for %d (%d != %d)", type, num, msgsize); } if(reliable) messagereliable = true; - if(mcn != messagecn) - { - static uchar mbuf[16]; - ucharbuf m(mbuf, sizeof(mbuf)); - putint(m, N_FROMAI); - putint(m, mcn); - messages.put(mbuf, m.length()); - messagecn = mcn; - } messages.put(buf, p.length()); return true; } void connectattempt(const char *name, const char *password, const ENetAddress &address) { - copystring(connectpass, password); } void connectfail() { - memset(connectpass, 0, sizeof(connectpass)); } void gameconnect(bool _remote) @@ -939,19 +487,16 @@ namespace game void gamedisconnect(bool cleanup) { if(remote) stopfollowing(); - ignores.setsize(0); connected = remote = false; player1->clientnum = -1; if(editmode) toggleedit(); sessionid = 0; - mastermode = MM_OPEN; messages.setsize(0); messagereliable = false; messagecn = -1; player1->respawn(); player1->lifesequence = 0; player1->state = CS_ALIVE; - player1->privilege = PRIV_NONE; sendcrc = senditemstoserver = false; demoplayback = false; gamepaused = false; @@ -959,7 +504,6 @@ namespace game clearclients(false); if(cleanup) { - nextmode = gamemode = INT_MAX; clientmap[0] = '\0'; } } @@ -1044,14 +588,14 @@ namespace game loopv(players) { gameent *d = players[i]; - if((d == player1 || d->ai) && (d->state == CS_ALIVE || d->state == CS_EDITING)) + if((d == player1) && (d->state == CS_ALIVE || d->state == CS_EDITING)) { packetbuf q(100); sendposition(d, q); for(int j = i+1; j < players.length(); j++) { gameent *d = players[j]; - if((d == player1 || d->ai) && (d->state == CS_ALIVE || d->state == CS_EDITING)) + if((d == player1) && (d->state == CS_ALIVE || d->state == CS_EDITING)) sendposition(d, q); } sendclientpacket(q.finalize(), 0); @@ -1113,25 +657,6 @@ namespace game sendstring(player1->name, p); putint(p, player1->playermodel); putint(p, player1->playercolor); - string hash = ""; - if(connectpass[0]) - { - server::hashpassword(player1->clientnum, sessionid, connectpass, hash); - memset(connectpass, 0, sizeof(connectpass)); - } - sendstring(hash, p); - authkey *a = servauth[0] && autoauth ? findauthkey(servauth) : NULL; - if(a) - { - a->lastauth = lastmillis; - sendstring(a->desc, p); - sendstring(a->name, p); - } - else - { - sendstring("", p); - sendstring("", p); - } sendclientpacket(p.finalize(), 1); } @@ -1165,7 +690,6 @@ namespace game int type; while(p.remaining()) switch(type = getint(p)) { - case N_DEMOPACKET: break; case N_POS: // position of another client { int cn = getuint(p), physstate = p.get(), flags = getuint(p); @@ -1288,18 +812,13 @@ namespace game } } - extern int deathscore; - void parsemessages(int cn, gameent *d, ucharbuf &p) { static char text[MAXTRANS]; int type; - bool mapchanged = false, demopacket = false; while(p.remaining()) switch(type = getint(p)) { - case N_DEMOPACKET: demopacket = true; break; - case N_SERVINFO: // welcome messsage from the server { int mycn = getint(p), prot = getint(p); @@ -1311,9 +830,6 @@ namespace game } sessionid = getint(p); player1->clientnum = mycn; // we are now connected - if(getint(p) > 0) conoutf("this server is password protected"); - getstring(servdesc, p, sizeof(servdesc)); - getstring(servauth, p, sizeof(servauth)); sendintro(); break; } @@ -1330,11 +846,8 @@ namespace game bool val = getint(p) > 0; int cn = getint(p); gameent *a = cn >= 0 ? getclient(cn) : NULL; - if(!demopacket) - { - gamepaused = val; - player1->attacking = ACT_IDLE; - } + gamepaused = val; + player1->attacking = ACT_IDLE; if(a) conoutf("%s %s the game", colorname(a), val ? "paused" : "resumed"); else conoutf("game is %s", val ? "paused" : "resumed"); break; @@ -1344,7 +857,7 @@ namespace game { int val = clamp(getint(p), 10, 1000), cn = getint(p); gameent *a = cn >= 0 ? getclient(cn) : NULL; - if(!demopacket) gamespeed = val; + gamespeed = val; if(a) conoutf("%s set gamespeed to %d", colorname(a), val); else conoutf("gamespeed is %d", val); break; @@ -1368,7 +881,6 @@ namespace game if(!d) return; getstring(text, p); filtertext(text, text, true, true); - if(isignored(d->clientnum)) break; if(d->state!=CS_DEAD && d->state!=CS_SPECTATOR) particle_textcopy(d->abovehead(), text, PART_TEXT, 2000, 0x32FF64, 4.0f, -8); conoutf(CON_CHAT, "%s:%s %s", chatcolorname(d), teamtextcode[0], text); @@ -1381,7 +893,7 @@ namespace game gameent *t = getclient(tcn); getstring(text, p); filtertext(text, text, true, true); - if(!t || isignored(t->clientnum)) break; + if(!t) break; int team = validteam(t->team) ? t->team : 0; if(t->state!=CS_DEAD && t->state!=CS_SPECTATOR) particle_textcopy(t->abovehead(), text, PART_TEXT, 2000, teamtextcolor[team], 4.0f, -8); @@ -1392,38 +904,10 @@ namespace game case N_MAPCHANGE: getstring(text, p); changemapserv(text, getint(p)); - mapchanged = true; if(getint(p)) entities::spawnitems(); else senditemstoserver = false; break; - case N_FORCEDEATH: - { - int cn = getint(p); - gameent *d = cn==player1->clientnum ? player1 : newclient(cn); - if(!d) break; - if(d==player1) - { - if(editmode) toggleedit(); - if(deathscore) showscores(true); - } - else d->resetinterp(); - d->state = CS_DEAD; - checkfollow(); - break; - } - - case N_ITEMLIST: - { - int n; - while((n = getint(p))>=0 && !p.overread()) - { - if(mapchanged) entities::setspawn(n, true); - getint(p); // type - } - break; - } - case N_INITCLIENT: // another client either connected or changed name/team { int cn = getint(p); @@ -1441,7 +925,7 @@ namespace game if(!text[0]) copystring(text, "unnamed"); if(d->name[0]) // already connected { - if(strcmp(d->name, text) && !isignored(d->clientnum)) + if(strcmp(d->name, text)) conoutf("%s is now known as %s", colorname(d), colorname(d, text)); } else // new client @@ -1465,30 +949,12 @@ namespace game if(!text[0]) copystring(text, "unnamed"); if(strcmp(text, d->name)) { - if(!isignored(d->clientnum)) conoutf("%s is now known as %s", colorname(d), colorname(d, text)); + conoutf("%s is now known as %s", colorname(d), colorname(d, text)); copystring(d->name, text, MAXNAMELEN+1); } } break; - case N_SWITCHMODEL: - { - int model = getint(p); - if(d) - { - d->playermodel = model; - if(d->ragdoll) cleanragdoll(d); - } - break; - } - - case N_SWITCHCOLOR: - { - int color = getint(p); - if(d) d->playercolor = color; - break; - } - case N_CDIS: clientdisconnected(getint(p)); break; @@ -1525,58 +991,14 @@ namespace game else findplayerspawn(s, -1, m_teammode ? s->team : 0); if(s == player1) { - showscores(false); lasthit = 0; } if(cmode) cmode->respawned(s); - ai::spawned(s); checkfollow(); addmsg(N_SPAWN, "rcii", s, s->lifesequence, s->gunselect); break; } - case N_SHOTFX: - { - int scn = getint(p), atk = getint(p), id = getint(p); - vec from, to; - loopk(3) from[k] = getint(p)/DMF; - loopk(3) to[k] = getint(p)/DMF; - gameent *s = getclient(scn); - if(!s || !validatk(atk)) break; - int gun = attacks[atk].gun; - s->gunselect = gun; - s->ammo[gun] -= attacks[atk].use; - s->gunwait = attacks[atk].attackdelay; - int prevaction = s->lastaction; - s->lastaction = lastmillis; - s->lastattack = atk; - shoteffects(atk, from, to, s, false, id, prevaction); - break; - } - - case N_EXPLODEFX: - { - int ecn = getint(p), atk = getint(p), id = getint(p); - gameent *e = getclient(ecn); - if(!e || !validatk(atk)) break; - explodeeffects(atk, e, false, id); - break; - } - case N_DAMAGE: - { - int tcn = getint(p), - acn = getint(p), - damage = getint(p), - health = getint(p); - gameent *target = getclient(tcn), - *actor = getclient(acn); - if(!target || !actor) break; - target->health = health; - if(target->state == CS_ALIVE && actor != player1) target->lastpain = lastmillis; - damaged(damage, target, actor, false); - break; - } - case N_HITPUSH: { int tcn = getint(p), atk = getint(p), damage = getint(p); @@ -1595,42 +1017,11 @@ namespace game *actor = getclient(acn); if(!actor) break; actor->frags = frags; - if(m_teammode) setteaminfo(actor->team, tfrags); -#if 0 - extern int hidefrags; - if(actor!=player1 && (!cmode || !cmode->hidefrags() || !hidefrags)) - particle_textcopy(actor->abovehead(), tempformatstring("%d", actor->frags), PART_TEXT, 2000, 0x32FF64, 4.0f, -8); -#endif if(!victim) break; killed(victim, actor); break; } - case N_TEAMINFO: - loopi(MAXTEAMS) - { - int frags = getint(p); - if(m_teammode) setteaminfo(1+i, frags); - } - break; - - case N_GUNSELECT: - { - if(!d) return; - int gun = getint(p); - if(!validgun(gun)) return; - d->gunselect = gun; - playsound(S_WEAPLOAD, &d->o); - break; - } - - case N_TAUNT: - { - if(!d) return; - d->lasttaunt = lastmillis; - break; - } - case N_RESUME: { for(;;) @@ -1643,34 +1034,6 @@ namespace game break; } - case N_ITEMSPAWN: - { - int i = getint(p); - if(!entities::ents.inrange(i)) break; - entities::setspawn(i, true); - ai::itemspawned(i); - playsound(S_ITEMSPAWN, &entities::ents[i]->o, NULL, 0, 0, 0, -1, 0, 1500); - #if 0 - const char *name = entities::itemname(i); - if(name) particle_text(entities::ents[i]->o, name, PART_TEXT, 2000, 0x32FF64, 4.0f, -8); - #endif - int icon = entities::itemicon(i); - if(icon >= 0) particle_icon(vec(0.0f, 0.0f, 4.0f).add(entities::ents[i]->o), icon%4, icon/4, PART_HUD_ICON, 2000, 0xFFFFFF, 2.0f, -8); - break; - } - - case N_ITEMACC: // server acknowledges that I picked up this item - { - int i = getint(p), cn = getint(p); - if(cn >= 0) - { - gameent *d = getclient(cn); - entities::pickupeffects(i, d); - } - else entities::setspawn(i, true); - break; - } - case N_CLIPBOARD: { int cn = getint(p), unpacklen = getint(p), packlen = getint(p); @@ -1825,57 +1188,6 @@ namespace game conoutf("%s", text); break; - case N_SENDDEMOLIST: - { - int demos = getint(p); - if(demos <= 0) conoutf("no demos available"); - else loopi(demos) - { - getstring(text, p); - if(p.overread()) break; - conoutf("%d. %s", i+1, text); - } - break; - } - - case N_DEMOPLAYBACK: - { - int on = getint(p); - if(on) player1->state = CS_SPECTATOR; - else clearclients(); - demoplayback = on!=0; - player1->clientnum = getint(p); - gamepaused = false; - checkfollow(); - execident(on ? "demostart" : "demoend"); - break; - } - - case N_CURRENTMASTER: - { - int mm = getint(p), mn; - loopv(players) players[i]->privilege = PRIV_NONE; - while((mn = getint(p))>=0 && !p.overread()) - { - gameent *m = mn==player1->clientnum ? player1 : newclient(mn); - int priv = getint(p); - if(m) m->privilege = priv; - } - if(mm != mastermode) - { - mastermode = mm; - conoutf("mastermode is %s (%d)", server::mastermodename(mastermode), mastermode); - } - break; - } - - case N_MASTERMODE: - { - mastermode = getint(p); - conoutf("mastermode is %s (%d)", server::mastermodename(mastermode), mastermode); - break; - } - case N_EDITMODE: { int val = getint(p); @@ -1894,44 +1206,6 @@ namespace game break; } - case N_SPECTATOR: - { - int sn = getint(p), val = getint(p); - gameent *s; - if(sn==player1->clientnum) - { - s = player1; - if(val && remote && !player1->privilege) senditemstoserver = false; - } - else s = newclient(sn); - if(!s) return; - if(val) - { - if(s==player1) - { - if(editmode) toggleedit(); - if(s->state==CS_DEAD) showscores(false); - disablezoom(); - } - s->state = CS_SPECTATOR; - } - else if(s->state==CS_SPECTATOR) deathstate(s, true); - checkfollow(); - break; - } - - case N_SETTEAM: - { - int wn = getint(p), team = getint(p), reason = getint(p); - gameent *w = getclient(wn); - if(!w) return; - w->team = validteam(team) ? team : 0; - static const char * const fmt[2] = { "%s switched to team %s", "%s forced to team %s"}; - if(reason >= 0 && size_t(reason) < sizeof(fmt)/sizeof(fmt[0])) - conoutf(fmt[reason], colorname(w), teamnames[w->team]); - break; - } - case N_NEWMAP: { int size = getint(p); @@ -1946,46 +1220,6 @@ namespace game break; } - case N_REQAUTH: - { - getstring(text, p); - if(autoauth && text[0] && tryauth(text)) conoutf("server requested authkey \"%s\"", text); - break; - } - - case N_AUTHCHAL: - { - getstring(text, p); - authkey *a = findauthkey(text); - uint id = (uint)getint(p); - getstring(text, p); - if(a && a->lastauth && lastmillis - a->lastauth < 60*1000) - { - vector buf; - answerchallenge(a->key, text, buf); - //conoutf(CON_DEBUG, "answering %u, challenge %s with %s", id, text, buf.getbuf()); - packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - putint(p, N_AUTHANS); - sendstring(a->desc, p); - putint(p, id); - sendstring(buf.getbuf(), p); - sendclientpacket(p.finalize(), 1); - } - break; - } - - case N_INITAI: - { - int bn = getint(p), on = getint(p), at = getint(p), sk = clamp(getint(p), 1, 101), pm = getint(p), col = getint(p), team = getint(p); - string name; - getstring(text, p); - filtertext(name, text, false, false, MAXNAMELEN); - gameent *b = newclient(bn); - if(!b) break; - ai::init(b, at, on, sk, bn, pm, col, name, team); - break; - } - case N_SERVCMD: getstring(text, p); break; @@ -1998,41 +1232,6 @@ namespace game void receivefile(packetbuf &p) { - int type; - while(p.remaining()) switch(type = getint(p)) - { - case N_DEMOPACKET: return; - case N_SENDDEMO: - { - defformatstring(fname, "%d.dmo", lastmillis); - stream *demo = openrawfile(fname, "wb"); - if(!demo) return; - conoutf("received demo \"%s\"", fname); - ucharbuf b = p.subbuf(p.remaining()); - demo->write(b.buf, b.maxlen); - delete demo; - break; - } - - case N_SENDMAP: - { - if(!m_edit) return; - string oldname; - copystring(oldname, getclientmap()); - defformatstring(mname, "getmap_%d", lastmillis); - defformatstring(fname, "media/map/%s.ogz", mname); - stream *map = openrawfile(path(fname), "wb"); - if(!map) return; - conoutf("received map"); - ucharbuf b = p.subbuf(p.remaining()); - map->write(b.buf, b.maxlen); - delete map; - if(load_world(mname, oldname[0] ? oldname : NULL)) - entities::spawnitems(true); - remove(findfile(fname, "rb")); - break; - } - } } void parsepacketclient(int chan, packetbuf &p) // processes any updates from the server @@ -2053,106 +1252,5 @@ namespace game break; } } - - void getmap() - { - if(!m_edit) { conoutf(CON_ERROR, "\"getmap\" only works in edit mode"); return; } - conoutf("getting map..."); - addmsg(N_GETMAP, "r"); - } - COMMAND(getmap, ""); - - void stopdemo() - { - if(remote) - { - if(player1->privilegeprivilegeprivilegestate==CS_SPECTATOR && remote && !player1->privilege)) { conoutf(CON_ERROR, "\"sendmap\" only works in coop edit mode"); return; } - conoutf("sending map..."); - defformatstring(mname, "sendmap_%d", lastmillis); - save_world(mname, true); - defformatstring(fname, "media/map/%s.ogz", mname); - stream *map = openrawfile(path(fname), "rb"); - if(map) - { - stream::offset len = map->size(); - if(len > 4*1024*1024) conoutf(CON_ERROR, "map is too large"); - else if(len <= 0) conoutf(CON_ERROR, "could not read map"); - else - { - sendfile(-1, 2, map); - if(needclipboard >= 0) needclipboard++; - } - delete map; - } - else conoutf(CON_ERROR, "could not read map"); - remove(findfile(fname, "rb")); - } - COMMAND(sendmap, ""); - - void gotoplayer(const char *arg) - { - if(player1->state!=CS_SPECTATOR && player1->state!=CS_EDITING) return; - int i = parseplayer(arg); - if(i>=0) - { - gameent *d = getclient(i); - if(!d || d==player1) return; - player1->o = d->o; - vec dir; - vecfromyawpitch(player1->yaw, player1->pitch, 1, 0, dir); - player1->o.add(dir.mul(-32)); - player1->resetinterp(); - } - } - COMMANDN(goto, gotoplayer, "s"); - - void gotosel() - { - if(player1->state!=CS_EDITING) return; - player1->o = getselpos(); - vec dir; - vecfromyawpitch(player1->yaw, player1->pitch, 1, 0, dir); - player1->o.add(dir.mul(-32)); - player1->resetinterp(); - } - COMMAND(gotosel, ""); } diff --git a/src/game/entities.cc b/src/game/entities.cc index dfbc0b6..0751109 100644 --- a/src/game/entities.cc +++ b/src/game/entities.cc @@ -225,7 +225,6 @@ namespace entities else d->vel = vec(0, 0, 0); entinmap(d); updatedynentcache(d); - ai::inferwaypoints(d, ents[n]->o, ents[e]->o, 16.f); break; } } @@ -267,7 +266,6 @@ namespace entities d->lastpickup = e->type; d->lastpickupmillis = lastmillis; jumppadeffects(d, n, true); - if(d->ai) d->ai->becareful = true; d->falling = vec(0, 0, 0); d->physstate = PHYS_FALL; d->timeinair = 1; diff --git a/src/game/extinfo.hh b/src/game/extinfo.hh deleted file mode 100644 index 54bf654..0000000 --- a/src/game/extinfo.hh +++ /dev/null @@ -1,145 +0,0 @@ - -#define EXT_ACK -1 -#define EXT_VERSION 105 -#define EXT_NO_ERROR 0 -#define EXT_ERROR 1 -#define EXT_PLAYERSTATS_RESP_IDS -10 -#define EXT_PLAYERSTATS_RESP_STATS -11 -#define EXT_UPTIME 0 -#define EXT_PLAYERSTATS 1 -#define EXT_TEAMSCORE 2 - -/* - Client: - ----- - A: 0 EXT_UPTIME - B: 0 EXT_PLAYERSTATS cn #a client number or -1 for all players# - C: 0 EXT_TEAMSCORE - - Server: - -------- - A: 0 EXT_UPTIME EXT_ACK EXT_VERSION uptime #in seconds# - B: 0 EXT_PLAYERSTATS cn #send by client# EXT_ACK EXT_VERSION 0 or 1 #error, if cn was > -1 and client does not exist# ... - EXT_PLAYERSTATS_RESP_IDS pid(s) #1 packet# - EXT_PLAYERSTATS_RESP_STATS pid playerdata #1 packet for each player# - C: 0 EXT_TEAMSCORE EXT_ACK EXT_VERSION 0 or 1 #error, no teammode# remaining_time gamemode loop(teamdata [numbases bases] or -1) - - Errors: - -------------- - B:C:default: 0 command EXT_ACK EXT_VERSION EXT_ERROR -*/ - - VAR(extinfoip, 0, 0, 1); - - void extinfoplayer(ucharbuf &p, clientinfo *ci) - { - ucharbuf q = p; - putint(q, EXT_PLAYERSTATS_RESP_STATS); // send player stats following - putint(q, ci->clientnum); //add player id - putint(q, ci->ping); - sendstring(ci->name, q); - sendstring(teamname(m_teammode ? ci->team : 0), q); - putint(q, ci->state.frags); - putint(q, ci->state.flags); - putint(q, ci->state.deaths); - putint(q, ci->state.teamkills); - putint(q, ci->state.damage*100/max(ci->state.shotdamage,1)); - putint(q, ci->state.health); - putint(q, 0); - putint(q, ci->state.gunselect); - putint(q, ci->privilege); - putint(q, ci->state.state); - uint ip = extinfoip ? getclientip(ci->clientnum) : 0; - q.put((uchar*)&ip, 3); - sendserverinforeply(q); - } - - static inline void extinfoteamscore(ucharbuf &p, int team, int score) - { - sendstring(teamname(team), p); - putint(p, score); - if(!smode || !smode->extinfoteam(team, p)) - putint(p,-1); //no bases follow - } - - void extinfoteams(ucharbuf &p) - { - putint(p, m_teammode ? 0 : 1); - putint(p, gamemode); - putint(p, max((gamelimit - gamemillis)/1000, 0)); - if(!m_teammode) return; - - vector scores; - if(smode && smode->hidefrags()) smode->getteamscores(scores); - loopv(clients) - { - clientinfo *ci = clients[i]; - if(ci->state.state!=CS_SPECTATOR && validteam(ci->team) && scores.htfind(ci->team) < 0) - { - if(smode && smode->hidefrags()) scores.add(teamscore(ci->team, 0)); - else { teaminfo &t = teaminfos[ci->team-1]; scores.add(teamscore(ci->team, t.frags)); } - } - } - loopv(scores) extinfoteamscore(p, scores[i].team, scores[i].score); - } - - void extserverinforeply(ucharbuf &req, ucharbuf &p) - { - int extcmd = getint(req); // extended commands - - //Build a new packet - putint(p, EXT_ACK); //send ack - putint(p, EXT_VERSION); //send version of extended info - - switch(extcmd) - { - case EXT_UPTIME: - { - putint(p, totalsecs); //in seconds - break; - } - - case EXT_PLAYERSTATS: - { - int cn = getint(req); //a special player, -1 for all - - clientinfo *ci = NULL; - if(cn >= 0) - { - loopv(clients) if(clients[i]->clientnum == cn) { ci = clients[i]; break; } - if(!ci) - { - putint(p, EXT_ERROR); //client requested by id was not found - sendserverinforeply(p); - return; - } - } - - putint(p, EXT_NO_ERROR); //so far no error can happen anymore - - ucharbuf q = p; //remember buffer position - putint(q, EXT_PLAYERSTATS_RESP_IDS); //send player ids following - if(ci) putint(q, ci->clientnum); - else loopv(clients) putint(q, clients[i]->clientnum); - sendserverinforeply(q); - - if(ci) extinfoplayer(p, ci); - else loopv(clients) extinfoplayer(p, clients[i]); - return; - } - - case EXT_TEAMSCORE: - { - extinfoteams(p); - break; - } - - default: - { - putint(p, EXT_ERROR); - break; - } - } - sendserverinforeply(p); - } - diff --git a/src/game/game.cc b/src/game/game.cc index cf0b737..de38747 100644 --- a/src/game/game.cc +++ b/src/game/game.cc @@ -39,15 +39,6 @@ namespace game void follow(char *arg) { - int cn = -1; - if(arg[0]) - { - if(player1->state != CS_SPECTATOR) return; - cn = parseplayer(arg); - if(cn == player1->clientnum) cn = -1; - } - if(cn < 0 && (following < 0 || specmode)) return; - following = cn; } COMMAND(follow, "s"); @@ -96,25 +87,16 @@ namespace game gameent *spawnstate(gameent *d) // reset player state not persistent accross spawns { d->respawn(); - d->spawnstate(gamemode); + d->spawnstate(0); return d; } void respawnself() { if(ispaused()) return; - if(m_mp(gamemode)) - { - int seq = (player1->lifesequence<<16)|((lastmillis/1000)&0xFFFF); - if(player1->respawned!=seq) { addmsg(N_TRYSPAWN, "rc", player1); player1->respawned = seq; } - } - else - { - spawnplayer(player1); - showscores(false); - lasthit = 0; - if(cmode) cmode->respawned(player1); - } + spawnplayer(player1); + lasthit = 0; + if(cmode) cmode->respawned(player1); } gameent *pointatplayer() @@ -194,7 +176,7 @@ namespace game loopv(players) { gameent *d = players[i]; - if(d == player1 || d->ai) continue; + if(d == player1) continue; if(d->state==CS_DEAD && d->ragdoll) moveragdoll(d); else if(!intermission) @@ -225,10 +207,8 @@ namespace game if(!curtime) { gets2c(); if(player1->clientnum>=0) c2sinfo(); return; } physicsframe(); - ai::navigate(); updateweapons(curtime); otherplayers(curtime); - ai::update(); moveragdolls(); gets2c(); if(connected) @@ -354,15 +334,11 @@ namespace game } damageeffect(damage, d, d!=h); - ai::damaged(d, actor); - if(d->health<=0) { if(local) killed(d, actor); } else if(d==h) playsound(S_PAIN2); else playsound(S_PAIN1, &d->o); } - VARP(deathscore, 0, 1, 1); - void deathstate(gameent *d, bool restore) { d->state = CS_DEAD; @@ -374,7 +350,6 @@ namespace game } if(d==player1) { - if(deathscore) showscores(true); disablezoom(); d->attacking = ACT_IDLE; //d->pitch = 0; @@ -431,7 +406,6 @@ namespace game else conoutf(contype, "\f2%s fragged %s", aname, dname); } deathstate(d); - ai::killed(d, actor); } void timeupdate(int secs) @@ -451,7 +425,6 @@ namespace game int accuracy = (player1->totaldamage*100)/max(player1->totalshots, 1); conoutf(CON_GAMEINFO, "\f2player total damage dealt: %d, damage wasted: %d, accuracy(%%): %d", player1->totaldamage, player1->totalshots-player1->totaldamage, accuracy); - showscores(true); disablezoom(); execident("intermission"); @@ -469,7 +442,7 @@ namespace game gameent *newclient(int cn) // ensure valid entity { - if(cn < 0 || cn > max(0xFF, MAXCLIENTS + MAXBOTS)) + if(cn < 0 || cn > max(0xFF, MAXCLIENTS)) { neterr("clientnum", false); return NULL; @@ -497,7 +470,6 @@ namespace game void clientdisconnected(int cn, bool notify) { if(!clients.inrange(cn)) return; - unignore(cn); gameent *d = clients[cn]; if(d) { @@ -506,7 +478,6 @@ namespace game removetrackedparticles(d); removetrackeddynlights(d); if(cmode) cmode->removeplayer(d); - removegroupedplayer(d); players.removeobj(d); DELETEP(clients[cn]); cleardynentcache(); @@ -538,8 +509,6 @@ namespace game clearbouncers(); clearragdolls(); - clearteaminfo(); - // reset perma-state loopv(players) players[i]->startgame(); @@ -555,14 +524,8 @@ namespace game cmode->setup(); } - conoutf(CON_GAMEINFO, "\f2game mode is %s", server::modeprettyname(gamemode)); - - const char *info = m_valid(gamemode) ? gamemodes[gamemode - STARTGAMEMODE].info : NULL; - if(showmodeinfo && info) conoutf(CON_GAMEINFO, "\f0%s", info); - syncplayer(); - showscores(false); disablezoom(); lasthit = 0; @@ -571,11 +534,7 @@ namespace game void startmap(const char *name) // called just after a map load { - ai::savewaypoints(); - ai::clearwaypoints(true); - - if(!m_mp(gamemode)) spawnplayer(player1); - else findplayerspawn(player1, -1, m_teammode ? player1->team : 0); + spawnplayer(player1); entities::resetspawns(); copystring(clientmap, name ? name : ""); @@ -584,20 +543,20 @@ namespace game const char *getmapinfo() { - return showmodeinfo && m_valid(gamemode) ? gamemodes[gamemode - STARTGAMEMODE].info : NULL; + return NULL; } const char *getscreenshotinfo() { - return server::modename(gamemode, NULL); + return NULL; } void physicstrigger(physent *d, bool local, int floorlevel, int waterlevel, int material) { if (waterlevel>0) { if(material!=MAT_LAVA) playsound(S_SPLASHOUT, d==player1 ? NULL : &d->o); } else if(waterlevel<0) playsound(material==MAT_LAVA ? S_BURN : S_SPLASHIN, d==player1 ? NULL : &d->o); - if (floorlevel>0) { if(d==player1 || d->type!=ENT_PLAYER || ((gameent *)d)->ai) msgsound(S_JUMP, d); } - else if(floorlevel<0) { if(d==player1 || d->type!=ENT_PLAYER || ((gameent *)d)->ai) msgsound(S_LAND, d); } + if (floorlevel>0) { if(d==player1 || d->type!=ENT_PLAYER) msgsound(S_JUMP, d); } + else if(floorlevel<0) { if(d==player1 || d->type!=ENT_PLAYER) msgsound(S_LAND, d); } } void dynentcollide(physent *d, physent *o, const vec &dir) @@ -613,8 +572,6 @@ namespace game } else { - if(d->type==ENT_PLAYER && ((gameent *)d)->ai) - addmsg(N_SOUND, "ci", d, n); playsound(n, &d->o); } } @@ -638,10 +595,10 @@ namespace game const char *colorname(gameent *d, const char *name, const char * alt, const char *color) { if(!name) name = alt && d == player1 ? alt : d->name; - bool dup = !name[0] || duplicatename(d, name, alt) || d->aitype != AI_NONE; + bool dup = !name[0] || duplicatename(d, name, alt); if(dup || color[0]) { - if(dup) return tempformatstring(d->aitype == AI_NONE ? "\fs%s%s \f5(%d)\fr" : "\fs%s%s \f5[%d]\fr", color, name, d->clientnum); + if(dup) return tempformatstring("\fs%s%s \f5(%d)\fr", color, name, d->clientnum); return tempformatstring("\fs%s%s\fr", color, name); } return name; @@ -673,22 +630,6 @@ namespace game teamsound(isteam(d->team, player1->team), n, loc); } - void suicide(physent *d) - { - if(d==player1 || (d->type==ENT_PLAYER && ((gameent *)d)->ai)) - { - if(d->state!=CS_ALIVE) return; - gameent *pl = (gameent *)d; - if(!m_mp(gamemode)) killed(pl, pl); - else - { - int seq = (pl->lifesequence<<16)|((lastmillis/1000)&0xFFFF); - if(pl->suicided!=seq) { addmsg(N_SUICIDE, "rc", pl); pl->suicided = seq; } - } - } - } - ICOMMAND(suicide, "", (), suicide(player1)); - bool needminimap() { return false; } void drawicon(int icon, float x, float y, float sz) @@ -719,53 +660,12 @@ namespace game void drawhudicons(gameent *d) { -#if 0 - pushhudscale(2); - - draw_textf("%d", (HICON_X + HICON_SIZE + HICON_SPACE)/2, HICON_TEXTY/2, d->state==CS_DEAD ? 0 : d->health); - if(d->state!=CS_DEAD) - { - draw_textf("%d", (HICON_X + 2*HICON_STEP + HICON_SIZE + HICON_SPACE)/2, HICON_TEXTY/2, d->ammo[d->gunselect]); - } - - pophudmatrix(); - resethudshader(); - - drawicon(HICON_HEALTH, HICON_X, HICON_Y); - if(d->state!=CS_DEAD) - { - drawicon(HICON_MELEE+d->gunselect, HICON_X + 2*HICON_STEP, HICON_Y); - } -#endif } void gameplayhud(int w, int h) { pushhudscale(h/1800.0f); - if(player1->state==CS_SPECTATOR) - { - float pw, ph, tw, th, fw, fh; - text_boundsf(" ", pw, ph); - text_boundsf("SPECTATOR", tw, th); - th = max(th, ph); - gameent *f = followingplayer(); - text_boundsf(f ? colorname(f) : " ", fw, fh); - fh = max(fh, ph); - draw_text("SPECTATOR", w*1800/h - tw - pw, 1650 - th - fh); - if(f) - { - int color = f->state!=CS_DEAD ? 0xFFFFFF : 0x606060; - if(f->privilege) - { - color = f->privilege>=PRIV_ADMIN ? 0xFF8000 : 0x40FF80; - if(f->state==CS_DEAD) color = (color>>1)&0x7F7F7F; - } - draw_text(colorname(f), w*1800/h - fw - pw, 1650 - fh, (color>>16)&0xFF, (color>>8)&0xFF, color&0xFF); - } - resethudshader(); - } - gameent *d = hudplayer(); if(d->state!=CS_EDITING) { @@ -795,80 +695,11 @@ namespace game } } - int selectcrosshair(vec &col) + int selectcrosshair(vec &) { - gameent *d = hudplayer(); - if(d->state==CS_SPECTATOR || d->state==CS_DEAD || UI::uivisible("scoreboard")) return -1; - - if(d->state!=CS_ALIVE) return 0; - - int crosshair = 0; - if(lasthit && lastmillis - lasthit < hitcrosshair) crosshair = 2; - else if(teamcrosshair && m_teammode) - { - dynent *o = intersectclosest(d->o, worldpos, d); - if(o && o->type==ENT_PLAYER && validteam(d->team) && ((gameent *)o)->team == d->team) - { - crosshair = 1; - - col = vec::hexcolor(teamtextcolor[d->team]); - } - } - -#if 0 - if(crosshair!=1 && !editmode) - { - if(d->health<=25) { r = 1.0f; g = b = 0; } - else if(d->health<=50) { r = 1.0f; g = 0.5f; b = 0; } - } -#endif - if(d->gunwait) col.mul(0.5f); - return crosshair; + return 0; } - const char *mastermodecolor(int n, const char *unknown) - { - return (n>=MM_START && size_t(n-MM_START)=MM_START && size_t(n-MM_START)attr.inrange(2) ? si->attr[2] : MM_INVALID; - result(si->maxplayers > 0 && si->numplayers >= si->maxplayers ? "serverfull" : mastermodeicon(mm, "serverunk")); - })); - // any data written into this vector will get saved with the map data. Must take care to do own versioning, and endianess if applicable. Will not get called when loading maps from other games, so provide defaults. void writegamedata(vector &extras) {} void readgamedata(vector &extras) {} diff --git a/src/game/game.hh b/src/game/game.hh index 29727e6..046ee2d 100644 --- a/src/game/game.hh +++ b/src/game/game.hh @@ -124,18 +124,18 @@ static struct gamemodeinfo #define m_checknot(mode, flag) (m_valid(mode) && !(gamemodes[(mode) - STARTGAMEMODE].flags&(flag))) #define m_checkall(mode, flag) (m_valid(mode) && (gamemodes[(mode) - STARTGAMEMODE].flags&(flag)) == (flag)) -#define m_teammode (m_check(gamemode, M_TEAM)) -#define m_overtime (m_check(gamemode, M_OVERTIME)) -#define isteam(a,b) (m_teammode && a==b) -#define m_rail (m_check(gamemode, M_RAIL)) -#define m_pulse (m_check(gamemode, M_PULSE)) +#define m_teammode false +#define m_overtime false +#define isteam(a,b) false +#define m_rail false +#define m_pulse false -#define m_demo (m_check(gamemode, M_DEMO)) -#define m_edit (m_check(gamemode, M_EDIT)) -#define m_lobby (m_check(gamemode, M_LOBBY)) -#define m_timed (m_checknot(gamemode, M_DEMO|M_EDIT|M_LOCAL)) -#define m_botmode (m_checknot(gamemode, M_DEMO|M_LOCAL)) -#define m_mp(mode) (m_checknot(mode, M_LOCAL)) +#define m_demo false +#define m_edit true +#define m_lobby false +#define m_timed false +#define m_botmode false +#define m_mp(mode) false enum { MM_AUTH = -1, MM_OPEN = 0, MM_VETO, MM_LOCKED, MM_PRIVATE, MM_PASSWORD, MM_START = MM_AUTH, MM_INVALID = MM_START - 1 }; @@ -151,14 +151,7 @@ enum S_ITEMSPAWN, S_TELEPORT, S_JUMPPAD, S_MELEE, S_PULSE1, S_PULSE2, S_PULSEEXPLODE, S_RAIL1, S_RAIL2, S_WEAPLOAD, S_NOAMMO, S_HIT, - S_PAIN1, S_PAIN2, S_DIE1, S_DIE2, - - S_FLAGPICKUP, - S_FLAGDROP, - S_FLAGRETURN, - S_FLAGSCORE, - S_FLAGRESET, - S_FLAGFAIL + S_PAIN1, S_PAIN2, S_DIE1, S_DIE2 }; // network messages codes, c2s, c2c, s2c @@ -177,19 +170,13 @@ enum N_TIMEUP, N_FORCEINTERMISSION, N_SERVMSG, N_ITEMLIST, N_RESUME, N_EDITMODE, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_CALCLIGHT, N_REMIP, N_EDITVSLOT, N_UNDO, N_REDO, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, N_EDITVAR, - N_MASTERMODE, N_KICK, N_CLEARBANS, N_CURRENTMASTER, N_SPECTATOR, N_SETMASTER, N_SETTEAM, - N_LISTDEMOS, N_SENDDEMOLIST, N_GETDEMO, N_SENDDEMO, - N_DEMOPLAYBACK, N_RECORDDEMO, N_STOPDEMO, N_CLEARDEMOS, - N_TAKEFLAG, N_RETURNFLAG, N_RESETFLAG, N_TRYDROPFLAG, N_DROPFLAG, N_SCOREFLAG, N_INITFLAGS, + N_SPECTATOR, N_SETTEAM, N_SAYTEAM, N_CLIENT, - N_AUTHTRY, N_AUTHKICK, N_AUTHCHAL, N_AUTHANS, N_REQAUTH, N_PAUSEGAME, N_GAMESPEED, - N_ADDBOT, N_DELBOT, N_INITAI, N_FROMAI, N_BOTLIMIT, N_BOTBALANCE, N_MAPCRC, N_CHECKMAPS, N_SWITCHNAME, N_SWITCHMODEL, N_SWITCHCOLOR, N_SWITCHTEAM, N_SERVCMD, - N_DEMOPACKET, NUMMSG }; @@ -205,19 +192,13 @@ static const int msgsizes[] = // size inclusive message token, 0 f N_TIMEUP, 2, N_FORCEINTERMISSION, 1, N_SERVMSG, 0, N_ITEMLIST, 0, N_RESUME, 0, N_EDITMODE, 2, N_EDITENT, 11, N_EDITF, 16, N_EDITT, 16, N_EDITM, 16, N_FLIP, 14, N_COPY, 14, N_PASTE, 14, N_ROTATE, 15, N_REPLACE, 17, N_DELCUBE, 14, N_CALCLIGHT, 1, N_REMIP, 1, N_EDITVSLOT, 16, N_UNDO, 0, N_REDO, 0, N_NEWMAP, 2, N_GETMAP, 1, N_SENDMAP, 0, N_EDITVAR, 0, - N_MASTERMODE, 2, N_KICK, 0, N_CLEARBANS, 1, N_CURRENTMASTER, 0, N_SPECTATOR, 3, N_SETMASTER, 0, N_SETTEAM, 0, - N_LISTDEMOS, 1, N_SENDDEMOLIST, 0, N_GETDEMO, 2, N_SENDDEMO, 0, - N_DEMOPLAYBACK, 3, N_RECORDDEMO, 2, N_STOPDEMO, 1, N_CLEARDEMOS, 2, - N_TAKEFLAG, 3, N_RETURNFLAG, 4, N_RESETFLAG, 3, N_TRYDROPFLAG, 1, N_DROPFLAG, 7, N_SCOREFLAG, 9, N_INITFLAGS, 0, + N_SPECTATOR, 3, N_SETTEAM, 0, N_SAYTEAM, 0, N_CLIENT, 0, - N_AUTHTRY, 0, N_AUTHKICK, 0, N_AUTHCHAL, 0, N_AUTHANS, 0, N_REQAUTH, 0, N_PAUSEGAME, 0, N_GAMESPEED, 0, - N_ADDBOT, 2, N_DELBOT, 1, N_INITAI, 0, N_FROMAI, 2, N_BOTLIMIT, 2, N_BOTBALANCE, 2, N_MAPCRC, 0, N_CHECKMAPS, 1, N_SWITCHNAME, 0, N_SWITCHMODEL, 2, N_SWITCHCOLOR, 2, N_SWITCHTEAM, 2, N_SERVCMD, 0, - N_DEMOPACKET, 0, -1 }; @@ -276,17 +257,14 @@ static const struct guninfo { const char *name, *file, *vwep; int attacks[NUMACT { "pulse rifle", "pulserifle", "worldgun/pulserifle", { -1, ATK_PULSE_SHOOT, ATK_PULSE_MELEE } } }; -#include "ai.hh" - // inherited by gameent and server clients struct gamestate { int health, maxhealth; int gunselect, gunwait; int ammo[NUMGUNS]; - int aitype, skill; - gamestate() : maxhealth(1), aitype(AI_NONE), skill(0) {} + gamestate() : maxhealth(1) {} bool canpickup(int type) { @@ -341,7 +319,6 @@ struct gamestate static const char * const teamnames[1+MAXTEAMS] = { "", "azul", "rojo" }; static const char * const teamtextcode[1+MAXTEAMS] = { "\f0", "\f1", "\f3" }; static const int teamtextcolor[1+MAXTEAMS] = { 0x1EC850, 0x6496FF, 0xFF4B19 }; -static const int teamscoreboardcolor[1+MAXTEAMS] = { 0, 0x3030C0, 0xC03030 }; static const char * const teamblipcolor[1+MAXTEAMS] = { "_neutral", "_blue", "_red" }; static inline int teamnumber(const char *name) { loopi(MAXTEAMS) if(!strcmp(teamnames[1+i], name)) return 1+i; return 0; } #define validteam(n) ((n) >= 1 && (n) <= MAXTEAMS) @@ -350,7 +327,7 @@ static inline int teamnumber(const char *name) { loopi(MAXTEAMS) if(!strcmp(team struct gameent : dynent, gamestate { int weight; // affects the effectiveness of hitpush - int clientnum, privilege, lastupdate, plag, ping; + int clientnum, lastupdate, plag, ping; int lifesequence; // sequence id for each respawn, used in damage test int respawned, suicided; int lastpain; @@ -365,12 +342,11 @@ struct gameent : dynent, gamestate string name, info; int team, playermodel, playercolor; - ai::aiinfo *ai; int ownernum, lastnode; vec muzzle; - gameent() : weight(100), clientnum(-1), privilege(PRIV_NONE), lastupdate(0), plag(0), ping(0), lifesequence(0), respawned(-1), suicided(-1), lastpain(0), frags(0), flags(0), deaths(0), totaldamage(0), totalshots(0), edit(NULL), smoothmillis(-1), team(0), playermodel(-1), playercolor(0), ai(NULL), ownernum(-1), muzzle(-1, -1, -1) + gameent() : weight(100), clientnum(-1), lastupdate(0), plag(0), ping(0), lifesequence(0), respawned(-1), suicided(-1), lastpain(0), frags(0), flags(0), deaths(0), totaldamage(0), totalshots(0), edit(NULL), smoothmillis(-1), team(0), playermodel(-1), playercolor(0), ownernum(-1), muzzle(-1, -1, -1) { name[0] = info[0] = 0; respawn(); @@ -378,7 +354,6 @@ struct gameent : dynent, gamestate ~gameent() { freeeditinfo(edit); - if(ai) delete ai; } void hitpush(int damage, const vec &dir, gameent *actor, int atk) @@ -418,23 +393,6 @@ struct gameent : dynent, gamestate } }; -struct teamscore -{ - int team, score; - teamscore() {} - teamscore(int team, int n) : team(team), score(n) {} - - static bool compare(const teamscore &x, const teamscore &y) - { - if(x.score > y.score) return true; - if(x.score < y.score) return false; - return x.team < y.team; - } -}; - -static inline uint hthash(const teamscore &t) { return hthash(t.team); } -static inline bool htcmp(int team, const teamscore &t) { return team == t.team; } - struct teaminfo { int frags; @@ -467,8 +425,6 @@ namespace entities namespace game { - extern int gamemode; - struct clientmode { virtual ~clientmode() {} @@ -486,19 +442,12 @@ namespace game virtual void removeplayer(gameent *d) {} virtual void gameover() {} virtual bool hidefrags() { return false; } - virtual int getteamscore(int team) { return 0; } - virtual void getteamscores(vector &scores) {} - virtual void aifind(gameent *d, ai::aistate &b, vector &interests) {} - virtual bool aicheck(gameent *d, ai::aistate &b) { return false; } - virtual bool aidefend(gameent *d, ai::aistate &b) { return false; } - virtual bool aipursue(gameent *d, ai::aistate &b) { return false; } }; extern clientmode *cmode; extern void setclientmode(); // game - extern int nextmode; extern string clientmap; extern bool intermission; extern int maptime, maprealtime, maplimit; @@ -542,16 +491,12 @@ namespace game extern vector messages; extern int parseplayer(const char *arg); - extern void ignore(int cn); - extern void unignore(int cn); - extern bool isignored(int cn); extern bool addmsg(int type, const char *fmt = NULL, ...); extern void switchname(const char *name); extern void switchteam(const char *name); extern void switchplayermodel(int playermodel); extern void switchplayercolor(int playercolor); extern void sendmapinfo(); - extern void stopdemo(); extern void changemap(const char *name, int mode); extern void c2sinfo(bool force = false); extern void sendposition(gameent *d, bool reliable = false); @@ -580,15 +525,6 @@ namespace game extern void updateweapons(int curtime); extern void gunselect(int gun, gameent *d); extern void weaponswitch(gameent *d); - extern void avoidweapons(ai::avoidset &obstacles, float radius); - - // scoreboard - extern void showscores(bool on); - extern void getbestplayers(vector &best); - extern void getbestteams(vector &best); - extern void clearteaminfo(); - extern void setteaminfo(int team, int frags); - extern void removegroupedplayer(gameent *d); // render struct playermodelinfo diff --git a/src/game/render.cc b/src/game/render.cc index fd6574a..0d7f738 100644 --- a/src/game/render.cc +++ b/src/game/render.cc @@ -21,7 +21,6 @@ namespace game gameent *r = new gameent(*d); r->lastupdate = ragdollfade && lastmillis > d->lastpain + max(ragdollmillis - ragdollfade, 0) ? lastmillis - max(ragdollmillis - ragdollfade, 0) : d->lastpain; r->edit = NULL; - r->ai = NULL; if(d==player1) r->playermodel = playermodel; ragdolls.add(r); d->ragdoll = NULL; @@ -318,14 +317,10 @@ namespace game void rendergame() { - ai::render(); - if(intermission) { bestteams.shrink(0); bestplayers.shrink(0); - if(m_teammode) getbestteams(bestteams); - else getbestplayers(bestplayers); } bool third = isthirdperson(); diff --git a/src/game/scoreboard.cc b/src/game/scoreboard.cc deleted file mode 100644 index 29f7f18..0000000 --- a/src/game/scoreboard.cc +++ /dev/null @@ -1,216 +0,0 @@ -// creation of scoreboard -#include "game.hh" - -namespace game -{ - VARP(showservinfo, 0, 1, 1); - VARP(showclientnum, 0, 0, 1); - VARP(showpj, 0, 0, 1); - VARP(showping, 0, 1, 1); - VARP(showspectators, 0, 1, 1); - VARP(highlightscore, 0, 1, 1); - VARP(showconnecting, 0, 0, 1); - VARP(hidefrags, 0, 1, 1); - - static teaminfo teaminfos[MAXTEAMS]; - - void clearteaminfo() - { - loopi(MAXTEAMS) teaminfos[i].reset(); - } - - void setteaminfo(int team, int frags) - { - if(!validteam(team)) return; - teaminfo &t = teaminfos[team-1]; - t.frags = frags; - } - - static inline bool playersort(const gameent *a, const gameent *b) - { - if(a->state==CS_SPECTATOR) - { - if(b->state==CS_SPECTATOR) return strcmp(a->name, b->name) < 0; - else return false; - } - else if(b->state==CS_SPECTATOR) return true; - if(a->frags > b->frags) return true; - if(a->frags < b->frags) return false; - return strcmp(a->name, b->name) < 0; - } - - void getbestplayers(vector &best) - { - loopv(players) - { - gameent *o = players[i]; - if(o->state!=CS_SPECTATOR) best.add(o); - } - best.sort(playersort); - while(best.length() > 1 && best.last()->frags < best[0]->frags) best.drop(); - } - - void getbestteams(vector &best) - { - if(cmode && cmode->hidefrags()) - { - vector teamscores; - cmode->getteamscores(teamscores); - teamscores.sort(teamscore::compare); - while(teamscores.length() > 1 && teamscores.last().score < teamscores[0].score) teamscores.drop(); - loopv(teamscores) best.add(teamscores[i].team); - } - else - { - int bestfrags = INT_MIN; - loopi(MAXTEAMS) - { - teaminfo &t = teaminfos[i]; - bestfrags = max(bestfrags, t.frags); - } - loopi(MAXTEAMS) - { - teaminfo &t = teaminfos[i]; - if(t.frags >= bestfrags) best.add(1+i); - } - } - } - - static vector teamplayers[1+MAXTEAMS], spectators; - - static void groupplayers() - { - loopi(1+MAXTEAMS) teamplayers[i].setsize(0); - spectators.setsize(0); - loopv(players) - { - gameent *o = players[i]; - if(!showconnecting && !o->name[0]) continue; - if(o->state==CS_SPECTATOR) { spectators.add(o); continue; } - int team = m_teammode && validteam(o->team) ? o->team : 0; - teamplayers[team].add(o); - } - loopi(1+MAXTEAMS) teamplayers[i].sort(playersort); - spectators.sort(playersort); - } - - void removegroupedplayer(gameent *d) - { - loopi(1+MAXTEAMS) teamplayers[i].removeobj(d); - spectators.removeobj(d); - } - - void refreshscoreboard() - { - groupplayers(); - } - - COMMAND(refreshscoreboard, ""); - ICOMMAND(numscoreboard, "i", (int *team), intret(*team < 0 ? spectators.length() : (*team <= MAXTEAMS ? teamplayers[*team].length() : 0))); - ICOMMAND(loopscoreboard, "rie", (ident *id, int *team, uint *body), - { - if(*team > MAXTEAMS) return; - loopstart(id, stack); - vector &p = *team < 0 ? spectators : teamplayers[*team]; - loopv(p) - { - loopiter(id, stack, p[i]->clientnum); - execute(body); - } - loopend(id, stack); - }); - - ICOMMAND(scoreboardstatus, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) - { - int status = d->state!=CS_DEAD ? 0xFFFFFF : 0x606060; - if(d->privilege) - { - status = d->privilege>=PRIV_ADMIN ? 0xFF8000 : 0x40FF80; - if(d->state==CS_DEAD) status = (status>>1)&0x7F7F7F; - } - intret(status); - } - }); - - ICOMMAND(scoreboardpj, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d && d != player1) - { - if(d->state==CS_LAGGED) result("LAG"); - else intret(d->plag); - } - }); - - ICOMMAND(scoreboardping, "i", (int *cn), - { - gameent *d = getclient(*cn); - if(d) - { - if(!showpj && d->state==CS_LAGGED) result("LAG"); - else intret(d->ping); - } - }); - - ICOMMAND(scoreboardshowfrags, "", (), intret(cmode && cmode->hidefrags() && hidefrags ? 0 : 1)); - ICOMMAND(scoreboardshowclientnum, "", (), intret(showclientnum || player1->privilege>=PRIV_MASTER ? 1 : 0)); - ICOMMAND(scoreboardmultiplayer, "", (), intret(multiplayer(false) || demoplayback ? 1 : 0)); - - ICOMMAND(scoreboardhighlight, "i", (int *cn), - intret(*cn == player1->clientnum && highlightscore && (multiplayer(false) || demoplayback || players.length() > 1) ? 0x808080 : 0)); - - ICOMMAND(scoreboardservinfo, "", (), - { - if(!showservinfo) return; - const ENetAddress *address = connectedpeer(); - if(address && player1->clientnum >= 0) - { - if(servdesc[0]) result(servdesc); - else - { - string hostname; - if(enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0) - result(tempformatstring("%s:%d", hostname, address->port)); - } - } - }); - - ICOMMAND(scoreboardmode, "", (), - { - result(server::modeprettyname(gamemode)); - }); - - ICOMMAND(scoreboardmap, "", (), - { - const char *mname = getclientmap(); - result(mname[0] ? mname : "[new map]"); - }); - - ICOMMAND(scoreboardtime, "", (), - { - if(m_timed && getclientmap() && (maplimit >= 0 || intermission)) - { - if(intermission) result("intermission"); - else - { - int secs = max(maplimit-lastmillis + 999, 0)/1000; - result(tempformatstring("%d:%02d", secs/60, secs%60)); - } - } - }); - - ICOMMAND(getteamscore, "i", (int *team), - { - if(m_teammode && validteam(*team)) - { - if(cmode && cmode->hidefrags()) intret(cmode->getteamscore(*team)); - else intret(teaminfos[*team-1].frags); - } - }); - - void showscores(bool on) { UI::holdui("scoreboard", on); } -} - diff --git a/src/game/server.cc b/src/game/server.cc index 8152c9f..ea86be6 100644 --- a/src/game/server.cc +++ b/src/game/server.cc @@ -121,7 +121,7 @@ namespace server int lastdeath, deadflush, lastspawn, lifesequence; int lastshot; projectilestate<8> projs; - int frags, flags, deaths, teamkills, shotdamage, damage; + int frags, flags, deaths, shotdamage, damage; int lasttimeplayed, timeplayed; float effectiveness; @@ -145,7 +145,7 @@ namespace server timeplayed = 0; effectiveness = 0; - frags = flags = deaths = teamkills = shotdamage = damage = 0; + frags = flags = deaths = shotdamage = damage = 0; lastdeath = 0; @@ -168,48 +168,13 @@ namespace server } }; - struct savedscore - { - uint ip; - string name; - int frags, flags, deaths, teamkills, shotdamage, damage; - int timeplayed; - float effectiveness; - - void save(servstate &gs) - { - frags = gs.frags; - flags = gs.flags; - deaths = gs.deaths; - teamkills = gs.teamkills; - shotdamage = gs.shotdamage; - damage = gs.damage; - timeplayed = gs.timeplayed; - effectiveness = gs.effectiveness; - } - - void restore(servstate &gs) - { - gs.frags = frags; - gs.flags = flags; - gs.deaths = deaths; - gs.teamkills = teamkills; - gs.shotdamage = shotdamage; - gs.damage = damage; - gs.timeplayed = timeplayed; - gs.effectiveness = effectiveness; - } - }; - extern int gamemillis, nextexceeded; struct clientinfo { int clientnum, ownernum, connectmillis, sessionid, overflow; - string name, mapvote; + string name; int team, playermodel, playercolor; - int modevote; - int privilege; bool connected, local, timesync; int gameoffset, lastevent, pushed, exceeded; servstate state; @@ -217,22 +182,15 @@ namespace server vector position, messages; uchar *wsdata; int wslen; - vector bots; int ping, aireinit; string clientmap; int mapcrc; bool warned, gameclip; - ENetPacket *getdemo, *getmap, *clipboard; + ENetPacket *getmap, *clipboard; int lastclipboard, needclipboard; - int connectauth; - uint authreq; - string authname, authdesc; - void *authchallenge; - int authkickvictim; - char *authkickreason; - clientinfo() : getdemo(NULL), getmap(NULL), clipboard(NULL), authchallenge(NULL), authkickreason(NULL) { reset(); } - ~clientinfo() { events.deletecontents(); cleanclipboard(); cleanauth(); } + clientinfo() : getmap(NULL), clipboard(NULL) { reset(); } + ~clientinfo() { events.deletecontents(); cleanclipboard(); } void addevent(gameevent *e) { @@ -282,8 +240,6 @@ namespace server void mapchange() { - mapvote[0] = 0; - modevote = INT_MAX; state.reset(); events.deletecontents(); overflow = 0; @@ -311,35 +267,19 @@ namespace server if(fullclean) lastclipboard = 0; } - void cleanauthkick() - { - authkickvictim = -1; - DELETEA(authkickreason); - } - - void cleanauth(bool full = true) - { - authreq = 0; - if(authchallenge) { freechallenge(authchallenge); authchallenge = NULL; } - if(full) cleanauthkick(); - } - void reset() { name[0] = 0; team = 0; playermodel = -1; playercolor = 0; - privilege = PRIV_NONE; connected = local = false; - connectauth = 0; position.setsize(0); messages.setsize(0); ping = 0; aireinit = 0; needclipboard = 0; cleanclipboard(); - cleanauth(); mapchange(); } @@ -361,20 +301,6 @@ namespace server uint ip; }; - namespace aiman - { - extern void removeai(clientinfo *ci); - extern void clearai(); - extern void checkai(); - extern void reqadd(clientinfo *ci, int skill); - extern void reqdel(clientinfo *ci); - extern void setbotlimit(clientinfo *ci, int limit); - extern void setbotbalance(clientinfo *ci, bool balance); - extern void changemap(); - extern void addclient(clientinfo *ci); - extern void changeteam(clientinfo *ci); - } - #define MM_MODE 0xF #define MM_AUTOAPPROVE 0x1000 #define MM_PRIVSERV (MM_MODE | MM_AUTOAPPROVE) @@ -406,91 +332,7 @@ namespace server bannedips.add(b); } - vector connects, clients, bots; - - void kickclients(uint ip, clientinfo *actor = NULL, int priv = PRIV_NONE) - { - loopvrev(clients) - { - clientinfo &c = *clients[i]; - if(c.state.aitype != AI_NONE || c.privilege >= PRIV_ADMIN || c.local) continue; - if(actor && ((c.privilege > priv && !actor->local) || c.clientnum == actor->clientnum)) continue; - if(getclientip(c.clientnum) == ip) disconnect_client(c.clientnum, DISC_KICK); - } - } - - struct maprotation - { - static int exclude; - int modes; - string map; - - int calcmodemask() const { return modes&(1< maprotations; - int curmaprotation = 0; - - VAR(lockmaprotation, 0, 0, 2); - - void maprotationreset() - { - maprotations.setsize(0); - curmaprotation = 0; - maprotation::exclude = 0; - } - - void nextmaprotation() - { - curmaprotation++; - if(maprotations.inrange(curmaprotation) && maprotations[curmaprotation].modes) return; - do curmaprotation--; - while(maprotations.inrange(curmaprotation) && maprotations[curmaprotation].modes); - curmaprotation++; - } - - int findmaprotation(int mode, const char *map) - { - for(int i = max(curmaprotation, 0); i < maprotations.length(); i++) - { - maprotation &rot = maprotations[i]; - if(!rot.modes) break; - if(rot.match(mode, map)) return i; - } - int start; - for(start = max(curmaprotation, 0) - 1; start >= 0; start--) if(!maprotations[start].modes) break; - start++; - for(int i = start; i < curmaprotation; i++) - { - maprotation &rot = maprotations[i]; - if(!rot.modes) break; - if(rot.match(mode, map)) return i; - } - int best = -1; - loopv(maprotations) - { - maprotation &rot = maprotations[i]; - if(rot.match(mode, map) && (best < 0 || maprotations[best].includes(rot))) best = i; - } - return best; - } + vector connects, clients; bool searchmodename(const char *haystack, const char *needle) { @@ -553,164 +395,18 @@ namespace server return modemask; } - bool addmaprotation(int modemask, const char *map) - { - if(!map[0]) loopk(NUMGAMEMODES) if(modemask&(1< modes, maps; - for(int i = 0; i + 1 < numargs; i += 2) - { - explodelist(args[i].getstr(), modes); - explodelist(args[i+1].getstr(), maps); - int modemask = genmodemask(modes); - if(maps.length()) loopvj(maps) addmaprotation(modemask, maps[j]); - else addmaprotation(modemask, ""); - modes.deletearrays(); - maps.deletearrays(); - } - if(maprotations.length() && maprotations.last().modes) - { - maprotation &rot = maprotations.add(); - rot.modes = 0; - rot.map[0] = '\0'; - } - } - - COMMAND(maprotationreset, ""); - COMMANDN(maprotation, addmaprotations, "ss2V"); - - struct demofile - { - string info; - uchar *data; - int len; - }; - - vector demos; - - bool demonextmatch = false; - stream *demotmp = NULL, *demorecord = NULL, *demoplayback = NULL; - int nextplayback = 0, demomillis = 0; - - VAR(maxdemos, 0, 5, 25); - VAR(maxdemosize, 0, 16, 31); - VAR(restrictdemos, 0, 1, 1); - - VAR(restrictpausegame, 0, 1, 1); - VAR(restrictgamespeed, 0, 1, 1); - - SVAR(serverdesc, ""); - SVAR(serverpass, ""); - SVAR(adminpass, ""); - VARF(publicserver, 0, 0, 2, { - switch(publicserver) - { - case 0: default: mastermask = MM_PRIVSERV; break; - case 1: mastermask = MM_PUBSERV; break; - case 2: mastermask = MM_COOPSERV; break; - } - }); - SVAR(servermotd, ""); - - struct teamkillkick - { - int modes, limit, ban; - - bool match(int mode) const - { - return (modes&(1<<(mode-STARTGAMEMODE)))!=0; - } - - bool includes(const teamkillkick &tk) const - { - return tk.modes != modes && (tk.modes & modes) == tk.modes; - } - }; - vector teamkillkicks; - - void teamkillkickreset() - { - teamkillkicks.setsize(0); - } - - void addteamkillkick(char *modestr, int *limit, int *ban) - { - vector modes; - explodelist(modestr, modes); - teamkillkick &kick = teamkillkicks.add(); - kick.modes = genmodemask(modes); - kick.limit = *limit; - kick.ban = *ban > 0 ? *ban*60000 : (*ban < 0 ? 0 : 30*60000); - modes.deletearrays(); - } - - COMMAND(teamkillkickreset, ""); - COMMANDN(teamkillkick, addteamkillkick, "sii"); - - struct teamkillinfo - { - uint ip; - int teamkills; - }; - vector teamkills; - bool shouldcheckteamkills = false; - - void addteamkill(clientinfo *actor, clientinfo *victim, int n) - { - if(!m_timed || actor->state.aitype != AI_NONE || actor->local || actor->privilege || (victim && victim->state.aitype != AI_NONE)) return; - shouldcheckteamkills = true; - uint ip = getclientip(actor->clientnum); - loopv(teamkills) if(teamkills[i].ip == ip) - { - teamkills[i].teamkills += n; - return; - } - teamkillinfo &tk = teamkills.add(); - tk.ip = ip; - tk.teamkills = n; - } - - void checkteamkills() - { - teamkillkick *kick = NULL; - if(m_timed) loopv(teamkillkicks) if(teamkillkicks[i].match(gamemode) && (!kick || kick->includes(teamkillkicks[i]))) - kick = &teamkillkicks[i]; - if(kick) loopvrev(teamkills) - { - teamkillinfo &tk = teamkills[i]; - if(tk.teamkills >= kick->limit) - { - if(kick->ban > 0) addban(tk.ip, kick->ban); - kickclients(tk.ip); - teamkills.removeunordered(i); - } - } - shouldcheckteamkills = false; - } - void *newclientinfo() { return new clientinfo; } void deleteclientinfo(void *ci) { delete (clientinfo *)ci; } clientinfo *getinfo(int n) { if(n < MAXCLIENTS) return (clientinfo *)getclientinfo(n); - n -= MAXCLIENTS; - return bots.inrange(n) ? bots[n] : NULL; + return NULL; } uint mcrc = 0; vector ments; vector sents; - vector scores; int msgsizelookup(int msg) { @@ -742,13 +438,7 @@ namespace server const char *privname(int type) { - switch(type) - { - case PRIV_ADMIN: return "admin"; - case PRIV_AUTH: return "auth"; - case PRIV_MASTER: return "master"; - default: return "unknown"; - } + return "unknown"; } void sendservmsg(const char *s) { sendf(-1, 1, "ris", N_SERVMSG, s); } @@ -785,7 +475,7 @@ namespace server loopv(clients) { clientinfo *ci = clients[i]; - if(ci->clientnum!=exclude && (!nospec || ci->state.state!=CS_SPECTATOR || (priv && (ci->privilege || ci->local))) && (!noai || ci->state.aitype == AI_NONE)) n++; + if(ci->clientnum!=exclude && (!nospec || ci->state.state!=CS_SPECTATOR || (priv && ci->local))) n++; } return n; } @@ -800,11 +490,11 @@ namespace server const char *colorname(clientinfo *ci, const char *name = NULL) { if(!name) name = ci->name; - if(name[0] && !duplicatename(ci, name) && ci->state.aitype == AI_NONE) return name; + if(name[0] && !duplicatename(ci, name)) return name; static string cname[3]; static int cidx = 0; cidx = (cidx+1)%3; - formatstring(cname[cidx], ci->state.aitype == AI_NONE ? "%s \fs\f5(%d)\fr" : "%s \fs\f5[%d]\fr", name, ci->clientnum); + formatstring(cname[cidx], "%s \fs\f5(%d)\fr", name, ci->clientnum); return cname[cidx]; } @@ -833,9 +523,6 @@ namespace server virtual void newmap() {} virtual void intermission() {} virtual bool hidefrags() { return false; } - virtual int getteamscore(int team) { return 0; } - virtual void getteamscores(vector &scores) {} - virtual bool extinfoteam(int team, ucharbuf &p) { return false; } }; servmode *smode = NULL; @@ -946,7 +633,7 @@ namespace server loopv(clients) { clientinfo *ci = clients[i]; - if(ci==exclude || ci->state.aitype!=AI_NONE || ci->state.state==CS_SPECTATOR || !validteam(ci->team)) continue; + if(ci==exclude || ci->state.state==CS_SPECTATOR || !validteam(ci->team)) continue; ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; ci->state.lasttimeplayed = lastmillis; @@ -968,231 +655,9 @@ namespace server return 1+int(worst-teamranks); } - void prunedemos(int extra = 0) - { - int n = clamp(demos.length() + extra - maxdemos, 0, demos.length()); - if(n <= 0) return; - loopi(n) delete[] demos[i].data; - demos.remove(0, n); - } - - void adddemo() - { - if(!demotmp) return; - int len = (int)min(demotmp->size(), stream::offset((maxdemosize<<20) + 0x10000)); - demofile &d = demos.add(); - time_t t = time(NULL); - char *timestr = ctime(&t), *trim = timestr + strlen(timestr); - while(trim>timestr && iscubespace(*--trim)) *trim = '\0'; - formatstring(d.info, "%s: %s, %s, %.2f%s", timestr, modeprettyname(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); - sendservmsgf("demo \"%s\" recorded", d.info); - d.data = new uchar[len]; - d.len = len; - demotmp->seek(0, SEEK_SET); - demotmp->read(d.data, len); - DELETEP(demotmp); - } - - void enddemorecord() - { - if(!demorecord) return; - - DELETEP(demorecord); - - if(!demotmp) return; - if(!maxdemos || !maxdemosize) { DELETEP(demotmp); return; } - - prunedemos(1); - adddemo(); - } - - void writedemo(int chan, void *data, int len) - { - if(!demorecord) return; - int stamp[3] = { gamemillis, chan, len }; - lilswap(stamp, 3); - demorecord->write(stamp, sizeof(stamp)); - demorecord->write(data, len); - if(demorecord->rawtell() >= (maxdemosize<<20)) enddemorecord(); - } - - void recordpacket(int chan, void *data, int len) - { - writedemo(chan, data, len); - } - int welcomepacket(packetbuf &p, clientinfo *ci); void sendwelcome(clientinfo *ci); - void setupdemorecord() - { - if(!m_mp(gamemode) || m_edit) return; - - demotmp = opentempfile("demorecord", "w+b"); - if(!demotmp) return; - - stream *f = opengzfile(NULL, "wb", demotmp); - if(!f) { DELETEP(demotmp); return; } - - sendservmsg("recording demo"); - - demorecord = f; - - demoheader hdr; - memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)); - hdr.version = DEMO_VERSION; - hdr.protocol = PROTOCOL_VERSION; - lilswap(&hdr.version, 2); - demorecord->write(&hdr, sizeof(demoheader)); - - packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - welcomepacket(p, NULL); - writedemo(1, p.buf, p.len); - } - - void listdemos(int cn) - { - packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - putint(p, N_SENDDEMOLIST); - putint(p, demos.length()); - loopv(demos) sendstring(demos[i].info, p); - sendpacket(cn, 1, p.finalize()); - } - - void cleardemos(int n) - { - if(!n) - { - loopv(demos) delete[] demos[i].data; - demos.shrink(0); - sendservmsg("cleared all demos"); - } - else if(demos.inrange(n-1)) - { - delete[] demos[n-1].data; - demos.remove(n-1); - sendservmsgf("cleared demo %d", n); - } - } - - static void freegetmap(ENetPacket *packet) - { - loopv(clients) - { - clientinfo *ci = clients[i]; - if(ci->getmap == packet) ci->getmap = NULL; - } - } - - static void freegetdemo(ENetPacket *packet) - { - loopv(clients) - { - clientinfo *ci = clients[i]; - if(ci->getdemo == packet) ci->getdemo = NULL; - } - } - - void senddemo(clientinfo *ci, int num) - { - if(ci->getdemo) return; - if(!num) num = demos.length(); - if(!demos.inrange(num-1)) return; - demofile &d = demos[num-1]; - if((ci->getdemo = sendf(ci->clientnum, 2, "rim", N_SENDDEMO, d.len, d.data))) - ci->getdemo->freeCallback = freegetdemo; - } - - void enddemoplayback() - { - if(!demoplayback) return; - DELETEP(demoplayback); - - loopv(clients) sendf(clients[i]->clientnum, 1, "ri3", N_DEMOPLAYBACK, 0, clients[i]->clientnum); - - sendservmsg("demo playback finished"); - - loopv(clients) sendwelcome(clients[i]); - } - - void setupdemoplayback() - { - if(demoplayback) return; - demoheader hdr; - string msg; - msg[0] = '\0'; - defformatstring(file, "%s.dmo", smapname); - demoplayback = opengzfile(file, "rb"); - if(!demoplayback) formatstring(msg, "could not read demo \"%s\"", file); - else if(demoplayback->read(&hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) - formatstring(msg, "\"%s\" is not a demo file", file); - else - { - lilswap(&hdr.version, 2); - if(hdr.version!=DEMO_VERSION) formatstring(msg, "demo \"%s\" requires an %s version of Tesseract", file, hdr.versionread(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) - { - enddemoplayback(); - return; - } - lilswap(&nextplayback, 1); - } - - void readdemo() - { - if(!demoplayback) return; - demomillis += curtime; - while(demomillis>=nextplayback) - { - int chan, len; - if(demoplayback->read(&chan, sizeof(chan))!=sizeof(chan) || - demoplayback->read(&len, sizeof(len))!=sizeof(len)) - { - enddemoplayback(); - return; - } - lilswap(&chan, 1); - lilswap(&len, 1); - ENetPacket *packet = enet_packet_create(NULL, len+1, 0); - if(!packet || demoplayback->read(packet->data+1, len)!=size_t(len)) - { - if(packet) enet_packet_destroy(packet); - enddemoplayback(); - return; - } - packet->data[0] = N_DEMOPACKET; - sendpacket(-1, chan, packet); - if(!packet->referenceCount) enet_packet_destroy(packet); - if(!demoplayback) break; - if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) - { - enddemoplayback(); - return; - } - lilswap(&nextplayback, 1); - } - } - - void stopdemo() - { - if(m_demo) enddemoplayback(); - else enddemorecord(); - } - void pausegame(bool val, clientinfo *ci = NULL) { if(gamepaused==val) return; @@ -1202,10 +667,6 @@ namespace server void checkpausegame() { - if(!gamepaused) return; - int admins = 0; - loopv(clients) if(clients[i]->privilege >= (restrictpausegame ? PRIV_ADMIN : PRIV_MASTER) || clients[i]->local) admins++; - if(!admins) pausegame(false); } void forcepaused(bool paused) @@ -1230,211 +691,6 @@ namespace server int scaletime(int t) { return t*gamespeed; } - SVAR(serverauth, ""); - - struct userkey - { - char *name; - char *desc; - - userkey() : name(NULL), desc(NULL) {} - userkey(char *name, char *desc) : name(name), desc(desc) {} - }; - - static inline uint hthash(const userkey &k) { return ::hthash(k.name); } - static inline bool htcmp(const userkey &x, const userkey &y) { return !strcmp(x.name, y.name) && !strcmp(x.desc, y.desc); } - - struct userinfo : userkey - { - void *pubkey; - int privilege; - - userinfo() : pubkey(NULL), privilege(PRIV_NONE) {} - ~userinfo() { delete[] name; delete[] desc; if(pubkey) freepubkey(pubkey); } - }; - hashset users; - - void adduser(char *name, char *desc, char *pubkey, char *priv) - { - userkey key(name, desc); - userinfo &u = users[key]; - if(u.pubkey) { freepubkey(u.pubkey); u.pubkey = NULL; } - if(!u.name) u.name = newstring(name); - if(!u.desc) u.desc = newstring(desc); - u.pubkey = parsepubkey(pubkey); - switch(priv[0]) - { - case 'a': case 'A': u.privilege = PRIV_ADMIN; break; - case 'm': case 'M': default: u.privilege = PRIV_AUTH; break; - case 'n': case 'N': u.privilege = PRIV_NONE; break; - } - } - COMMAND(adduser, "ssss"); - - void clearusers() - { - users.clear(); - } - COMMAND(clearusers, ""); - - void hashpassword(int cn, int sessionid, const char *pwd, char *result, int maxlen) - { - char buf[2*sizeof(string)]; - formatstring(buf, "%d %d %s", cn, sessionid, pwd); - if(!hashstring(buf, result, maxlen)) *result = '\0'; - } - - bool checkpassword(clientinfo *ci, const char *wanted, const char *given) - { - string hash; - hashpassword(ci->clientnum, ci->sessionid, wanted, hash, sizeof(hash)); - return !strcmp(hash, given); - } - - void revokemaster(clientinfo *ci) - { - ci->privilege = PRIV_NONE; - if(ci->state.state==CS_SPECTATOR && !ci->local) aiman::removeai(ci); - } - - extern void connected(clientinfo *ci); - - bool setmaster(clientinfo *ci, bool val, const char *pass = "", const char *authname = NULL, const char *authdesc = NULL, int authpriv = PRIV_MASTER, bool force = false, bool trial = false) - { - if(authname && !val) return false; - const char *name = ""; - if(val) - { - bool haspass = adminpass[0] && checkpassword(ci, adminpass, pass); - int wantpriv = ci->local || haspass ? PRIV_ADMIN : authpriv; - if(wantpriv <= ci->privilege) return true; - else if(wantpriv <= PRIV_MASTER && !force) - { - if(ci->state.state==CS_SPECTATOR) - { - sendf(ci->clientnum, 1, "ris", N_SERVMSG, "Spectators may not claim master."); - return false; - } - loopv(clients) if(ci!=clients[i] && clients[i]->privilege) - { - sendf(ci->clientnum, 1, "ris", N_SERVMSG, "Master is already claimed."); - return false; - } - if(!authname && !(mastermask&MM_AUTOAPPROVE) && !ci->privilege && !ci->local) - { - sendf(ci->clientnum, 1, "ris", N_SERVMSG, "This server requires you to use the \"/auth\" command to claim master."); - return false; - } - } - if(trial) return true; - ci->privilege = wantpriv; - name = privname(ci->privilege); - } - else - { - if(!ci->privilege) return false; - if(trial) return true; - name = privname(ci->privilege); - revokemaster(ci); - } - bool hasmaster = false; - loopv(clients) if(clients[i]->local || clients[i]->privilege >= PRIV_MASTER) hasmaster = true; - if(!hasmaster) - { - mastermode = MM_OPEN; - allowedips.shrink(0); - } - string msg; - if(val && authname) - { - if(authdesc && authdesc[0]) formatstring(msg, "%s claimed %s as '\fs\f5%s\fr' [\fs\f0%s\fr]", colorname(ci), name, authname, authdesc); - else formatstring(msg, "%s claimed %s as '\fs\f5%s\fr'", colorname(ci), name, authname); - } - else formatstring(msg, "%s %s %s", colorname(ci), val ? "claimed" : "relinquished", name); - packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - putint(p, N_SERVMSG); - sendstring(msg, p); - putint(p, N_CURRENTMASTER); - putint(p, mastermode); - loopv(clients) if(clients[i]->privilege >= PRIV_MASTER) - { - putint(p, clients[i]->clientnum); - putint(p, clients[i]->privilege); - } - putint(p, -1); - sendpacket(-1, 1, p.finalize()); - checkpausegame(); - return true; - } - - bool trykick(clientinfo *ci, int victim, const char *reason = NULL, const char *authname = NULL, const char *authdesc = NULL, int authpriv = PRIV_NONE, bool trial = false) - { - int priv = ci->privilege; - if(authname) - { - if(priv >= authpriv || ci->local) authname = authdesc = NULL; - else priv = authpriv; - } - if((priv || ci->local) && ci->clientnum!=victim) - { - clientinfo *vinfo = (clientinfo *)getclientinfo(victim); - if(vinfo && vinfo->connected && (priv >= vinfo->privilege || ci->local) && vinfo->privilege < PRIV_ADMIN && !vinfo->local) - { - if(trial) return true; - string kicker; - if(authname) - { - if(authdesc && authdesc[0]) formatstring(kicker, "%s as '\fs\f5%s\fr' [\fs\f0%s\fr]", colorname(ci), authname, authdesc); - else formatstring(kicker, "%s as '\fs\f5%s\fr'", colorname(ci), authname); - } - else copystring(kicker, colorname(ci)); - if(reason && reason[0]) sendservmsgf("%s kicked %s because: %s", kicker, colorname(vinfo), reason); - else sendservmsgf("%s kicked %s", kicker, colorname(vinfo)); - uint ip = getclientip(victim); - addban(ip, 4*60*60000); - kickclients(ip, ci, priv); - } - } - return false; - } - - savedscore *findscore(clientinfo *ci, bool insert) - { - uint ip = getclientip(ci->clientnum); - if(!ip && !ci->local) return 0; - if(!insert) - { - loopv(clients) - { - clientinfo *oi = clients[i]; - if(oi->clientnum != ci->clientnum && getclientip(oi->clientnum) == ip && !strcmp(oi->name, ci->name)) - { - oi->state.timeplayed += lastmillis - oi->state.lasttimeplayed; - oi->state.lasttimeplayed = lastmillis; - static savedscore curscore; - curscore.save(oi->state); - return &curscore; - } - } - } - loopv(scores) - { - savedscore &sc = scores[i]; - if(sc.ip == ip && !strcmp(sc.name, ci->name)) return ≻ - } - if(!insert) return 0; - savedscore &sc = scores.add(); - sc.ip = ip; - copystring(sc.name, ci->name); - return ≻ - } - - void savescore(clientinfo *ci) - { - savedscore *sc = findscore(ci, true); - if(sc) sc->save(ci->state); - } - static struct msgfilter { uchar msgmask[NUMMSG]; @@ -1453,25 +709,13 @@ namespace server } uchar operator[](int msg) const { return msg >= 0 && msg < NUMMSG ? msgmask[msg] : 0; } - } msgfilter(-1, N_CONNECT, N_SERVINFO, N_INITCLIENT, N_WELCOME, N_MAPCHANGE, N_SERVMSG, N_DAMAGE, N_HITPUSH, N_SHOTFX, N_EXPLODEFX, N_DIED, N_SPAWNSTATE, N_FORCEDEATH, N_TEAMINFO, N_ITEMACC, N_ITEMSPAWN, N_TIMEUP, N_CDIS, N_CURRENTMASTER, N_PONG, N_RESUME, N_SENDDEMOLIST, N_SENDDEMO, N_DEMOPLAYBACK, N_SENDMAP, N_DROPFLAG, N_SCOREFLAG, N_RETURNFLAG, N_RESETFLAG, N_CLIENT, N_AUTHCHAL, N_INITAI, N_DEMOPACKET, -2, N_CALCLIGHT, N_REMIP, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, -3, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_EDITVAR, N_EDITVSLOT, N_UNDO, N_REDO, -4, N_POS, NUMMSG), - connectfilter(-1, N_CONNECT, -2, N_AUTHANS, -3, N_PING, NUMMSG); + } msgfilter(-1, N_CONNECT, N_SERVINFO, N_INITCLIENT, N_WELCOME, N_MAPCHANGE, N_SERVMSG, N_DAMAGE, N_HITPUSH, N_SHOTFX, N_EXPLODEFX, N_DIED, N_SPAWNSTATE, N_FORCEDEATH, N_TEAMINFO, N_ITEMACC, N_ITEMSPAWN, N_TIMEUP, N_CDIS, N_PONG, N_RESUME, N_SENDMAP, N_CLIENT, -2, N_CALCLIGHT, N_REMIP, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, -3, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_EDITVAR, N_EDITVSLOT, N_UNDO, N_REDO, -4, N_POS, NUMMSG); int checktype(int type, clientinfo *ci) { if(ci) { - if(!ci->connected) switch(connectfilter[type]) - { - // allow only before authconnect - case 1: return !ci->connectauth ? type : -1; - // allow only during authconnect - case 2: return ci->connectauth ? type : -1; - // always allow - case 3: return type; - // never allow - default: return -1; - } - if(ci->local) return type; + return type; } switch(msgfilter[type]) { @@ -1520,7 +764,7 @@ namespace server void flushclientposition(clientinfo &ci) { - if(ci.position.empty() || (!hasnonlocalclients() && !demorecord)) return; + if(ci.position.empty() || !hasnonlocalclients()) return; packetbuf p(ci.position.length(), 0); p.put(ci.position.getbuf(), ci.position.length()); ci.position.setsize(0); @@ -1531,12 +775,10 @@ namespace server { if(wsbuf.empty()) return; int wslen = wsbuf.length(); - recordpacket(0, wsbuf.buf, wslen); wsbuf.put(wsbuf.buf, wslen); loopv(clients) { clientinfo &ci = *clients[i]; - if(ci.state.aitype != AI_NONE) continue; uchar *data = wsbuf.buf; int size = wslen; if(ci.wsdata >= wsbuf.buf) { data = ci.wsdata + ci.wslen; size -= ci.wslen; } @@ -1565,12 +807,10 @@ namespace server { if(wsbuf.empty()) return; int wslen = wsbuf.length(); - recordpacket(1, wsbuf.buf, wslen); wsbuf.put(wsbuf.buf, wslen); loopv(clients) { clientinfo &ci = *clients[i]; - if(ci.state.aitype != AI_NONE) continue; uchar *data = wsbuf.buf; int size = wslen; if(ci.wsdata >= wsbuf.buf) { data = ci.wsdata + ci.wslen; size -= ci.wslen; } @@ -1622,17 +862,13 @@ namespace server loopv(clients) { clientinfo &ci = *clients[i]; - if(ci.state.aitype != AI_NONE) continue; addposition(ws, wsbuf, mtu, ci, ci); - loopvj(ci.bots) addposition(ws, wsbuf, mtu, *ci.bots[j], ci); } sendpositions(ws, wsbuf); loopv(clients) { clientinfo &ci = *clients[i]; - if(ci.state.aitype != AI_NONE) continue; addmessages(ws, wsbuf, mtu, ci, ci); - loopvj(ci.bots) addmessages(ws, wsbuf, mtu, *ci.bots[j], ci); } sendmessages(ws, wsbuf); reliablemessages = false; @@ -1644,7 +880,7 @@ namespace server bool sendpackets(bool force) { - if(clients.empty() || (!hasnonlocalclients() && !demorecord)) return false; + if(clients.empty() || !hasnonlocalclients()) return false; enet_uint32 curtime = enet_time_get()-lastsend; if(curtime<40 && !force) return false; bool flush = buildworldstate(); @@ -1688,27 +924,12 @@ namespace server void putinitclient(clientinfo *ci, packetbuf &p) { - if(ci->state.aitype != AI_NONE) - { - putint(p, N_INITAI); - putint(p, ci->clientnum); - putint(p, ci->ownernum); - putint(p, ci->state.aitype); - putint(p, ci->state.skill); - putint(p, ci->playermodel); - putint(p, ci->playercolor); - putint(p, ci->team); - sendstring(ci->name, p); - } - else - { - putint(p, N_INITCLIENT); - putint(p, ci->clientnum); - sendstring(ci->name, p); - putint(p, ci->team); - putint(p, ci->playermodel); - putint(p, ci->playercolor); - } + putint(p, N_INITCLIENT); + putint(p, ci->clientnum); + sendstring(ci->name, p); + putint(p, ci->team); + putint(p, ci->playermodel); + putint(p, ci->playercolor); } void welcomeinitclient(packetbuf &p, int exclude = -1) @@ -1725,7 +946,7 @@ namespace server bool hasmap(clientinfo *ci) { return (m_edit && (clients.length() > 0 || ci->local)) || - (smapname[0] && (!m_timed || gamemillis < gamelimit || (ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) || numclients(ci->clientnum, true, true, true))); + (smapname[0] && (!m_timed || gamemillis < gamelimit || (ci->state.state==CS_SPECTATOR && !ci->local) || numclients(ci->clientnum, true, true, true))); } int welcomepacket(packetbuf &p, clientinfo *ci) @@ -1750,25 +971,6 @@ namespace server } putint(p, -1); } - bool hasmaster = false; - if(mastermode != MM_OPEN) - { - putint(p, N_CURRENTMASTER); - putint(p, mastermode); - hasmaster = true; - } - loopv(clients) if(clients[i]->privilege >= PRIV_MASTER) - { - if(!hasmaster) - { - putint(p, N_CURRENTMASTER); - putint(p, mastermode); - hasmaster = true; - } - putint(p, clients[i]->clientnum); - putint(p, clients[i]->privilege); - } - if(hasmaster) putint(p, -1); if(gamepaused) { putint(p, N_PAUSEGAME); @@ -1797,7 +999,7 @@ namespace server putint(p, ci->team); putint(p, -1); } - if(ci && (m_demo || m_mp(gamemode)) && ci->state.state!=CS_SPECTATOR) + if(ci && m_mp(gamemode) && ci->state.state!=CS_SPECTATOR) { if(smode && !smode->canspawn(ci, true)) { @@ -1844,18 +1046,6 @@ namespace server return 1; } - bool restorescore(clientinfo *ci) - { - //if(ci->local) return false; - savedscore *sc = findscore(ci, false); - if(sc) - { - sc->restore(ci->state); - return true; - } - return false; - } - void sendresume(clientinfo *ci) { servstate &gs = ci->state; @@ -1892,11 +1082,9 @@ namespace server void changemap(const char *s, int mode) { - stopdemo(); pausegame(false); changegamespeed(100); if(smode) smode->cleanup(); - aiman::clearai(); gamemode = mode; gamemillis = 0; @@ -1905,9 +1093,6 @@ namespace server nextexceeded = 0; copystring(smapname, s); loaditems(); - scores.shrink(0); - shouldcheckteamkills = false; - teamkills.shrink(0); loopv(clients) { clientinfo *ci = clients[i]; @@ -1932,129 +1117,14 @@ namespace server if(m_mp(gamemode) && ci->state.state!=CS_SPECTATOR) sendspawn(ci); } - aiman::changemap(); - - if(m_demo) - { - if(clients.length()) setupdemoplayback(); - } - else if(demonextmatch) - { - demonextmatch = false; - setupdemorecord(); - } - if(smode) smode->setup(); } - void rotatemap(bool next) - { - if(!maprotations.inrange(curmaprotation)) - { - changemap("", 0); - return; - } - if(next) - { - curmaprotation = findmaprotation(gamemode, smapname); - if(curmaprotation >= 0) nextmaprotation(); - else curmaprotation = smapname[0] ? max(findmaprotation(gamemode, ""), 0) : 0; - } - maprotation &rot = maprotations[curmaprotation]; - changemap(rot.map, rot.findmode(gamemode)); - } - - struct votecount - { - char *map; - int mode, count; - votecount() {} - votecount(char *s, int n) : map(s), mode(n), count(0) {} - }; - - void checkvotes(bool force = false) - { - vector votes; - int maxvotes = 0; - loopv(clients) - { - clientinfo *oi = clients[i]; - if(oi->state.state==CS_SPECTATOR && !oi->privilege && !oi->local) continue; - if(oi->state.aitype!=AI_NONE) continue; - maxvotes++; - if(!m_valid(oi->modevote)) continue; - votecount *vc = NULL; - loopvj(votes) if(!strcmp(oi->mapvote, votes[j].map) && oi->modevote==votes[j].mode) - { - vc = &votes[j]; - break; - } - if(!vc) vc = &votes.add(votecount(oi->mapvote, oi->modevote)); - vc->count++; - } - votecount *best = NULL; - loopv(votes) if(!best || votes[i].count > best->count || (votes[i].count == best->count && rnd(2))) best = &votes[i]; - if(force || (best && best->count > maxvotes/2)) - { - sendpackets(true); - if(demorecord) enddemorecord(); - if(best && (best->count > (force ? 1 : maxvotes/2))) - { - sendservmsg(force ? "vote passed by default" : "vote passed by majority"); - changemap(best->map, best->mode); - } - else rotatemap(true); - } - } - void forcemap(const char *map, int mode) { - stopdemo(); - if(!map[0] && !m_check(mode, M_EDIT)) - { - int idx = findmaprotation(mode, smapname); - if(idx < 0 && smapname[0]) idx = findmaprotation(mode, ""); - if(idx < 0) return; - map = maprotations[idx].map; - } - if(hasnonlocalclients()) sendservmsgf("local player forced %s on map %s", modeprettyname(mode), map[0] ? map : "[new map]"); changemap(map, mode); } - void vote(const char *map, int reqmode, int sender) - { - clientinfo *ci = getinfo(sender); - if(!ci || (ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) || (!ci->local && !m_mp(reqmode))) return; - if(!m_valid(reqmode)) return; - if(!map[0] && !m_check(reqmode, M_EDIT)) - { - int idx = findmaprotation(reqmode, smapname); - if(idx < 0 && smapname[0]) idx = findmaprotation(reqmode, ""); - if(idx < 0) return; - map = maprotations[idx].map; - } - if(lockmaprotation && !ci->local && ci->privilege < (lockmaprotation > 1 ? PRIV_ADMIN : PRIV_MASTER) && findmaprotation(reqmode, map) < 0) - { - sendf(sender, 1, "ris", N_SERVMSG, "This server has locked the map rotation."); - return; - } - copystring(ci->mapvote, map); - ci->modevote = reqmode; - if(ci->local || (ci->privilege && mastermode>=MM_VETO)) - { - sendpackets(true); - if(demorecord) enddemorecord(); - if(!ci->local || hasnonlocalclients()) - sendservmsgf("%s forced %s on map %s", colorname(ci), modeprettyname(ci->modevote), ci->mapvote[0] ? ci->mapvote : "[new map]"); - changemap(ci->mapvote, ci->modevote); - } - else - { - sendservmsgf("%s suggests %s on map %s (select map to vote)", colorname(ci), modeprettyname(reqmode), map[0] ? map : "[new map]"); - checkvotes(); - } - } - void checkintermission() { if(gamemillis >= gamelimit && !interm) @@ -2100,11 +1170,6 @@ namespace server if(smode) smode->died(target, actor); ts.state = CS_DEAD; ts.lastdeath = gamemillis; - if(actor!=target && m_teammode && actor->team == target->team) - { - actor->state.teamkills++; - addteamkill(actor, target, 1); - } ts.deadflush = ts.lastdeath + DEATHMILLIS; // don't issue respawn yet until DEATHMILLIS has elapsed // ts.respawn(); @@ -2278,8 +1343,7 @@ namespace server { gamemillis += curtime; - if(m_demo) readdemo(); - else if(!m_timed || gamemillis < gamelimit) + if(!m_timed || gamemillis < gamelimit) { processevents(); if(curtime) @@ -2295,7 +1359,6 @@ namespace server } } } - aiman::checkai(); if(smode) smode->update(); } } @@ -2309,22 +1372,17 @@ namespace server loopvrev(clients) { clientinfo &c = *clients[i]; - if(c.state.aitype != AI_NONE) continue; if(c.checkexceeded()) disconnect_client(c.clientnum, DISC_MSGERR); else c.scheduleexceeded(); } } - if(shouldcheckteamkills) checkteamkills(); - if(shouldstep && !gamepaused) { if(m_timed && smapname[0] && gamemillis-curtime>0) checkintermission(); if(interm > 0 && gamemillis>interm) { - if(demorecord) enddemorecord(); interm = -1; - checkvotes(true); } } @@ -2337,7 +1395,6 @@ namespace server if(smode) smode->leavegame(ci); ci->state.state = CS_SPECTATOR; ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; - if(!ci->local && (!ci->privilege || ci->warned)) aiman::removeai(ci); sendf(-1, 1, "ri3", N_SPECTATOR, ci->clientnum, 1); } @@ -2362,7 +1419,7 @@ namespace server loopv(clients) { clientinfo *ci = clients[i]; - if(ci->state.state==CS_SPECTATOR || ci->state.aitype != AI_NONE) continue; + if(ci->state.state==CS_SPECTATOR) continue; total++; if(!ci->clientmap[0]) { @@ -2383,7 +1440,7 @@ namespace server loopv(clients) { clientinfo *ci = clients[i]; - if(ci->state.state==CS_SPECTATOR || ci->state.aitype != AI_NONE || ci->clientmap[0] || ci->mapcrc >= 0 || (req < 0 && ci->warned)) continue; + if(ci->state.state==CS_SPECTATOR || ci->clientmap[0] || ci->mapcrc >= 0 || (req < 0 && ci->warned)) continue; formatstring(msg, "%s has modified map \"%s\"", colorname(ci), smapname); sendf(req, 1, "ris", N_SERVMSG, msg); if(req < 0) ci->warned = true; @@ -2394,7 +1451,7 @@ namespace server if(i || info.matches <= crcs[i+1].matches) loopvj(clients) { clientinfo *ci = clients[j]; - if(ci->state.state==CS_SPECTATOR || ci->state.aitype != AI_NONE || !ci->clientmap[0] || ci->mapcrc != info.crc || (req < 0 && ci->warned)) continue; + if(ci->state.state==CS_SPECTATOR || !ci->clientmap[0] || ci->mapcrc != info.crc || (req < 0 && ci->warned)) continue; formatstring(msg, "%s has modified map \"%s\"", colorname(ci), smapname); sendf(req, 1, "ris", N_SERVMSG, msg); if(req < 0) ci->warned = true; @@ -2418,21 +1475,18 @@ namespace server ci->state.state = CS_DEAD; ci->state.respawn(); ci->state.lasttimeplayed = lastmillis; - aiman::addclient(ci); sendf(-1, 1, "ri3", N_SPECTATOR, ci->clientnum, 0); if(ci->clientmap[0] || ci->mapcrc) checkmaps(); - if(!hasmap(ci)) rotatemap(true); } void sendservinfo(clientinfo *ci) { - sendf(ci->clientnum, 1, "ri5ss", N_SERVINFO, ci->clientnum, PROTOCOL_VERSION, ci->sessionid, serverpass[0] ? 1 : 0, serverdesc, serverauth); + sendf(ci->clientnum, 1, "ri4", N_SERVINFO, ci->clientnum, PROTOCOL_VERSION, ci->sessionid); } void noclients() { bannedips.shrink(0); - aiman::clearai(); } void localconnect(int n) @@ -2449,7 +1503,6 @@ namespace server void localdisconnect(int n) { - if(m_demo) enddemoplayback(); clientdisconnect(n); } @@ -2469,16 +1522,12 @@ namespace server void clientdisconnect(int n) { clientinfo *ci = getinfo(n); - loopv(clients) if(clients[i]->authkickvictim == ci->clientnum) clients[i]->cleanauth(); if(ci->connected) { - if(ci->privilege) setmaster(ci, false); if(smode) smode->leavegame(ci, true); ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; - savescore(ci); sendf(-1, 1, "ri2", N_CDIS, n); clients.removeobj(ci); - aiman::removeai(ci); if(!numclients(-1, false, true)) noclients(); // bans clear when server empties if(ci->local) checkpausegame(); } @@ -2487,214 +1536,29 @@ namespace server int reserveclients() { return 3; } - extern void verifybans(); - - struct banlist - { - vector bans; - - void clear() { bans.shrink(0); } - - bool check(uint ip) - { - loopv(bans) if(bans[i].check(ip)) return true; - return false; - } - - void add(const char *ipname) - { - ipmask ban; - ban.parse(ipname); - bans.add(ban); - - verifybans(); - } - } ipbans, gbans; - - bool checkbans(uint ip) - { - loopv(bannedips) if(bannedips[i].ip==ip) return true; - return ipbans.check(ip) || gbans.check(ip); - } - - void verifybans() - { - loopvrev(clients) - { - clientinfo *ci = clients[i]; - if(ci->state.aitype != AI_NONE || ci->local || ci->privilege >= PRIV_ADMIN) continue; - if(checkbans(getclientip(ci->clientnum))) disconnect_client(ci->clientnum, DISC_IPBAN); - } - } - - ICOMMAND(clearipbans, "", (), ipbans.clear()); - ICOMMAND(ipban, "s", (const char *ipname), ipbans.add(ipname)); - - int allowconnect(clientinfo *ci, const char *pwd = "") - { - if(ci->local) return DISC_NONE; - if(!m_mp(gamemode)) return DISC_LOCAL; - if(serverpass[0]) - { - if(!checkpassword(ci, serverpass, pwd)) return DISC_PASSWORD; - return DISC_NONE; - } - if(adminpass[0] && checkpassword(ci, adminpass, pwd)) return DISC_NONE; - if(numclients(-1, false, true)>=maxclients) return DISC_MAXCLIENTS; - uint ip = getclientip(ci->clientnum); - if(checkbans(ip)) return DISC_IPBAN; - if(mastermode>=MM_PRIVATE && allowedips.find(ip)<0) return DISC_PRIVATE; - return DISC_NONE; - } - bool allowbroadcast(int n) { clientinfo *ci = getinfo(n); return ci && ci->connected; } - clientinfo *findauth(uint id) - { - loopv(clients) if(clients[i]->authreq == id) return clients[i]; - return NULL; - } - - void authfailed(clientinfo *ci) - { - if(!ci) return; - ci->cleanauth(); - if(ci->connectauth) disconnect_client(ci->clientnum, ci->connectauth); - } - - void authfailed(uint id) - { - authfailed(findauth(id)); - } - - void authsucceeded(uint id) - { - clientinfo *ci = findauth(id); - if(!ci) return; - ci->cleanauth(ci->connectauth!=0); - if(ci->connectauth) connected(ci); - if(ci->authkickvictim >= 0) - { - if(setmaster(ci, true, "", ci->authname, NULL, PRIV_AUTH, false, true)) - trykick(ci, ci->authkickvictim, ci->authkickreason, ci->authname, NULL, PRIV_AUTH); - ci->cleanauthkick(); - } - else setmaster(ci, true, "", ci->authname, NULL, PRIV_AUTH); - } - - void authchallenged(uint id, const char *val, const char *desc = "") - { - clientinfo *ci = findauth(id); - if(!ci) return; - sendf(ci->clientnum, 1, "risis", N_AUTHCHAL, desc, id, val); - } - - uint nextauthreq = 0; - - bool tryauth(clientinfo *ci, const char *user, const char *desc) - { - ci->cleanauth(); - if(!nextauthreq) nextauthreq = 1; - ci->authreq = nextauthreq++; - filtertext(ci->authname, user, false, false, 100); - copystring(ci->authdesc, desc); - if(ci->authdesc[0]) - { - userinfo *u = users.access(userkey(ci->authname, ci->authdesc)); - if(u) - { - uint seed[3] = { ::hthash(serverauth) + detrnd(size_t(ci) + size_t(user) + size_t(desc), 0x10000), uint(totalmillis), randomMT() }; - vector buf; - ci->authchallenge = genchallenge(u->pubkey, seed, sizeof(seed), buf); - sendf(ci->clientnum, 1, "risis", N_AUTHCHAL, desc, ci->authreq, buf.getbuf()); - } - else ci->cleanauth(); - } - else if(!requestmasterf("reqauth %u %s\n", ci->authreq, ci->authname)) - { - ci->cleanauth(); - sendf(ci->clientnum, 1, "ris", N_SERVMSG, "not connected to authentication server"); - } - if(ci->authreq) return true; - if(ci->connectauth) disconnect_client(ci->clientnum, ci->connectauth); - return false; - } - - bool answerchallenge(clientinfo *ci, uint id, char *val, const char *desc) - { - if(ci->authreq != id || strcmp(ci->authdesc, desc)) - { - ci->cleanauth(); - return !ci->connectauth; - } - for(char *s = val; *s; s++) - { - if(!isxdigit(*s)) { *s = '\0'; break; } - } - if(desc[0]) - { - if(ci->authchallenge && checkchallenge(val, ci->authchallenge)) - { - userinfo *u = users.access(userkey(ci->authname, ci->authdesc)); - if(u) - { - if(ci->connectauth) connected(ci); - if(ci->authkickvictim >= 0) - { - if(setmaster(ci, true, "", ci->authname, ci->authdesc, u->privilege, false, true)) - trykick(ci, ci->authkickvictim, ci->authkickreason, ci->authname, ci->authdesc, u->privilege); - } - else setmaster(ci, true, "", ci->authname, ci->authdesc, u->privilege); - } - } - ci->cleanauth(); - } - else if(!requestmasterf("confauth %u %s\n", id, val)) - { - ci->cleanauth(); - sendf(ci->clientnum, 1, "ris", N_SERVMSG, "not connected to authentication server"); - } - return ci->authreq || !ci->connectauth; - } - void masterconnected() { } void masterdisconnected() { - loopvrev(clients) - { - clientinfo *ci = clients[i]; - if(ci->authreq) authfailed(ci); - } } void processmasterinput(const char *cmd, int cmdlen, const char *args) { - uint id; - string val; - if(sscanf(cmd, "failauth %u", &id) == 1) - authfailed(id); - else if(sscanf(cmd, "succauth %u", &id) == 1) - authsucceeded(id); - else if(sscanf(cmd, "chalauth %u %255s", &id, val) == 2) - authchallenged(id, val); - else if(matchstring(cmd, cmdlen, "cleargbans")) - gbans.clear(); - else if(sscanf(cmd, "addgban %100s", val) == 1) - gbans.add(val); } void receivefile(int sender, uchar *data, int len) { if(!m_edit || len <= 0 || len > 4*1024*1024) return; clientinfo *ci = getinfo(sender); - if(ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) return; + if(ci->state.state==CS_SPECTATOR && !ci->local) return; if(mapdata) DELETEP(mapdata); mapdata = opentempfile("mapdata", "w+b"); if(!mapdata) { sendf(sender, 1, "ris", N_SERVMSG, "failed to open temporary file for map"); return; } @@ -2719,16 +1583,11 @@ namespace server void connected(clientinfo *ci) { - if(m_demo) enddemoplayback(); - - if(!hasmap(ci)) rotatemap(false); - shouldstep = true; connects.removeobj(ci); clients.add(ci); - ci->connectauth = 0; ci->connected = true; ci->needclipboard = totalmillis ? totalmillis : 1; if(mastermode>=MM_LOCKED) ci->state.state = CS_SPECTATOR; @@ -2737,14 +1596,7 @@ namespace server ci->team = m_teammode ? chooseworstteam(ci) : 0; sendwelcome(ci); - if(restorescore(ci)) sendresume(ci); sendinitclient(ci); - - aiman::addclient(ci); - - if(m_demo) setupdemoplayback(); - - if(servermotd[0]) sendf(ci->clientnum, 1, "ris", N_SERVMSG, servermotd); } void parsepacket(int sender, int chan, packetbuf &p) // has to parse exactly each byte of the packet @@ -2767,36 +1619,7 @@ namespace server copystring(ci->name, text, MAXNAMELEN+1); ci->playermodel = getint(p); ci->playercolor = getint(p); - - string password, authdesc, authname; - getstring(password, p, sizeof(password)); - getstring(authdesc, p, sizeof(authdesc)); - getstring(authname, p, sizeof(authname)); - int disc = allowconnect(ci, password); - if(disc) - { - if(disc == DISC_LOCAL || !serverauth[0] || strcmp(serverauth, authdesc) || !tryauth(ci, authname, authdesc)) - { - disconnect_client(sender, disc); - return; - } - ci->connectauth = disc; - } - else connected(ci); - break; - } - - case N_AUTHANS: - { - string desc, ans; - getstring(desc, p, sizeof(desc)); - uint id = (uint)getint(p); - getstring(ans, p, sizeof(ans)); - if(!answerchallenge(ci, id, ans, desc)) - { - disconnect_client(sender, ci->connectauth); - return; - } + connected(ci); break; } @@ -2818,9 +1641,9 @@ namespace server if(p.packet->flags&ENET_PACKET_FLAG_RELIABLE) reliablemessages = true; #define QUEUE_AI clientinfo *cm = cq; - #define QUEUE_MSG { if(cm && (!cm->local || demorecord || hasnonlocalclients())) while(curmsgmessages.add(p.buf[curmsg++]); } + #define QUEUE_MSG { if(cm && (!cm->local || hasnonlocalclients())) while(curmsgmessages.add(p.buf[curmsg++]); } #define QUEUE_BUF(body) { \ - if(cm && (!cm->local || demorecord || hasnonlocalclients())) \ + if(cm && (!cm->local || hasnonlocalclients())) \ { \ curmsg = p.length(); \ { body; } \ @@ -2856,7 +1679,7 @@ namespace server } if(cp) { - if((!ci->local || demorecord || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) + if((!ci->local || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) { if(!ci->local && !m_edit && max(vel.magnitude2(), (float)fabs(vel.z)) >= 180) cp->setexceeded(); @@ -2870,45 +1693,6 @@ namespace server break; } - case N_TELEPORT: - { - int pcn = getint(p), teleport = getint(p), teledest = getint(p); - clientinfo *cp = getinfo(pcn); - if(cp && pcn != sender && cp->ownernum != sender) cp = NULL; - if(cp && (!ci->local || demorecord || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) - { - flushclientposition(*cp); - sendf(-1, 0, "ri4x", N_TELEPORT, pcn, teleport, teledest, cp->ownernum); - } - break; - } - - case N_JUMPPAD: - { - int pcn = getint(p), jumppad = getint(p); - clientinfo *cp = getinfo(pcn); - if(cp && pcn != sender && cp->ownernum != sender) cp = NULL; - if(cp && (!ci->local || demorecord || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) - { - cp->setpushed(); - flushclientposition(*cp); - sendf(-1, 0, "ri3x", N_JUMPPAD, pcn, jumppad, cp->ownernum); - } - break; - } - - case N_FROMAI: - { - int qcn = getint(p); - if(qcn < 0) cq = ci; - else - { - cq = getinfo(qcn); - if(cq && qcn != sender && cq->ownernum != sender) cq = NULL; - } - break; - } - case N_EDITMODE: { int val = getint(p); @@ -2975,16 +1759,6 @@ namespace server sendspawn(cq); break; - case N_GUNSELECT: - { - int gunselect = getint(p); - if(!cq || cq->state.state!=CS_ALIVE || !validgun(gunselect)) break; - cq->state.gunselect = gunselect; - QUEUE_AI; - QUEUE_MSG; - break; - } - case N_SPAWN: { int ls = getint(p), gunselect = getint(p); @@ -3002,73 +1776,6 @@ namespace server break; } - case N_SUICIDE: - { - if(cq) cq->addevent(new suicideevent); - break; - } - - case N_SHOOT: - { - shotevent *shot = new shotevent; - shot->id = getint(p); - shot->millis = cq ? cq->geteventmillis(gamemillis, shot->id) : 0; - shot->atk = getint(p); - loopk(3) shot->from[k] = getint(p)/DMF; - loopk(3) shot->to[k] = getint(p)/DMF; - int hits = getint(p); - loopk(hits) - { - if(p.overread()) break; - hitinfo &hit = shot->hits.add(); - hit.target = getint(p); - hit.lifesequence = getint(p); - hit.dist = getint(p)/DMF; - hit.rays = getint(p); - loopk(3) hit.dir[k] = getint(p)/DNF; - } - if(cq) - { - cq->addevent(shot); - cq->setpushed(); - } - else delete shot; - break; - } - - case N_EXPLODE: - { - explodeevent *exp = new explodeevent; - int cmillis = getint(p); - exp->millis = cq ? cq->geteventmillis(gamemillis, cmillis) : 0; - exp->atk = getint(p); - exp->id = getint(p); - int hits = getint(p); - loopk(hits) - { - if(p.overread()) break; - hitinfo &hit = exp->hits.add(); - hit.target = getint(p); - hit.lifesequence = getint(p); - hit.dist = getint(p)/DMF; - hit.rays = getint(p); - loopk(3) hit.dir[k] = getint(p)/DNF; - } - if(cq) cq->addevent(exp); - else delete exp; - break; - } - - case N_ITEMPICKUP: - { - int n = getint(p); - if(!cq) break; - pickupevent *pickup = new pickupevent; - pickup->ent = n; - cq->addevent(pickup); - break; - } - case N_TEXT: { QUEUE_AI; @@ -3083,12 +1790,12 @@ namespace server case N_SAYTEAM: { getstring(text, p); - if(!ci || !cq || (ci->state.state==CS_SPECTATOR && !ci->local && !ci->privilege) || !m_teammode || !validteam(cq->team)) break; + if(!ci || !cq || (ci->state.state==CS_SPECTATOR && !ci->local) || !m_teammode || !validteam(cq->team)) break; filtertext(text, text, true, true); loopv(clients) { clientinfo *t = clients[i]; - if(t==cq || t->state.state==CS_SPECTATOR || t->state.aitype != AI_NONE || cq->team != t->team) continue; + if(t==cq || t->state.state==CS_SPECTATOR || cq->team != t->team) continue; sendf(t->clientnum, 1, "riis", N_SAYTEAM, cq->clientnum, text); } if(isdedicatedserver() && cq) logoutf("%s <%s>: %s", colorname(cq), teamnames[cq->team], text); @@ -3105,62 +1812,6 @@ namespace server break; } - case N_SWITCHMODEL: - { - ci->playermodel = getint(p); - QUEUE_MSG; - break; - } - - case N_SWITCHCOLOR: - { - ci->playercolor = getint(p); - QUEUE_MSG; - break; - } - - case N_SWITCHTEAM: - { - int team = getint(p); - if(m_teammode && validteam(team) && ci->team != team && (!smode || smode->canchangeteam(ci, ci->team, team))) - { - if(ci->state.state==CS_ALIVE) suicide(ci); - ci->team = team; - aiman::changeteam(ci); - sendf(-1, 1, "riiii", N_SETTEAM, sender, ci->team, ci->state.state==CS_SPECTATOR ? -1 : 0); - } - break; - } - - case N_MAPVOTE: - { - getstring(text, p); - filtertext(text, text, false); - fixmapname(text); - int reqmode = getint(p); - vote(text, reqmode, sender); - break; - } - - case N_ITEMLIST: - { - if((ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) || !notgotitems || strcmp(ci->clientmap, smapname)) { while(getint(p)>=0 && !p.overread()) getint(p); break; } - int n; - while((n = getint(p))>=0 && nping = ping; - loopv(ci->bots) ci->bots[i]->ping = ping; } QUEUE_MSG; break; } - case N_MASTERMODE: - { - int mm = getint(p); - if((ci->privilege || ci->local) && mm>=MM_OPEN && mm<=MM_PRIVATE) - { - if((ci->privilege>=PRIV_ADMIN || ci->local) || (mastermask&(1<=MM_PRIVATE) - { - loopv(clients) allowedips.add(getclientip(clients[i]->clientnum)); - } - sendf(-1, 1, "rii", N_MASTERMODE, mastermode); - //sendservmsgf("mastermode is now %s (%d)", mastermodename(mastermode), mastermode); - } - else - { - sendf(sender, 1, "ris", N_SERVMSG, tempformatstring("mastermode %d is disabled on this server", mm)); - } - } - break; - } - - case N_CLEARBANS: - { - if(ci->privilege || ci->local) - { - bannedips.shrink(0); - sendservmsg("cleared all bans"); - } - break; - } - - case N_KICK: - { - int victim = getint(p); - getstring(text, p); - filtertext(text, text); - trykick(ci, victim, text); - break; - } - - case N_SPECTATOR: - { - int spectator = getint(p), val = getint(p); - if(!ci->privilege && !ci->local && (spectator!=sender || (ci->state.state==CS_SPECTATOR && mastermode>=MM_LOCKED))) break; - clientinfo *spinfo = (clientinfo *)getclientinfo(spectator); // no bots - if(!spinfo || !spinfo->connected || (spinfo->state.state==CS_SPECTATOR ? val : !val)) break; - - if(spinfo->state.state!=CS_SPECTATOR && val) forcespectator(spinfo); - else if(spinfo->state.state==CS_SPECTATOR && !val) unspectate(spinfo); - - if(cq && cq != ci && cq->ownernum != ci->clientnum) cq = NULL; - break; - } - - case N_SETTEAM: - { - int who = getint(p), team = getint(p); - if(!ci->privilege && !ci->local) break; - clientinfo *wi = getinfo(who); - if(!m_teammode || !validteam(team) || !wi || !wi->connected || wi->team == team) break; - if(!smode || smode->canchangeteam(wi, wi->team, team)) - { - if(wi->state.state==CS_ALIVE) suicide(wi); - wi->team = team; - } - aiman::changeteam(wi); - sendf(-1, 1, "riiii", N_SETTEAM, who, wi->team, 1); - break; - } - case N_FORCEINTERMISSION: if(ci->local && !hasnonlocalclients()) startintermission(); break; - case N_RECORDDEMO: - { - int val = getint(p); - if(ci->privilege < (restrictdemos ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; - if(!maxdemos || !maxdemosize) - { - sendf(ci->clientnum, 1, "ris", N_SERVMSG, "the server has disabled demo recording"); - break; - } - demonextmatch = val!=0; - sendservmsgf("demo recording is %s for next match", demonextmatch ? "enabled" : "disabled"); - break; - } - - case N_STOPDEMO: - { - if(ci->privilege < (restrictdemos ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; - stopdemo(); - break; - } - - case N_CLEARDEMOS: - { - int demo = getint(p); - if(ci->privilege < (restrictdemos ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; - cleardemos(demo); - break; - } - - case N_LISTDEMOS: - if(!ci->privilege && !ci->local && ci->state.state==CS_SPECTATOR) break; - listdemos(sender); - break; - - case N_GETDEMO: - { - int n = getint(p); - if(!ci->privilege && !ci->local && ci->state.state==CS_SPECTATOR) break; - senddemo(ci, n); - break; - } - - case N_GETMAP: - if(!mapdata) sendf(sender, 1, "ris", N_SERVMSG, "no map to send"); - else if(ci->getmap) sendf(sender, 1, "ris", N_SERVMSG, "already sending map"); - else - { - sendservmsgf("[%s is getting the map]", colorname(ci)); - if((ci->getmap = sendfile(sender, 2, mapdata, "ri", N_SENDMAP))) - ci->getmap->freeCallback = freegetmap; - ci->needclipboard = totalmillis ? totalmillis : 1; - } - break; - case N_NEWMAP: { int size = getint(p); - if(!ci->privilege && !ci->local && ci->state.state==CS_SPECTATOR) break; + if(!ci->local && ci->state.state==CS_SPECTATOR) break; if(size>=0) { smapname[0] = '\0'; @@ -3360,94 +1883,9 @@ namespace server break; } - case N_SETMASTER: - { - int mn = getint(p), val = getint(p); - getstring(text, p); - if(mn != ci->clientnum) - { - if(!ci->privilege && !ci->local) break; - clientinfo *minfo = (clientinfo *)getclientinfo(mn); - if(!minfo || !minfo->connected || (!ci->local && minfo->privilege >= ci->privilege) || (val && minfo->privilege)) break; - setmaster(minfo, val!=0, "", NULL, NULL, PRIV_MASTER, true); - } - else setmaster(ci, val!=0, text); - // don't broadcast the master password - break; - } - - case N_ADDBOT: - { - aiman::reqadd(ci, getint(p)); - break; - } - - case N_DELBOT: - { - aiman::reqdel(ci); - break; - } - - case N_BOTLIMIT: - { - int limit = getint(p); - if(ci) aiman::setbotlimit(ci, limit); - break; - } - - case N_BOTBALANCE: - { - int balance = getint(p); - if(ci) aiman::setbotbalance(ci, balance!=0); - break; - } - - case N_AUTHTRY: - { - string desc, name; - getstring(desc, p, sizeof(desc)); - getstring(name, p, sizeof(name)); - tryauth(ci, name, desc); - break; - } - - case N_AUTHKICK: - { - string desc, name; - getstring(desc, p, sizeof(desc)); - getstring(name, p, sizeof(name)); - int victim = getint(p); - getstring(text, p); - filtertext(text, text); - int authpriv = PRIV_AUTH; - if(desc[0]) - { - userinfo *u = users.access(userkey(name, desc)); - if(u) authpriv = u->privilege; else break; - } - if(ci->local || ci->privilege >= authpriv) trykick(ci, victim, text); - else if(trykick(ci, victim, text, name, desc, authpriv, true) && tryauth(ci, name, desc)) - { - ci->authkickvictim = victim; - ci->authkickreason = newstring(text); - } - break; - } - - case N_AUTHANS: - { - string desc, ans; - getstring(desc, p, sizeof(desc)); - uint id = (uint)getint(p); - getstring(ans, p, sizeof(ans)); - answerchallenge(ci, id, ans, desc); - break; - } - case N_PAUSEGAME: { int val = getint(p); - if(ci->privilege < (restrictpausegame ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; pausegame(val > 0, ci); break; } @@ -3455,7 +1893,6 @@ namespace server case N_GAMESPEED: { int val = getint(p); - if(ci->privilege < (restrictgamespeed ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; changegamespeed(val, ci); break; } @@ -3562,35 +1999,24 @@ namespace server int masterport() { return TESSERACT_MASTER_PORT; } int numchannels() { return 3; } - #include "extinfo.hh" - void serverinforeply(ucharbuf &req, ucharbuf &p) { - if(req.remaining() && !getint(req)) - { - extserverinforeply(req, p); - return; - } - putint(p, PROTOCOL_VERSION); putint(p, numclients(-1, false, true)); putint(p, maxclients); putint(p, gamepaused || gamespeed != 100 ? 5 : 3); // number of attrs following putint(p, gamemode); putint(p, m_timed ? max((gamelimit - gamemillis)/1000, 0) : 0); - putint(p, serverpass[0] ? MM_PASSWORD : (!m_mp(gamemode) ? MM_PRIVATE : (mastermode || mastermask&MM_AUTOAPPROVE ? mastermode : MM_AUTH))); + putint(p, MM_PRIVATE); if(gamepaused || gamespeed != 100) { putint(p, gamepaused ? 1 : 0); putint(p, gamespeed); } sendstring(smapname, p); - sendstring(serverdesc, p); sendserverinforeply(p); } int protocolversion() { return PROTOCOL_VERSION; } - - #include "aiman.hh" } diff --git a/src/game/waypoint.cc b/src/game/waypoint.cc deleted file mode 100644 index 8e98d40..0000000 --- a/src/game/waypoint.cc +++ /dev/null @@ -1,806 +0,0 @@ -#include "game.hh" - -extern selinfo sel; - -namespace ai -{ - using namespace game; - - vector waypoints; - - bool clipped(const vec &o) - { - int material = lookupmaterial(o), clipmat = material&MATF_CLIP; - return clipmat == MAT_CLIP || material&MAT_DEATH || (material&MATF_VOLUME) == MAT_LAVA; - } - - int getweight(const vec &o) - { - vec pos = o; pos.z += ai::JUMPMIN; - if(!insideworld(vec(pos.x, pos.y, min(pos.z, getworldsize() - 1e-3f)))) return -2; - float dist = raycube(pos, vec(0, 0, -1), 0, RAY_CLIPMAT); - int posmat = lookupmaterial(pos), weight = 1; - if(isliquid(posmat&MATF_VOLUME)) weight *= 5; - if(dist >= 0) - { - weight = int(dist/ai::JUMPMIN); - pos.z -= clamp(dist-8.0f, 0.0f, pos.z); - int trgmat = lookupmaterial(pos); - if(trgmat&MAT_DEATH || (trgmat&MATF_VOLUME) == MAT_LAVA) weight *= 10; - else if(isliquid(trgmat&MATF_VOLUME)) weight *= 2; - } - return weight; - } - - enum - { - WPCACHE_STATIC = 0, - WPCACHE_DYNAMIC, - NUMWPCACHES - }; - - struct wpcache - { - struct node - { - float split[2]; - uint child[2]; - - int axis() const { return child[0]>>30; } - int childindex(int which) const { return child[which]&0x3FFFFFFF; } - bool isleaf(int which) const { return (child[1]&(1<<(30+which)))!=0; } - }; - - vector nodes; - int firstwp, lastwp; - vec bbmin, bbmax; - - wpcache() { clear(); } - - void clear() - { - nodes.setsize(0); - firstwp = lastwp = -1; - bbmin = vec(1e16f, 1e16f, 1e16f); - bbmax = vec(-1e16f, -1e16f, -1e16f); - } - - void build(int first = 0, int last = -1) - { - if(last < 0) last = waypoints.length(); - vector indices; - for(int i = first; i < last; i++) - { - waypoint &w = waypoints[i]; - indices.add(i); - if(firstwp < 0) firstwp = i; - float radius = WAYPOINTRADIUS; - bbmin.min(vec(w.o).sub(radius)); - bbmax.max(vec(w.o).add(radius)); - } - if(first < last) lastwp = max(lastwp, last-1); - if(indices.length()) - { - nodes.reserve(indices.length()); - build(indices.getbuf(), indices.length(), bbmin, bbmax); - } - } - - void build(int *indices, int numindices, const vec &vmin, const vec &vmax) - { - int axis = 2; - loopk(2) if(vmax[k] - vmin[k] > vmax[axis] - vmin[axis]) axis = k; - - vec leftmin(1e16f, 1e16f, 1e16f), leftmax(-1e16f, -1e16f, -1e16f), rightmin(1e16f, 1e16f, 1e16f), rightmax(-1e16f, -1e16f, -1e16f); - float split = 0.5f*(vmax[axis] + vmin[axis]), splitleft = -1e16f, splitright = 1e16f; - int left, right; - for(left = 0, right = numindices; left < right;) - { - waypoint &w = waypoints[indices[left]]; - float radius = WAYPOINTRADIUS; - if(max(split - (w.o[axis]-radius), 0.0f) > max((w.o[axis]+radius) - split, 0.0f)) - { - ++left; - splitleft = max(splitleft, w.o[axis]+radius); - leftmin.min(vec(w.o).sub(radius)); - leftmax.max(vec(w.o).add(radius)); - } - else - { - --right; - swap(indices[left], indices[right]); - splitright = min(splitright, w.o[axis]-radius); - rightmin.min(vec(w.o).sub(radius)); - rightmax.max(vec(w.o).add(radius)); - } - } - - if(!left || right==numindices) - { - leftmin = rightmin = vec(1e16f, 1e16f, 1e16f); - leftmax = rightmax = vec(-1e16f, -1e16f, -1e16f); - left = right = numindices/2; - splitleft = -1e16f; - splitright = 1e16f; - loopi(numindices) - { - waypoint &w = waypoints[indices[i]]; - float radius = WAYPOINTRADIUS; - if(i < left) - { - splitleft = max(splitleft, w.o[axis]+radius); - leftmin.min(vec(w.o).sub(radius)); - leftmax.max(vec(w.o).add(radius)); - } - else - { - splitright = min(splitright, w.o[axis]-radius); - rightmin.min(vec(w.o).sub(radius)); - rightmax.max(vec(w.o).add(radius)); - } - } - } - - int offset = nodes.length(); - node &curnode = nodes.add(); - curnode.split[0] = splitleft; - curnode.split[1] = splitright; - - if(left<=1) curnode.child[0] = (axis<<30) | (left>0 ? indices[0] : 0x3FFFFFFF); - else - { - curnode.child[0] = (axis<<30) | (nodes.length()-offset); - if(left) build(indices, left, leftmin, leftmax); - } - - if(numindices-right<=1) curnode.child[1] = (1<<31) | (left<=1 ? 1<<30 : 0) | (numindices-right>0 ? indices[right] : 0x3FFFFFFF); - else - { - curnode.child[1] = (left<=1 ? 1<<30 : 0) | (nodes.length()-offset); - if(numindices-right) build(&indices[right], numindices-right, rightmin, rightmax); - } - } - } wpcaches[NUMWPCACHES]; - - static int invalidatedwpcaches = 0, clearedwpcaches = (1<= 1000) { numinvalidatewpcaches = 0; invalidatedwpcaches = (1<= wpcaches[i].firstwp && wp <= wpcaches[i].lastwp) { invalidatedwpcaches |= 1< 0 ? wpcaches[i-1].lastwp+1 : 1, i+1 >= NUMWPCACHES || wpcaches[i+1].firstwp < 0 ? -1 : wpcaches[i+1].firstwp); - clearedwpcaches = 0; - lastwpcache = waypoints.length(); - - wpavoid.clear(); - loopv(waypoints) if(waypoints[i].weight < 0) wpavoid.avoidnear(NULL, waypoints[i].o.z + WAYPOINTRADIUS, waypoints[i].o, WAYPOINTRADIUS); - } - - struct wpcachestack - { - wpcache::node *node; - float tmin, tmax; - }; - - vector wpcachestack; - - int closestwaypoint(const vec &pos, float mindist, bool links, gameent *d) - { - if(waypoints.empty()) return -1; - if(clearedwpcaches) buildwpcache(); - - #define CHECKCLOSEST(index) do { \ - int n = (index); \ - if(n < waypoints.length()) \ - { \ - const waypoint &w = waypoints[n]; \ - if(!links || w.links[0]) \ - { \ - float dist = w.o.squaredist(pos); \ - if(dist < mindist*mindist) { closest = n; mindist = sqrtf(dist); } \ - } \ - } \ - } while(0) - int closest = -1; - wpcache::node *curnode; - loop(which, NUMWPCACHES) if(wpcaches[which].firstwp >= 0) for(curnode = &wpcaches[which].nodes[0], wpcachestack.setsize(0);;) - { - int axis = curnode->axis(); - float dist1 = pos[axis] - curnode->split[0], dist2 = curnode->split[1] - pos[axis]; - if(dist1 >= mindist) - { - if(dist2 < mindist) - { - if(!curnode->isleaf(1)) { curnode += curnode->childindex(1); continue; } - CHECKCLOSEST(curnode->childindex(1)); - } - } - else if(curnode->isleaf(0)) - { - CHECKCLOSEST(curnode->childindex(0)); - if(dist2 < mindist) - { - if(!curnode->isleaf(1)) { curnode += curnode->childindex(1); continue; } - CHECKCLOSEST(curnode->childindex(1)); - } - } - else - { - if(dist2 < mindist) - { - if(!curnode->isleaf(1)) wpcachestack.add(curnode + curnode->childindex(1)); - else CHECKCLOSEST(curnode->childindex(1)); - } - curnode += curnode->childindex(0); - continue; - } - if(wpcachestack.empty()) break; - curnode = wpcachestack.pop(); - } - for(int i = lastwpcache; i < waypoints.length(); i++) { CHECKCLOSEST(i); } - return closest; - } - - void findwaypointswithin(const vec &pos, float mindist, float maxdist, vector &results) - { - if(waypoints.empty()) return; - if(clearedwpcaches) buildwpcache(); - - float mindist2 = mindist*mindist, maxdist2 = maxdist*maxdist; - #define CHECKWITHIN(index) do { \ - int n = (index); \ - if(n < waypoints.length()) \ - { \ - const waypoint &w = waypoints[n]; \ - float dist = w.o.squaredist(pos); \ - if(dist > mindist2 && dist < maxdist2) results.add(n); \ - } \ - } while(0) - wpcache::node *curnode; - loop(which, NUMWPCACHES) if(wpcaches[which].firstwp >= 0) for(curnode = &wpcaches[which].nodes[0], wpcachestack.setsize(0);;) - { - int axis = curnode->axis(); - float dist1 = pos[axis] - curnode->split[0], dist2 = curnode->split[1] - pos[axis]; - if(dist1 >= maxdist) - { - if(dist2 < maxdist) - { - if(!curnode->isleaf(1)) { curnode += curnode->childindex(1); continue; } - CHECKWITHIN(curnode->childindex(1)); - } - } - else if(curnode->isleaf(0)) - { - CHECKWITHIN(curnode->childindex(0)); - if(dist2 < maxdist) - { - if(!curnode->isleaf(1)) { curnode += curnode->childindex(1); continue; } - CHECKWITHIN(curnode->childindex(1)); - } - } - else - { - if(dist2 < maxdist) - { - if(!curnode->isleaf(1)) wpcachestack.add(curnode + curnode->childindex(1)); - else CHECKWITHIN(curnode->childindex(1)); - } - curnode += curnode->childindex(0); - continue; - } - if(wpcachestack.empty()) break; - curnode = wpcachestack.pop(); - } - for(int i = lastwpcache; i < waypoints.length(); i++) { CHECKWITHIN(i); } - } - - void avoidset::avoidnear(void *owner, float above, const vec &pos, float limit) - { - if(ai::waypoints.empty()) return; - if(clearedwpcaches) buildwpcache(); - - float limit2 = limit*limit; - #define CHECKNEAR(index) do { \ - int n = (index); \ - if(n < ai::waypoints.length()) \ - { \ - const waypoint &w = ai::waypoints[n]; \ - if(w.o.squaredist(pos) < limit2) add(owner, above, n); \ - } \ - } while(0) - wpcache::node *curnode; - loop(which, NUMWPCACHES) if(wpcaches[which].firstwp >= 0) for(curnode = &wpcaches[which].nodes[0], wpcachestack.setsize(0);;) - { - int axis = curnode->axis(); - float dist1 = pos[axis] - curnode->split[0], dist2 = curnode->split[1] - pos[axis]; - if(dist1 >= limit) - { - if(dist2 < limit) - { - if(!curnode->isleaf(1)) { curnode += curnode->childindex(1); continue; } - CHECKNEAR(curnode->childindex(1)); - } - } - else if(curnode->isleaf(0)) - { - CHECKNEAR(curnode->childindex(0)); - if(dist2 < limit) - { - if(!curnode->isleaf(1)) { curnode += curnode->childindex(1); continue; } - CHECKNEAR(curnode->childindex(1)); - } - } - else - { - if(dist2 < limit) - { - if(!curnode->isleaf(1)) wpcachestack.add(curnode + curnode->childindex(1)); - else CHECKNEAR(curnode->childindex(1)); - } - curnode += curnode->childindex(0); - continue; - } - if(wpcachestack.empty()) break; - curnode = wpcachestack.pop(); - } - for(int i = lastwpcache; i < waypoints.length(); i++) { CHECKNEAR(i); } - } - - int avoidset::remap(gameent *d, int n, vec &pos, bool retry) - { - if(!obstacles.empty()) - { - int cur = 0; - loopv(obstacles) - { - obstacle &ob = obstacles[i]; - int next = cur + ob.numwaypoints; - if(ob.owner != d) - { - for(; cur < next; cur++) if(waypoints[cur] == n) - { - if(ob.above < 0) return retry ? n : -1; - vec above(pos.x, pos.y, ob.above); - if(above.z-d->o.z >= ai::JUMPMAX) - return retry ? n : -1; // too much scotty - int node = closestwaypoint(above, ai::SIGHTMIN, true, d); - if(ai::iswaypoint(node) && node != n) - { // try to reroute above their head? - if(!find(node, d)) - { - pos = ai::waypoints[node].o; - return node; - } - else return retry ? n : -1; - } - else - { - vec old = d->o; - d->o = vec(above).addz(d->eyeheight); - bool col = collide(d, vec(0, 0, 1)); - d->o = old; - if(!col) - { - pos = above; - return n; - } - else return retry ? n : -1; - } - } - } - cur = next; - } - } - return n; - } - - static inline float heapscore(waypoint *q) { return q->score(); } - - bool route(gameent *d, int node, int goal, vector &route, const avoidset &obstacles, int retries) - { - if(waypoints.empty() || !iswaypoint(node) || !iswaypoint(goal) || goal == node || !waypoints[node].links[0]) - return false; - - static ushort routeid = 1; - static vector queue; - - if(!routeid) - { - loopv(waypoints) waypoints[i].route = 0; - routeid = 1; - } - - if(d) - { - if(retries <= 1 && d->ai) loopi(ai::NUMPREVNODES) if(d->ai->prevnodes[i] != node && iswaypoint(d->ai->prevnodes[i])) - { - waypoints[d->ai->prevnodes[i]].route = routeid; - waypoints[d->ai->prevnodes[i]].curscore = -1; - waypoints[d->ai->prevnodes[i]].estscore = 0; - } - if(retries <= 0) - { - loopavoid(obstacles, d, - { - if(iswaypoint(wp) && wp != node && wp != goal && waypoints[node].find(wp) < 0 && waypoints[goal].find(wp) < 0) - { - waypoints[wp].route = routeid; - waypoints[wp].curscore = -1; - waypoints[wp].estscore = 0; - } - }); - } - } - - waypoints[node].route = routeid; - waypoints[node].curscore = waypoints[node].estscore = 0; - waypoints[node].prev = 0; - queue.setsize(0); - queue.add(&waypoints[node]); - route.setsize(0); - - int lowest = -1; - while(!queue.empty()) - { - waypoint &m = *queue.removeheap(); - float prevscore = m.curscore; - m.curscore = -1; - loopi(MAXWAYPOINTLINKS) - { - int link = m.links[i]; - if(!link) break; - if(iswaypoint(link) && (link == node || link == goal || waypoints[link].links[0])) - { - waypoint &n = waypoints[link]; - int weight = max(n.weight, 1); - float curscore = prevscore + n.o.dist(m.o)*weight; - if(n.route == routeid && curscore >= n.curscore) continue; - n.curscore = curscore; - n.prev = ushort(&m - &waypoints[0]); - if(n.route != routeid) - { - n.estscore = n.o.dist(waypoints[goal].o)*weight; - if(n.estscore <= WAYPOINTRADIUS*4 && (lowest < 0 || n.estscore <= waypoints[lowest].estscore)) - lowest = link; - n.route = routeid; - if(link == goal) goto foundgoal; - queue.addheap(&n); - } - else loopvj(queue) if(queue[j] == &n) { queue.upheap(j); break; } - } - } - } - foundgoal: - - routeid++; - - if(lowest >= 0) // otherwise nothing got there - { - for(waypoint *m = &waypoints[lowest]; m > &waypoints[0]; m = &waypoints[m->prev]) - route.add(m - &waypoints[0]); // just keep it stored backward - } - - return !route.empty(); - } - - VARF(dropwaypoints, 0, 0, 1, { player1->lastnode = -1; }); - - int addwaypoint(const vec &o, int weight = -1) - { - if(waypoints.length() > MAXWAYPOINTS) return -1; - int n = waypoints.length(); - waypoints.add(waypoint(o, weight >= 0 ? weight : getweight(o))); - invalidatewpcache(n); - return n; - } - - void linkwaypoint(waypoint &a, int n) - { - loopi(MAXWAYPOINTLINKS) - { - if(a.links[i] == n) return; - if(!a.links[i]) { a.links[i] = n; return; } - } - a.links[rnd(MAXWAYPOINTLINKS)] = n; - } - - string loadedwaypoints = ""; - - static inline bool shouldnavigate() - { - if(dropwaypoints) return true; - loopvrev(players) if(players[i]->aitype != AI_NONE) return true; - return false; - } - - static inline bool shoulddrop(gameent *d) - { - return !d->ai && (dropwaypoints || !loadedwaypoints[0]); - } - - void inferwaypoints(gameent *d, const vec &o, const vec &v, float mindist) - { - if(!shouldnavigate()) return; - if(shoulddrop(d)) - { - if(waypoints.empty()) seedwaypoints(); - int from = closestwaypoint(o, mindist, false), to = closestwaypoint(v, mindist, false); - if(!iswaypoint(from)) from = addwaypoint(o); - if(!iswaypoint(to)) to = addwaypoint(v); - if(d->lastnode != from && iswaypoint(d->lastnode) && iswaypoint(from)) - linkwaypoint(waypoints[d->lastnode], from); - if(iswaypoint(to)) - { - if(from != to && iswaypoint(from) && iswaypoint(to)) - linkwaypoint(waypoints[from], to); - d->lastnode = to; - } - } - else d->lastnode = closestwaypoint(v, WAYPOINTRADIUS*2, false, d); - } - - void navigate(gameent *d) - { - vec v(d->feetpos()); - if(d->state != CS_ALIVE) { d->lastnode = -1; return; } - bool dropping = shoulddrop(d); - int mat = lookupmaterial(v); - if((mat&MATF_CLIP) == MAT_CLIP || (mat&MATF_VOLUME) == MAT_LAVA || mat&MAT_DEATH) dropping = false; - float dist = dropping ? WAYPOINTRADIUS : (d->ai ? WAYPOINTRADIUS : SIGHTMIN); - int curnode = closestwaypoint(v, dist, false, d), prevnode = d->lastnode; - if(!iswaypoint(curnode) && dropping) - { - if(waypoints.empty()) seedwaypoints(); - curnode = addwaypoint(v); - } - if(iswaypoint(curnode)) - { - if(dropping && d->lastnode != curnode && iswaypoint(d->lastnode)) - { - linkwaypoint(waypoints[d->lastnode], curnode); - if(!d->timeinair) linkwaypoint(waypoints[curnode], d->lastnode); - } - d->lastnode = curnode; - if(d->ai && iswaypoint(prevnode) && d->lastnode != prevnode) d->ai->addprevnode(prevnode); - } - else if(!iswaypoint(d->lastnode) || waypoints[d->lastnode].o.squaredist(v) > SIGHTMIN*SIGHTMIN) - d->lastnode = closestwaypoint(v, SIGHTMAX, false, d); - } - - void navigate() - { - if(shouldnavigate()) loopv(players) ai::navigate(players[i]); - if(invalidatedwpcaches) clearwpcache(false); - } - - void clearwaypoints(bool full) - { - waypoints.setsize(0); - clearwpcache(); - if(full) - { - loadedwaypoints[0] = '\0'; - dropwaypoints = 0; - } - } - ICOMMAND(clearwaypoints, "", (), clearwaypoints()); - - void seedwaypoints() - { - if(waypoints.empty()) addwaypoint(vec(0, 0, 0)); - loopv(entities::ents) - { - extentity &e = *entities::ents[i]; - switch(e.type) - { - case PLAYERSTART: case TELEPORT: case JUMPPAD: case FLAG: - addwaypoint(e.o); - break; - default: - if(validitem(e.type)) addwaypoint(e.o); - break; - } - } - } - - void remapwaypoints() - { - vector remap; - int total = 0; - loopv(waypoints) remap.add(waypoints[i].links[1] == 0xFFFF ? 0 : total++); - total = 0; - loopvj(waypoints) - { - if(waypoints[j].links[1] == 0xFFFF) continue; - waypoint &w = waypoints[total]; - if(j != total) w = waypoints[j]; - int k = 0; - loopi(MAXWAYPOINTLINKS) - { - int link = w.links[i]; - if(!link) break; - if((w.links[k] = remap[link])) k++; - } - if(k < MAXWAYPOINTLINKS) w.links[k] = 0; - total++; - } - waypoints.setsize(total); - } - - bool cleanwaypoints() - { - int cleared = 0; - for(int i = 1; i < waypoints.length(); i++) - { - waypoint &w = waypoints[i]; - if(clipped(w.o)) - { - w.links[0] = 0; - w.links[1] = 0xFFFF; - cleared++; - } - } - if(cleared) - { - player1->lastnode = -1; - loopv(players) if(players[i]) players[i]->lastnode = -1; - remapwaypoints(); - clearwpcache(); - return true; - } - return false; - } - - bool getwaypointfile(const char *mname, char *wptname) - { - if(!mname || !*mname) mname = getclientmap(); - if(!*mname) return false; - - nformatstring(wptname, MAXSTRLEN, "media/map/%s.wpt", mname); - path(wptname); - return true; - } - - void loadwaypoints(bool force, const char *mname) - { - string wptname; - if(!getwaypointfile(mname, wptname)) return; - if(!force && (waypoints.length() || !strcmp(loadedwaypoints, wptname))) return; - - stream *f = opengzfile(wptname, "rb"); - if(!f) return; - char magic[4]; - if(f->read(magic, 4) < 4 || memcmp(magic, "OWPT", 4)) { delete f; return; } - - copystring(loadedwaypoints, wptname); - - waypoints.setsize(0); - waypoints.add(vec(0, 0, 0)); - ushort numwp = f->getlil(); - loopi(numwp) - { - if(f->end()) break; - vec o; - o.x = f->getlil(); - o.y = f->getlil(); - o.z = f->getlil(); - waypoint &w = waypoints.add(waypoint(o, getweight(o))); - int numlinks = f->getchar(), k = 0; - loopi(numlinks) - { - if((w.links[k] = f->getlil())) - { - if(++k >= MAXWAYPOINTLINKS) break; - } - } - } - - delete f; - conoutf("loaded %d waypoints from %s", numwp, wptname); - - if(!cleanwaypoints()) clearwpcache(); - } - ICOMMAND(loadwaypoints, "s", (char *mname), loadwaypoints(true, mname)); - - void savewaypoints(bool force, const char *mname) - { - if((!dropwaypoints && !force) || waypoints.empty()) return; - - string wptname; - if(!getwaypointfile(mname, wptname)) return; - - stream *f = opengzfile(wptname, "wb"); - if(!f) return; - f->write("OWPT", 4); - f->putlil(waypoints.length()-1); - for(int i = 1; i < waypoints.length(); i++) - { - waypoint &w = waypoints[i]; - f->putlil(w.o.x); - f->putlil(w.o.y); - f->putlil(w.o.z); - int numlinks = 0; - loopj(MAXWAYPOINTLINKS) { if(!w.links[j]) break; numlinks++; } - f->putchar(numlinks); - loopj(numlinks) f->putlil(w.links[j]); - } - - delete f; - conoutf("saved %d waypoints to %s", waypoints.length()-1, wptname); - } - - ICOMMAND(savewaypoints, "s", (char *mname), savewaypoints(true, mname)); - - void delselwaypoints() - { - if(noedit(true)) return; - vec o = vec(sel.o).sub(0.1f), s = vec(sel.s).mul(sel.grid).add(o).add(0.1f); - int cleared = 0; - for(int i = 1; i < waypoints.length(); i++) - { - waypoint &w = waypoints[i]; - if(w.o.x >= o.x && w.o.x <= s.x && w.o.y >= o.y && w.o.y <= s.y && w.o.z >= o.z && w.o.z <= s.z) - { - w.links[0] = 0; - w.links[1] = 0xFFFF; - cleared++; - } - } - if(cleared) - { - player1->lastnode = -1; - remapwaypoints(); - clearwpcache(); - } - } - COMMAND(delselwaypoints, ""); - - void movewaypoints(const vec &d) - { - if(noedit(true)) return; - int worldsize = getworldsize(); - if(d.x < -worldsize || d.x > worldsize || d.y < -worldsize || d.y > worldsize || d.z < -worldsize || d.z > worldsize) - { - clearwaypoints(); - return; - } - int cleared = 0; - for(int i = 1; i < waypoints.length(); i++) - { - waypoint &w = waypoints[i]; - w.o.add(d); - if(!insideworld(w.o)) { w.links[0] = 0; w.links[1] = 0xFFFF; cleared++; } - } - if(cleared) - { - player1->lastnode = -1; - remapwaypoints(); - } - clearwpcache(); - } - ICOMMAND(movewaypoints, "iii", (int *dx, int *dy, int *dz), movewaypoints(vec(*dx, *dy, *dz))); -} - diff --git a/src/game/weapon.cc b/src/game/weapon.cc index e0a2658..9fb1dd8 100644 --- a/src/game/weapon.cc +++ b/src/game/weapon.cc @@ -684,7 +684,7 @@ namespace game shoteffects(atk, from, to, d, true, 0, prevaction); - if(d==player1 || d->ai) + if(d==player1) { addmsg(N_SHOOT, "rci2i6iv", d, lastmillis-maptime, atk, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), @@ -693,7 +693,7 @@ namespace game } d->gunwait = attacks[atk].attackdelay; - if(attacks[atk].action != ACT_MELEE && d->ai) d->gunwait += int(d->gunwait*(((101-d->skill)+rnd(111-d->skill))/100.f)); + if(attacks[atk].action != ACT_MELEE) d->gunwait += int(d->gunwait*((101+rnd(111))/100.f)); d->totalshots += attacks[atk].damage*attacks[atk].rays; } @@ -771,14 +771,5 @@ namespace game if(player1->clientnum>=0 && player1->state==CS_ALIVE) shoot(player1, worldpos); // only shoot when connected to server updatebouncers(curtime); // need to do this after the player shoots so bouncers don't end up inside player's BB next frame } - - void avoidweapons(ai::avoidset &obstacles, float radius) - { - loopv(projs) - { - projectile &p = projs[i]; - obstacles.avoidnear(NULL, p.o.z + attacks[p.atk].exprad + 1, p.o, radius + attacks[p.atk].exprad); - } - } }; diff --git a/src/shared/igame.hh b/src/shared/igame.hh index 5b4fb8e..1d626d6 100644 --- a/src/shared/igame.hh +++ b/src/shared/igame.hh @@ -104,7 +104,6 @@ namespace server extern void localdisconnect(int n); extern void localconnect(int n); extern bool allowbroadcast(int n); - extern void recordpacket(int chan, void *data, int len); extern void parsepacket(int sender, int chan, packetbuf &p); extern void sendservmsg(const char *s); extern bool sendpackets(bool force = false);