forked from OctaForge/OctaCore
rip out most of gamecode
This commit is contained in:
parent
69ad67a65e
commit
b03c776b81
|
@ -21,12 +21,6 @@ defaultsoundpack = [
|
||||||
registersound // pain2
|
registersound // pain2
|
||||||
registersound "uphys/die1" 150
|
registersound "uphys/die1" 150
|
||||||
registersound "uphys/die2" 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 = [
|
loadsoundpack = [
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
exec "config/ui/lib.cfg" // UI library
|
exec "config/ui/lib.cfg" // UI library
|
||||||
exec "config/ui/style.cfg" // Styles
|
exec "config/ui/style.cfg" // Styles
|
||||||
exec "config/ui/scoreboard.cfg" // Scoreboard
|
|
||||||
exec "config/ui/edithud.cfg" // Edit HUD
|
exec "config/ui/edithud.cfg" // Edit HUD
|
||||||
exec "config/ui/fkey.cfg" // F# Key Menus
|
exec "config/ui/fkey.cfg" // F# Key Menus
|
||||||
exec "config/ui/serverbrowser.cfg" // Server Browser
|
exec "config/ui/serverbrowser.cfg" // Server Browser
|
||||||
|
@ -36,46 +35,6 @@ UImenu "main" [
|
||||||
uifill 0.2 0.02 [UIbar 1]
|
uifill 0.2 0.02 [UIbar 1]
|
||||||
UIbutton "hold2" [uitext "Quit" 0.65] 0.2 0.04 [quit]
|
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 //
|
// Master //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
menu_master = [if $isconnected [if $scoreboardmultiplayer [toggleui "master"]]]
|
menu_master = []
|
||||||
|
|
||||||
UImenu "master" [
|
UImenu "master" [
|
||||||
uifill 0 0 [
|
uifill 0 0 [
|
||||||
|
|
|
@ -57,7 +57,7 @@ newui "varicons" [
|
||||||
uialign 1 1
|
uialign 1 1
|
||||||
uispace 0.01 0.1 [
|
uispace 0.01 0.1 [
|
||||||
uivlist 0 [
|
uivlist 0 [
|
||||||
looplist i (concatword "allfaces entselsnap entediting fullbright showmat " (? $scoreboardmultiplayer "nompedit")) [
|
looplist i (concatword "allfaces entselsnap entediting fullbright showmat") [
|
||||||
uifont "default_outline" [
|
uifont "default_outline" [
|
||||||
uialign 1
|
uialign 1
|
||||||
if $$i [
|
if $$i [
|
||||||
|
|
|
@ -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]]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
|
@ -40,14 +40,11 @@ client_src = [
|
||||||
'../engine/water.cc',
|
'../engine/water.cc',
|
||||||
'../engine/world.cc',
|
'../engine/world.cc',
|
||||||
'../engine/worldio.cc',
|
'../engine/worldio.cc',
|
||||||
'../game/ai.cc',
|
|
||||||
'../game/client.cc',
|
'../game/client.cc',
|
||||||
'../game/entities.cc',
|
'../game/entities.cc',
|
||||||
'../game/game.cc',
|
'../game/game.cc',
|
||||||
'../game/render.cc',
|
'../game/render.cc',
|
||||||
'../game/scoreboard.cc',
|
|
||||||
'../game/server.cc',
|
'../game/server.cc',
|
||||||
'../game/waypoint.cc',
|
|
||||||
'../game/weapon.cc'
|
'../game/weapon.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);
|
else if(pl->inwater && !water) game::physicstrigger(pl, local, 0, 1, pl->inwater);
|
||||||
pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,6 @@ void sendpacket(int n, int chan, ENetPacket *packet, int exclude)
|
||||||
{
|
{
|
||||||
if(n<0)
|
if(n<0)
|
||||||
{
|
{
|
||||||
server::recordpacket(chan, packet->data, packet->dataLength);
|
|
||||||
loopv(clients) if(i!=exclude && server::allowbroadcast(i)) sendpacket(i, chan, packet);
|
loopv(clients) if(i!=exclude && server::allowbroadcast(i)) sendpacket(i, chan, packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
1441
src/game/ai.cc
1441
src/game/ai.cc
File diff suppressed because it is too large
Load diff
317
src/game/ai.hh
317
src/game/ai.hh
|
@ -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<waypoint> 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<int> &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<obstacle> obstacles;
|
|
||||||
vector<int> 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<int> &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<aistate> state;
|
|
||||||
vector<int> 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<int> &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<interest> &interests, bool all = false, bool force = false);
|
|
||||||
extern bool parseinterests(gameent *d, aistate &b, vector<interest> &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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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<teamscore> &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<teamscore> teams;
|
|
||||||
calcteams(teams);
|
|
||||||
vector<clientinfo *> 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<teamscore> 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;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -225,7 +225,6 @@ namespace entities
|
||||||
else d->vel = vec(0, 0, 0);
|
else d->vel = vec(0, 0, 0);
|
||||||
entinmap(d);
|
entinmap(d);
|
||||||
updatedynentcache(d);
|
updatedynentcache(d);
|
||||||
ai::inferwaypoints(d, ents[n]->o, ents[e]->o, 16.f);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +266,6 @@ namespace entities
|
||||||
d->lastpickup = e->type;
|
d->lastpickup = e->type;
|
||||||
d->lastpickupmillis = lastmillis;
|
d->lastpickupmillis = lastmillis;
|
||||||
jumppadeffects(d, n, true);
|
jumppadeffects(d, n, true);
|
||||||
if(d->ai) d->ai->becareful = true;
|
|
||||||
d->falling = vec(0, 0, 0);
|
d->falling = vec(0, 0, 0);
|
||||||
d->physstate = PHYS_FALL;
|
d->physstate = PHYS_FALL;
|
||||||
d->timeinair = 1;
|
d->timeinair = 1;
|
||||||
|
|
|
@ -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<teamscore> 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);
|
|
||||||
}
|
|
||||||
|
|
199
src/game/game.cc
199
src/game/game.cc
|
@ -39,15 +39,6 @@ namespace game
|
||||||
|
|
||||||
void follow(char *arg)
|
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");
|
COMMAND(follow, "s");
|
||||||
|
|
||||||
|
@ -96,25 +87,16 @@ namespace game
|
||||||
gameent *spawnstate(gameent *d) // reset player state not persistent accross spawns
|
gameent *spawnstate(gameent *d) // reset player state not persistent accross spawns
|
||||||
{
|
{
|
||||||
d->respawn();
|
d->respawn();
|
||||||
d->spawnstate(gamemode);
|
d->spawnstate(0);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
void respawnself()
|
void respawnself()
|
||||||
{
|
{
|
||||||
if(ispaused()) return;
|
if(ispaused()) return;
|
||||||
if(m_mp(gamemode))
|
spawnplayer(player1);
|
||||||
{
|
lasthit = 0;
|
||||||
int seq = (player1->lifesequence<<16)|((lastmillis/1000)&0xFFFF);
|
if(cmode) cmode->respawned(player1);
|
||||||
if(player1->respawned!=seq) { addmsg(N_TRYSPAWN, "rc", player1); player1->respawned = seq; }
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spawnplayer(player1);
|
|
||||||
showscores(false);
|
|
||||||
lasthit = 0;
|
|
||||||
if(cmode) cmode->respawned(player1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gameent *pointatplayer()
|
gameent *pointatplayer()
|
||||||
|
@ -194,7 +176,7 @@ namespace game
|
||||||
loopv(players)
|
loopv(players)
|
||||||
{
|
{
|
||||||
gameent *d = players[i];
|
gameent *d = players[i];
|
||||||
if(d == player1 || d->ai) continue;
|
if(d == player1) continue;
|
||||||
|
|
||||||
if(d->state==CS_DEAD && d->ragdoll) moveragdoll(d);
|
if(d->state==CS_DEAD && d->ragdoll) moveragdoll(d);
|
||||||
else if(!intermission)
|
else if(!intermission)
|
||||||
|
@ -225,10 +207,8 @@ namespace game
|
||||||
if(!curtime) { gets2c(); if(player1->clientnum>=0) c2sinfo(); return; }
|
if(!curtime) { gets2c(); if(player1->clientnum>=0) c2sinfo(); return; }
|
||||||
|
|
||||||
physicsframe();
|
physicsframe();
|
||||||
ai::navigate();
|
|
||||||
updateweapons(curtime);
|
updateweapons(curtime);
|
||||||
otherplayers(curtime);
|
otherplayers(curtime);
|
||||||
ai::update();
|
|
||||||
moveragdolls();
|
moveragdolls();
|
||||||
gets2c();
|
gets2c();
|
||||||
if(connected)
|
if(connected)
|
||||||
|
@ -354,15 +334,11 @@ namespace game
|
||||||
}
|
}
|
||||||
damageeffect(damage, d, d!=h);
|
damageeffect(damage, d, d!=h);
|
||||||
|
|
||||||
ai::damaged(d, actor);
|
|
||||||
|
|
||||||
if(d->health<=0) { if(local) killed(d, actor); }
|
if(d->health<=0) { if(local) killed(d, actor); }
|
||||||
else if(d==h) playsound(S_PAIN2);
|
else if(d==h) playsound(S_PAIN2);
|
||||||
else playsound(S_PAIN1, &d->o);
|
else playsound(S_PAIN1, &d->o);
|
||||||
}
|
}
|
||||||
|
|
||||||
VARP(deathscore, 0, 1, 1);
|
|
||||||
|
|
||||||
void deathstate(gameent *d, bool restore)
|
void deathstate(gameent *d, bool restore)
|
||||||
{
|
{
|
||||||
d->state = CS_DEAD;
|
d->state = CS_DEAD;
|
||||||
|
@ -374,7 +350,6 @@ namespace game
|
||||||
}
|
}
|
||||||
if(d==player1)
|
if(d==player1)
|
||||||
{
|
{
|
||||||
if(deathscore) showscores(true);
|
|
||||||
disablezoom();
|
disablezoom();
|
||||||
d->attacking = ACT_IDLE;
|
d->attacking = ACT_IDLE;
|
||||||
//d->pitch = 0;
|
//d->pitch = 0;
|
||||||
|
@ -431,7 +406,6 @@ namespace game
|
||||||
else conoutf(contype, "\f2%s fragged %s", aname, dname);
|
else conoutf(contype, "\f2%s fragged %s", aname, dname);
|
||||||
}
|
}
|
||||||
deathstate(d);
|
deathstate(d);
|
||||||
ai::killed(d, actor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void timeupdate(int secs)
|
void timeupdate(int secs)
|
||||||
|
@ -451,7 +425,6 @@ namespace game
|
||||||
int accuracy = (player1->totaldamage*100)/max(player1->totalshots, 1);
|
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);
|
conoutf(CON_GAMEINFO, "\f2player total damage dealt: %d, damage wasted: %d, accuracy(%%): %d", player1->totaldamage, player1->totalshots-player1->totaldamage, accuracy);
|
||||||
|
|
||||||
showscores(true);
|
|
||||||
disablezoom();
|
disablezoom();
|
||||||
|
|
||||||
execident("intermission");
|
execident("intermission");
|
||||||
|
@ -469,7 +442,7 @@ namespace game
|
||||||
|
|
||||||
gameent *newclient(int cn) // ensure valid entity
|
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);
|
neterr("clientnum", false);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -497,7 +470,6 @@ namespace game
|
||||||
void clientdisconnected(int cn, bool notify)
|
void clientdisconnected(int cn, bool notify)
|
||||||
{
|
{
|
||||||
if(!clients.inrange(cn)) return;
|
if(!clients.inrange(cn)) return;
|
||||||
unignore(cn);
|
|
||||||
gameent *d = clients[cn];
|
gameent *d = clients[cn];
|
||||||
if(d)
|
if(d)
|
||||||
{
|
{
|
||||||
|
@ -506,7 +478,6 @@ namespace game
|
||||||
removetrackedparticles(d);
|
removetrackedparticles(d);
|
||||||
removetrackeddynlights(d);
|
removetrackeddynlights(d);
|
||||||
if(cmode) cmode->removeplayer(d);
|
if(cmode) cmode->removeplayer(d);
|
||||||
removegroupedplayer(d);
|
|
||||||
players.removeobj(d);
|
players.removeobj(d);
|
||||||
DELETEP(clients[cn]);
|
DELETEP(clients[cn]);
|
||||||
cleardynentcache();
|
cleardynentcache();
|
||||||
|
@ -538,8 +509,6 @@ namespace game
|
||||||
clearbouncers();
|
clearbouncers();
|
||||||
clearragdolls();
|
clearragdolls();
|
||||||
|
|
||||||
clearteaminfo();
|
|
||||||
|
|
||||||
// reset perma-state
|
// reset perma-state
|
||||||
loopv(players) players[i]->startgame();
|
loopv(players) players[i]->startgame();
|
||||||
|
|
||||||
|
@ -555,14 +524,8 @@ namespace game
|
||||||
cmode->setup();
|
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();
|
syncplayer();
|
||||||
|
|
||||||
showscores(false);
|
|
||||||
disablezoom();
|
disablezoom();
|
||||||
lasthit = 0;
|
lasthit = 0;
|
||||||
|
|
||||||
|
@ -571,11 +534,7 @@ namespace game
|
||||||
|
|
||||||
void startmap(const char *name) // called just after a map load
|
void startmap(const char *name) // called just after a map load
|
||||||
{
|
{
|
||||||
ai::savewaypoints();
|
spawnplayer(player1);
|
||||||
ai::clearwaypoints(true);
|
|
||||||
|
|
||||||
if(!m_mp(gamemode)) spawnplayer(player1);
|
|
||||||
else findplayerspawn(player1, -1, m_teammode ? player1->team : 0);
|
|
||||||
entities::resetspawns();
|
entities::resetspawns();
|
||||||
copystring(clientmap, name ? name : "");
|
copystring(clientmap, name ? name : "");
|
||||||
|
|
||||||
|
@ -584,20 +543,20 @@ namespace game
|
||||||
|
|
||||||
const char *getmapinfo()
|
const char *getmapinfo()
|
||||||
{
|
{
|
||||||
return showmodeinfo && m_valid(gamemode) ? gamemodes[gamemode - STARTGAMEMODE].info : NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *getscreenshotinfo()
|
const char *getscreenshotinfo()
|
||||||
{
|
{
|
||||||
return server::modename(gamemode, NULL);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void physicstrigger(physent *d, bool local, int floorlevel, int waterlevel, int material)
|
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); }
|
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);
|
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); }
|
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 || ((gameent *)d)->ai) msgsound(S_LAND, 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)
|
void dynentcollide(physent *d, physent *o, const vec &dir)
|
||||||
|
@ -613,8 +572,6 @@ namespace game
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(d->type==ENT_PLAYER && ((gameent *)d)->ai)
|
|
||||||
addmsg(N_SOUND, "ci", d, n);
|
|
||||||
playsound(n, &d->o);
|
playsound(n, &d->o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -638,10 +595,10 @@ namespace game
|
||||||
const char *colorname(gameent *d, const char *name, const char * alt, const char *color)
|
const char *colorname(gameent *d, const char *name, const char * alt, const char *color)
|
||||||
{
|
{
|
||||||
if(!name) name = alt && d == player1 ? alt : d->name;
|
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 || 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 tempformatstring("\fs%s%s\fr", color, name);
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
|
@ -673,22 +630,6 @@ namespace game
|
||||||
teamsound(isteam(d->team, player1->team), n, loc);
|
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; }
|
bool needminimap() { return false; }
|
||||||
|
|
||||||
void drawicon(int icon, float x, float y, float sz)
|
void drawicon(int icon, float x, float y, float sz)
|
||||||
|
@ -719,53 +660,12 @@ namespace game
|
||||||
|
|
||||||
void drawhudicons(gameent *d)
|
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)
|
void gameplayhud(int w, int h)
|
||||||
{
|
{
|
||||||
pushhudscale(h/1800.0f);
|
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();
|
gameent *d = hudplayer();
|
||||||
if(d->state!=CS_EDITING)
|
if(d->state!=CS_EDITING)
|
||||||
{
|
{
|
||||||
|
@ -795,80 +695,11 @@ namespace game
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int selectcrosshair(vec &col)
|
int selectcrosshair(vec &)
|
||||||
{
|
{
|
||||||
gameent *d = hudplayer();
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mastermodecolor(int n, const char *unknown)
|
|
||||||
{
|
|
||||||
return (n>=MM_START && size_t(n-MM_START)<sizeof(mastermodecolors)/sizeof(mastermodecolors[0])) ? mastermodecolors[n-MM_START] : unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mastermodeicon(int n, const char *unknown)
|
|
||||||
{
|
|
||||||
return (n>=MM_START && size_t(n-MM_START)<sizeof(mastermodeicons)/sizeof(mastermodeicons[0])) ? mastermodeicons[n-MM_START] : unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICOMMAND(servinfomode, "i", (int *i), GETSERVINFOATTR(*i, 0, mode, intret(mode)));
|
|
||||||
ICOMMAND(servinfomodename, "i", (int *i),
|
|
||||||
GETSERVINFOATTR(*i, 0, mode,
|
|
||||||
{
|
|
||||||
const char *name = server::modeprettyname(mode, NULL);
|
|
||||||
if(name) result(name);
|
|
||||||
}));
|
|
||||||
ICOMMAND(servinfomastermode, "i", (int *i), GETSERVINFOATTR(*i, 2, mm, intret(mm)));
|
|
||||||
ICOMMAND(servinfomastermodename, "i", (int *i),
|
|
||||||
GETSERVINFOATTR(*i, 2, mm,
|
|
||||||
{
|
|
||||||
const char *name = server::mastermodename(mm, NULL);
|
|
||||||
if(name) stringret(newconcatstring(mastermodecolor(mm, ""), name));
|
|
||||||
}));
|
|
||||||
ICOMMAND(servinfotime, "ii", (int *i, int *raw),
|
|
||||||
GETSERVINFOATTR(*i, 1, secs,
|
|
||||||
{
|
|
||||||
secs = clamp(secs, 0, 59*60+59);
|
|
||||||
if(*raw) intret(secs);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int mins = secs/60;
|
|
||||||
secs %= 60;
|
|
||||||
result(tempformatstring("%d:%02d", mins, secs));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
ICOMMAND(servinfoicon, "i", (int *i),
|
|
||||||
GETSERVINFO(*i, si,
|
|
||||||
{
|
|
||||||
int mm = si->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.
|
// 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<char> &extras) {}
|
void writegamedata(vector<char> &extras) {}
|
||||||
void readgamedata(vector<char> &extras) {}
|
void readgamedata(vector<char> &extras) {}
|
||||||
|
|
|
@ -124,18 +124,18 @@ static struct gamemodeinfo
|
||||||
#define m_checknot(mode, flag) (m_valid(mode) && !(gamemodes[(mode) - STARTGAMEMODE].flags&(flag)))
|
#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_checkall(mode, flag) (m_valid(mode) && (gamemodes[(mode) - STARTGAMEMODE].flags&(flag)) == (flag))
|
||||||
|
|
||||||
#define m_teammode (m_check(gamemode, M_TEAM))
|
#define m_teammode false
|
||||||
#define m_overtime (m_check(gamemode, M_OVERTIME))
|
#define m_overtime false
|
||||||
#define isteam(a,b) (m_teammode && a==b)
|
#define isteam(a,b) false
|
||||||
#define m_rail (m_check(gamemode, M_RAIL))
|
#define m_rail false
|
||||||
#define m_pulse (m_check(gamemode, M_PULSE))
|
#define m_pulse false
|
||||||
|
|
||||||
#define m_demo (m_check(gamemode, M_DEMO))
|
#define m_demo false
|
||||||
#define m_edit (m_check(gamemode, M_EDIT))
|
#define m_edit true
|
||||||
#define m_lobby (m_check(gamemode, M_LOBBY))
|
#define m_lobby false
|
||||||
#define m_timed (m_checknot(gamemode, M_DEMO|M_EDIT|M_LOCAL))
|
#define m_timed false
|
||||||
#define m_botmode (m_checknot(gamemode, M_DEMO|M_LOCAL))
|
#define m_botmode false
|
||||||
#define m_mp(mode) (m_checknot(mode, M_LOCAL))
|
#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 };
|
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_ITEMSPAWN, S_TELEPORT, S_JUMPPAD,
|
||||||
S_MELEE, S_PULSE1, S_PULSE2, S_PULSEEXPLODE, S_RAIL1, S_RAIL2,
|
S_MELEE, S_PULSE1, S_PULSE2, S_PULSEEXPLODE, S_RAIL1, S_RAIL2,
|
||||||
S_WEAPLOAD, S_NOAMMO, S_HIT,
|
S_WEAPLOAD, S_NOAMMO, S_HIT,
|
||||||
S_PAIN1, S_PAIN2, S_DIE1, S_DIE2,
|
S_PAIN1, S_PAIN2, S_DIE1, S_DIE2
|
||||||
|
|
||||||
S_FLAGPICKUP,
|
|
||||||
S_FLAGDROP,
|
|
||||||
S_FLAGRETURN,
|
|
||||||
S_FLAGSCORE,
|
|
||||||
S_FLAGRESET,
|
|
||||||
S_FLAGFAIL
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// network messages codes, c2s, c2c, s2c
|
// network messages codes, c2s, c2c, s2c
|
||||||
|
@ -177,19 +170,13 @@ enum
|
||||||
N_TIMEUP, N_FORCEINTERMISSION,
|
N_TIMEUP, N_FORCEINTERMISSION,
|
||||||
N_SERVMSG, N_ITEMLIST, N_RESUME,
|
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_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_SPECTATOR, 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_SAYTEAM,
|
N_SAYTEAM,
|
||||||
N_CLIENT,
|
N_CLIENT,
|
||||||
N_AUTHTRY, N_AUTHKICK, N_AUTHCHAL, N_AUTHANS, N_REQAUTH,
|
|
||||||
N_PAUSEGAME, N_GAMESPEED,
|
N_PAUSEGAME, N_GAMESPEED,
|
||||||
N_ADDBOT, N_DELBOT, N_INITAI, N_FROMAI, N_BOTLIMIT, N_BOTBALANCE,
|
|
||||||
N_MAPCRC, N_CHECKMAPS,
|
N_MAPCRC, N_CHECKMAPS,
|
||||||
N_SWITCHNAME, N_SWITCHMODEL, N_SWITCHCOLOR, N_SWITCHTEAM,
|
N_SWITCHNAME, N_SWITCHMODEL, N_SWITCHCOLOR, N_SWITCHTEAM,
|
||||||
N_SERVCMD,
|
N_SERVCMD,
|
||||||
N_DEMOPACKET,
|
|
||||||
NUMMSG
|
NUMMSG
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -205,19 +192,13 @@ static const int msgsizes[] = // size inclusive message token, 0 f
|
||||||
N_TIMEUP, 2, N_FORCEINTERMISSION, 1,
|
N_TIMEUP, 2, N_FORCEINTERMISSION, 1,
|
||||||
N_SERVMSG, 0, N_ITEMLIST, 0, N_RESUME, 0,
|
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_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_SPECTATOR, 3, 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_SAYTEAM, 0,
|
N_SAYTEAM, 0,
|
||||||
N_CLIENT, 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_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_MAPCRC, 0, N_CHECKMAPS, 1,
|
||||||
N_SWITCHNAME, 0, N_SWITCHMODEL, 2, N_SWITCHCOLOR, 2, N_SWITCHTEAM, 2,
|
N_SWITCHNAME, 0, N_SWITCHMODEL, 2, N_SWITCHCOLOR, 2, N_SWITCHTEAM, 2,
|
||||||
N_SERVCMD, 0,
|
N_SERVCMD, 0,
|
||||||
N_DEMOPACKET, 0,
|
|
||||||
-1
|
-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 } }
|
{ "pulse rifle", "pulserifle", "worldgun/pulserifle", { -1, ATK_PULSE_SHOOT, ATK_PULSE_MELEE } }
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "ai.hh"
|
|
||||||
|
|
||||||
// inherited by gameent and server clients
|
// inherited by gameent and server clients
|
||||||
struct gamestate
|
struct gamestate
|
||||||
{
|
{
|
||||||
int health, maxhealth;
|
int health, maxhealth;
|
||||||
int gunselect, gunwait;
|
int gunselect, gunwait;
|
||||||
int ammo[NUMGUNS];
|
int ammo[NUMGUNS];
|
||||||
int aitype, skill;
|
|
||||||
|
|
||||||
gamestate() : maxhealth(1), aitype(AI_NONE), skill(0) {}
|
gamestate() : maxhealth(1) {}
|
||||||
|
|
||||||
bool canpickup(int type)
|
bool canpickup(int type)
|
||||||
{
|
{
|
||||||
|
@ -341,7 +319,6 @@ struct gamestate
|
||||||
static const char * const teamnames[1+MAXTEAMS] = { "", "azul", "rojo" };
|
static const char * const teamnames[1+MAXTEAMS] = { "", "azul", "rojo" };
|
||||||
static const char * const teamtextcode[1+MAXTEAMS] = { "\f0", "\f1", "\f3" };
|
static const char * const teamtextcode[1+MAXTEAMS] = { "\f0", "\f1", "\f3" };
|
||||||
static const int teamtextcolor[1+MAXTEAMS] = { 0x1EC850, 0x6496FF, 0xFF4B19 };
|
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 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; }
|
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)
|
#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
|
struct gameent : dynent, gamestate
|
||||||
{
|
{
|
||||||
int weight; // affects the effectiveness of hitpush
|
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 lifesequence; // sequence id for each respawn, used in damage test
|
||||||
int respawned, suicided;
|
int respawned, suicided;
|
||||||
int lastpain;
|
int lastpain;
|
||||||
|
@ -365,12 +342,11 @@ struct gameent : dynent, gamestate
|
||||||
|
|
||||||
string name, info;
|
string name, info;
|
||||||
int team, playermodel, playercolor;
|
int team, playermodel, playercolor;
|
||||||
ai::aiinfo *ai;
|
|
||||||
int ownernum, lastnode;
|
int ownernum, lastnode;
|
||||||
|
|
||||||
vec muzzle;
|
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;
|
name[0] = info[0] = 0;
|
||||||
respawn();
|
respawn();
|
||||||
|
@ -378,7 +354,6 @@ struct gameent : dynent, gamestate
|
||||||
~gameent()
|
~gameent()
|
||||||
{
|
{
|
||||||
freeeditinfo(edit);
|
freeeditinfo(edit);
|
||||||
if(ai) delete ai;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hitpush(int damage, const vec &dir, gameent *actor, int atk)
|
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
|
struct teaminfo
|
||||||
{
|
{
|
||||||
int frags;
|
int frags;
|
||||||
|
@ -467,8 +425,6 @@ namespace entities
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
extern int gamemode;
|
|
||||||
|
|
||||||
struct clientmode
|
struct clientmode
|
||||||
{
|
{
|
||||||
virtual ~clientmode() {}
|
virtual ~clientmode() {}
|
||||||
|
@ -486,19 +442,12 @@ namespace game
|
||||||
virtual void removeplayer(gameent *d) {}
|
virtual void removeplayer(gameent *d) {}
|
||||||
virtual void gameover() {}
|
virtual void gameover() {}
|
||||||
virtual bool hidefrags() { return false; }
|
virtual bool hidefrags() { return false; }
|
||||||
virtual int getteamscore(int team) { return 0; }
|
|
||||||
virtual void getteamscores(vector<teamscore> &scores) {}
|
|
||||||
virtual void aifind(gameent *d, ai::aistate &b, vector<ai::interest> &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 clientmode *cmode;
|
||||||
extern void setclientmode();
|
extern void setclientmode();
|
||||||
|
|
||||||
// game
|
// game
|
||||||
extern int nextmode;
|
|
||||||
extern string clientmap;
|
extern string clientmap;
|
||||||
extern bool intermission;
|
extern bool intermission;
|
||||||
extern int maptime, maprealtime, maplimit;
|
extern int maptime, maprealtime, maplimit;
|
||||||
|
@ -542,16 +491,12 @@ namespace game
|
||||||
extern vector<uchar> messages;
|
extern vector<uchar> messages;
|
||||||
|
|
||||||
extern int parseplayer(const char *arg);
|
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 bool addmsg(int type, const char *fmt = NULL, ...);
|
||||||
extern void switchname(const char *name);
|
extern void switchname(const char *name);
|
||||||
extern void switchteam(const char *name);
|
extern void switchteam(const char *name);
|
||||||
extern void switchplayermodel(int playermodel);
|
extern void switchplayermodel(int playermodel);
|
||||||
extern void switchplayercolor(int playercolor);
|
extern void switchplayercolor(int playercolor);
|
||||||
extern void sendmapinfo();
|
extern void sendmapinfo();
|
||||||
extern void stopdemo();
|
|
||||||
extern void changemap(const char *name, int mode);
|
extern void changemap(const char *name, int mode);
|
||||||
extern void c2sinfo(bool force = false);
|
extern void c2sinfo(bool force = false);
|
||||||
extern void sendposition(gameent *d, bool reliable = false);
|
extern void sendposition(gameent *d, bool reliable = false);
|
||||||
|
@ -580,15 +525,6 @@ namespace game
|
||||||
extern void updateweapons(int curtime);
|
extern void updateweapons(int curtime);
|
||||||
extern void gunselect(int gun, gameent *d);
|
extern void gunselect(int gun, gameent *d);
|
||||||
extern void weaponswitch(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<gameent *> &best);
|
|
||||||
extern void getbestteams(vector<int> &best);
|
|
||||||
extern void clearteaminfo();
|
|
||||||
extern void setteaminfo(int team, int frags);
|
|
||||||
extern void removegroupedplayer(gameent *d);
|
|
||||||
|
|
||||||
// render
|
// render
|
||||||
struct playermodelinfo
|
struct playermodelinfo
|
||||||
|
|
|
@ -21,7 +21,6 @@ namespace game
|
||||||
gameent *r = new gameent(*d);
|
gameent *r = new gameent(*d);
|
||||||
r->lastupdate = ragdollfade && lastmillis > d->lastpain + max(ragdollmillis - ragdollfade, 0) ? lastmillis - max(ragdollmillis - ragdollfade, 0) : d->lastpain;
|
r->lastupdate = ragdollfade && lastmillis > d->lastpain + max(ragdollmillis - ragdollfade, 0) ? lastmillis - max(ragdollmillis - ragdollfade, 0) : d->lastpain;
|
||||||
r->edit = NULL;
|
r->edit = NULL;
|
||||||
r->ai = NULL;
|
|
||||||
if(d==player1) r->playermodel = playermodel;
|
if(d==player1) r->playermodel = playermodel;
|
||||||
ragdolls.add(r);
|
ragdolls.add(r);
|
||||||
d->ragdoll = NULL;
|
d->ragdoll = NULL;
|
||||||
|
@ -318,14 +317,10 @@ namespace game
|
||||||
|
|
||||||
void rendergame()
|
void rendergame()
|
||||||
{
|
{
|
||||||
ai::render();
|
|
||||||
|
|
||||||
if(intermission)
|
if(intermission)
|
||||||
{
|
{
|
||||||
bestteams.shrink(0);
|
bestteams.shrink(0);
|
||||||
bestplayers.shrink(0);
|
bestplayers.shrink(0);
|
||||||
if(m_teammode) getbestteams(bestteams);
|
|
||||||
else getbestplayers(bestplayers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool third = isthirdperson();
|
bool third = isthirdperson();
|
||||||
|
|
|
@ -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<gameent *> &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<int> &best)
|
|
||||||
{
|
|
||||||
if(cmode && cmode->hidefrags())
|
|
||||||
{
|
|
||||||
vector<teamscore> 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<gameent *> 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<gameent *> &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); }
|
|
||||||
}
|
|
||||||
|
|
1652
src/game/server.cc
1652
src/game/server.cc
File diff suppressed because it is too large
Load diff
|
@ -1,806 +0,0 @@
|
||||||
#include "game.hh"
|
|
||||||
|
|
||||||
extern selinfo sel;
|
|
||||||
|
|
||||||
namespace ai
|
|
||||||
{
|
|
||||||
using namespace game;
|
|
||||||
|
|
||||||
vector<waypoint> 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<node> 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<int> 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<<NUMWPCACHES)-1, numinvalidatewpcaches = 0, lastwpcache = 0;
|
|
||||||
|
|
||||||
static inline void invalidatewpcache(int wp)
|
|
||||||
{
|
|
||||||
if(++numinvalidatewpcaches >= 1000) { numinvalidatewpcaches = 0; invalidatedwpcaches = (1<<NUMWPCACHES)-1; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
loopi(WPCACHE_DYNAMIC) if(wp >= wpcaches[i].firstwp && wp <= wpcaches[i].lastwp) { invalidatedwpcaches |= 1<<i; return; }
|
|
||||||
invalidatedwpcaches |= 1<<WPCACHE_DYNAMIC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearwpcache(bool full = true)
|
|
||||||
{
|
|
||||||
loopi(NUMWPCACHES) if(full || invalidatedwpcaches&(1<<i)) { wpcaches[i].clear(); clearedwpcaches |= 1<<i; }
|
|
||||||
if(full || invalidatedwpcaches == (1<<NUMWPCACHES)-1)
|
|
||||||
{
|
|
||||||
numinvalidatewpcaches = 0;
|
|
||||||
lastwpcache = 0;
|
|
||||||
}
|
|
||||||
invalidatedwpcaches = 0;
|
|
||||||
}
|
|
||||||
ICOMMAND(clearwpcache, "", (), clearwpcache());
|
|
||||||
|
|
||||||
avoidset wpavoid;
|
|
||||||
|
|
||||||
void buildwpcache()
|
|
||||||
{
|
|
||||||
loopi(NUMWPCACHES) if(wpcaches[i].firstwp < 0)
|
|
||||||
wpcaches[i].build(i > 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<wpcache::node *> 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<int> &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<int> &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<waypoint *> 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<ushort> 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<ushort>();
|
|
||||||
loopi(numwp)
|
|
||||||
{
|
|
||||||
if(f->end()) break;
|
|
||||||
vec o;
|
|
||||||
o.x = f->getlil<float>();
|
|
||||||
o.y = f->getlil<float>();
|
|
||||||
o.z = f->getlil<float>();
|
|
||||||
waypoint &w = waypoints.add(waypoint(o, getweight(o)));
|
|
||||||
int numlinks = f->getchar(), k = 0;
|
|
||||||
loopi(numlinks)
|
|
||||||
{
|
|
||||||
if((w.links[k] = f->getlil<ushort>()))
|
|
||||||
{
|
|
||||||
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<ushort>(waypoints.length()-1);
|
|
||||||
for(int i = 1; i < waypoints.length(); i++)
|
|
||||||
{
|
|
||||||
waypoint &w = waypoints[i];
|
|
||||||
f->putlil<float>(w.o.x);
|
|
||||||
f->putlil<float>(w.o.y);
|
|
||||||
f->putlil<float>(w.o.z);
|
|
||||||
int numlinks = 0;
|
|
||||||
loopj(MAXWAYPOINTLINKS) { if(!w.links[j]) break; numlinks++; }
|
|
||||||
f->putchar(numlinks);
|
|
||||||
loopj(numlinks) f->putlil<ushort>(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)));
|
|
||||||
}
|
|
||||||
|
|
|
@ -684,7 +684,7 @@ namespace game
|
||||||
|
|
||||||
shoteffects(atk, from, to, d, true, 0, prevaction);
|
shoteffects(atk, from, to, d, true, 0, prevaction);
|
||||||
|
|
||||||
if(d==player1 || d->ai)
|
if(d==player1)
|
||||||
{
|
{
|
||||||
addmsg(N_SHOOT, "rci2i6iv", d, lastmillis-maptime, atk,
|
addmsg(N_SHOOT, "rci2i6iv", d, lastmillis-maptime, atk,
|
||||||
(int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF),
|
(int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF),
|
||||||
|
@ -693,7 +693,7 @@ namespace game
|
||||||
}
|
}
|
||||||
|
|
||||||
d->gunwait = attacks[atk].attackdelay;
|
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;
|
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
|
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
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,6 @@ namespace server
|
||||||
extern void localdisconnect(int n);
|
extern void localdisconnect(int n);
|
||||||
extern void localconnect(int n);
|
extern void localconnect(int n);
|
||||||
extern bool allowbroadcast(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 parsepacket(int sender, int chan, packetbuf &p);
|
||||||
extern void sendservmsg(const char *s);
|
extern void sendservmsg(const char *s);
|
||||||
extern bool sendpackets(bool force = false);
|
extern bool sendpackets(bool force = false);
|
||||||
|
|
Loading…
Reference in a new issue