]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mirio/balance
authorMario <mario@smbclan.net>
Sat, 31 Dec 2016 00:54:39 +0000 (10:54 +1000)
committerMario <mario@smbclan.net>
Sat, 31 Dec 2016 00:54:39 +0000 (10:54 +1000)
1359 files changed:
.gitlab-ci.yml
CMakeLists.txt
_hud_common.cfg
_hud_descriptions.cfg
bal-wep-mario.cfg [new file with mode: 0644]
bal-wep-nexuiz25.cfg
bal-wep-overkill.cfg
bal-wep-samual.cfg
bal-wep-xdf.cfg
bal-wep-xonotic.cfg
bal-wep-xpm.cfg
balance-mario.cfg [new file with mode: 0644]
balance-nexuiz25.cfg
balance-overkill.cfg
balance-samual.cfg
balance-xdf.cfg
balance-xonotic.cfg
balance-xpm.cfg
binds-xonotic.cfg
bots.txt
defaultXonotic.cfg
gamemodes.cfg
gfx/hud/default/flag_stalemate.tga [new file with mode: 0644]
gfx/hud/luma/flag_blue_lost.tga
gfx/hud/luma/flag_neutral_lost.tga
gfx/hud/luma/flag_pink_lost.tga
gfx/hud/luma/flag_red_lost.tga
gfx/hud/luma/flag_stalemate.tga [new file with mode: 0644]
gfx/hud/luma/flag_yellow_lost.tga
gfx/hud/luma/notify_blue_lost.tga
gfx/hud/luma/notify_neutral_lost.tga
gfx/hud/luma/notify_pink_lost.tga
gfx/hud/luma/notify_red_lost.tga
gfx/hud/luma/notify_yellow_lost.tga
gfx/menu/luma/icon_mod_jeff.tga [new file with mode: 0644]
gfx/menu/luminos/icon_mod_jeff.tga [new file with mode: 0644]
gfx/menu/wickedx/icon_mod_jeff.tga [new file with mode: 0644]
gfx/menu/xaw/icon_mod_jeff.tga [new file with mode: 0644]
hud_luma.cfg
hud_luminos.cfg
hud_luminos_minimal.cfg
hud_luminos_minimal_xhair.cfg
hud_luminos_old.cfg
hud_nexuiz.cfg
minigames.cfg
minigames/bulldozer/storage_level1.txt
minigames/bulldozer/storage_level10.txt
minigames/bulldozer/storage_level11.txt
minigames/bulldozer/storage_level12.txt
minigames/bulldozer/storage_level13.txt
minigames/bulldozer/storage_level14.txt
minigames/bulldozer/storage_level15.txt
minigames/bulldozer/storage_level2.txt
minigames/bulldozer/storage_level3.txt
minigames/bulldozer/storage_level4.txt
minigames/bulldozer/storage_level5.txt
minigames/bulldozer/storage_level6.txt
minigames/bulldozer/storage_level7.txt
minigames/bulldozer/storage_level8.txt
minigames/bulldozer/storage_level9.txt
models/items/a_rockets.md3
models/player/gakarmored.iqm [deleted file]
models/player/gakarmored.iqm.framegroups [deleted file]
models/player/gakarmored.iqm_0.skin [deleted file]
models/player/gakarmored.iqm_0.sounds [deleted file]
models/player/gakarmored.iqm_0.tga [deleted file]
models/player/gakarmored.iqm_0.txt [deleted file]
models/player/gakarmored.iqm_1.skin [deleted file]
models/player/gakarmored_lod1.iqm [deleted file]
models/player/gakarmored_lod1.iqm.framegroups [deleted file]
models/player/gakarmored_lod1.iqm_0.skin [deleted file]
models/player/gakarmored_lod1.iqm_1.skin [deleted file]
models/player/gakarmored_lod2.iqm [deleted file]
models/player/gakarmored_lod2.iqm.framegroups [deleted file]
models/player/gakarmored_lod2.iqm_0.skin [deleted file]
models/player/gakarmored_lod2.iqm_1.skin [deleted file]
models/player/ignishalfmasked.iqm [deleted file]
models/player/ignishalfmasked.iqm.framegroups [deleted file]
models/player/ignishalfmasked.iqm_0.skin [deleted file]
models/player/ignishalfmasked.iqm_0.sounds [deleted file]
models/player/ignishalfmasked.iqm_0.tga [deleted file]
models/player/ignishalfmasked.iqm_0.txt [deleted file]
models/player/ignishalfmasked.iqm_1.skin [deleted file]
models/player/ignishalfmasked_lod1.iqm [deleted file]
models/player/ignishalfmasked_lod1.iqm.framegroups [deleted file]
models/player/ignishalfmasked_lod1.iqm_0.skin [deleted file]
models/player/ignishalfmasked_lod1.iqm_1.skin [deleted file]
models/player/ignishalfmasked_lod2.iqm [deleted file]
models/player/ignishalfmasked_lod2.iqm.framegroups [deleted file]
models/player/ignishalfmasked_lod2.iqm_0.skin [deleted file]
models/player/ignishalfmasked_lod2.iqm_1.skin [deleted file]
models/player/megaerebus.iqm_0.txt
models/player/pyria.iqm_0.skin
models/player/pyria_lod1.iqm_0.skin
models/player/pyria_lod2.iqm_0.skin
models/player/seraphinamasked.iqm_0.skin
models/player/seraphinamasked_lod1.iqm_0.skin
models/player/seraphinamasked_lod2.iqm_0.skin
models/weapons/g_fireball.md3
models/weapons/g_ok_hmg_luma.iqm [new file with mode: 0644]
models/weapons/g_ok_hmg_luma.iqm_0.skin [new file with mode: 0644]
models/weapons/g_ok_hmg_luma.tga [new file with mode: 0644]
models/weapons/g_ok_hmg_simple.iqm [new file with mode: 0644]
models/weapons/g_ok_hmg_simple.iqm_0.skin [new file with mode: 0644]
models/weapons/g_ok_hmg_simple.tga [new file with mode: 0644]
models/weapons/g_ok_rl_luma.iqm [new file with mode: 0644]
models/weapons/g_ok_rl_luma.iqm_0.skin [new file with mode: 0644]
models/weapons/g_ok_rl_luma.tga [new file with mode: 0644]
models/weapons/g_ok_rl_simple.iqm [new file with mode: 0644]
models/weapons/g_ok_rl_simple.iqm_0.skin [new file with mode: 0644]
models/weapons/g_ok_rl_simple.tga [new file with mode: 0644]
models/weapons/h_fireball.iqm
models/weapons/v_fireball.md3
monsters.cfg
mutators.cfg
notifications.cfg
physics.cfg
physicsCPMA.cfg
physicsFruit.cfg
physicsHavoc.cfg
physicsLeeStricklin-ModdedFruit.cfg
physicsLeeStricklin.cfg
physicsLeeStricklinOld.cfg
physicsLzd.cfg
physicsNexuiz10.cfg
physicsNexuiz11.cfg
physicsNexuiz151.cfg
physicsNexuiz151b.cfg
physicsNexuiz16rc1.cfg
physicsNexuiz20.cfg
physicsNexuiz25.cfg
physicsNexuiz26.cfg
physicsNoQWBunny-nexbased.cfg
physicsQ.cfg
physicsQ2.cfg
physicsQ2a.cfg
physicsQ3.cfg
physicsQBF.cfg
physicsQBFplus.cfg
physicsSamual.cfg
physicsWarsow.cfg
physicsWarsowClassicBunny.cfg
physicsWarsowDev.cfg
physicsX.cfg
physicsX010.cfg
physicsX07.cfg
physicsXDF.cfg
physicsXDFLight.cfg
qcsrc/.editorconfig [new file with mode: 0644]
qcsrc/Doxyfile
qcsrc/Makefile
qcsrc/client/_all.inc [new file with mode: 0644]
qcsrc/client/_all.qh
qcsrc/client/_mod.inc
qcsrc/client/_mod.qh
qcsrc/client/announcer.qc
qcsrc/client/autocvars.qh
qcsrc/client/commands/_mod.inc
qcsrc/client/commands/_mod.qh
qcsrc/client/commands/all.qc [deleted file]
qcsrc/client/commands/all.qh [deleted file]
qcsrc/client/commands/cl_cmd.qc
qcsrc/client/commands/cl_cmd.qh
qcsrc/client/csqc_constants.qh
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/defs.qh
qcsrc/client/hud/_mod.inc
qcsrc/client/hud/_mod.qh
qcsrc/client/hud/all.inc [deleted file]
qcsrc/client/hud/all.qh [deleted file]
qcsrc/client/hud/hud.qc
qcsrc/client/hud/hud.qh
qcsrc/client/hud/hud_config.qc
qcsrc/client/hud/hud_config.qh
qcsrc/client/hud/panel.qc [new file with mode: 0644]
qcsrc/client/hud/panel/_mod.inc
qcsrc/client/hud/panel/_mod.qh
qcsrc/client/hud/panel/ammo.qc
qcsrc/client/hud/panel/ammo.qh
qcsrc/client/hud/panel/centerprint.qc
qcsrc/client/hud/panel/centerprint.qh
qcsrc/client/hud/panel/chat.qc
qcsrc/client/hud/panel/engineinfo.qc
qcsrc/client/hud/panel/healtharmor.qc
qcsrc/client/hud/panel/infomessages.qc
qcsrc/client/hud/panel/minigame.qc
qcsrc/client/hud/panel/modicons.qc
qcsrc/client/hud/panel/notify.qc
qcsrc/client/hud/panel/physics.qc
qcsrc/client/hud/panel/powerups.qc
qcsrc/client/hud/panel/powerups.qh
qcsrc/client/hud/panel/pressedkeys.qc
qcsrc/client/hud/panel/quickmenu.qc
qcsrc/client/hud/panel/racetimer.qc
qcsrc/client/hud/panel/radar.qc
qcsrc/client/hud/panel/score.qc
qcsrc/client/hud/panel/scoreboard.qc [new file with mode: 0644]
qcsrc/client/hud/panel/scoreboard.qh [new file with mode: 0644]
qcsrc/client/hud/panel/timer.qc
qcsrc/client/hud/panel/vote.qc
qcsrc/client/hud/panel/weapons.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/mapvoting.qc
qcsrc/client/mapvoting.qh
qcsrc/client/miscfunctions.qc
qcsrc/client/miscfunctions.qh
qcsrc/client/mutators/_mod.inc
qcsrc/client/mutators/_mod.qh
qcsrc/client/mutators/events.qc [new file with mode: 0644]
qcsrc/client/mutators/events.qh
qcsrc/client/player_skeleton.qc
qcsrc/client/progs.inc
qcsrc/client/scoreboard.qc [deleted file]
qcsrc/client/scoreboard.qh [deleted file]
qcsrc/client/shownames.qc
qcsrc/client/teamradar.qc
qcsrc/client/teamradar.qh
qcsrc/client/view.qc
qcsrc/client/wall.qc
qcsrc/client/weapons/projectile.qc
qcsrc/client/weapons/projectile.qh
qcsrc/common/_all.inc
qcsrc/common/anim.qh
qcsrc/common/animdecide.qc
qcsrc/common/animdecide.qh
qcsrc/common/campaign_common.qh
qcsrc/common/campaign_file.qc
qcsrc/common/campaign_file.qh [new file with mode: 0644]
qcsrc/common/campaign_setup.qc
qcsrc/common/campaign_setup.qh [new file with mode: 0644]
qcsrc/common/command/_mod.inc
qcsrc/common/command/_mod.qh
qcsrc/common/command/all.qc [deleted file]
qcsrc/common/command/all.qh [deleted file]
qcsrc/common/command/command.qh
qcsrc/common/command/generic.qc
qcsrc/common/command/generic.qh
qcsrc/common/command/markup.qc
qcsrc/common/command/markup.qh
qcsrc/common/command/reg.qc [new file with mode: 0644]
qcsrc/common/command/reg.qh [new file with mode: 0644]
qcsrc/common/command/rpn.qc
qcsrc/common/command/rpn.qh
qcsrc/common/constants.qh
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/deathtypes/all.qh
qcsrc/common/debug.qh
qcsrc/common/effects/_mod.inc
qcsrc/common/effects/_mod.qh
qcsrc/common/effects/all.qc
qcsrc/common/effects/all.qh
qcsrc/common/effects/effect.qh
qcsrc/common/effects/effectinfo.qc
qcsrc/common/effects/effectinfo.qh [new file with mode: 0644]
qcsrc/common/effects/qc/all.qh
qcsrc/common/effects/qc/casings.qc
qcsrc/common/effects/qc/casings.qh [new file with mode: 0644]
qcsrc/common/effects/qc/damageeffects.qc
qcsrc/common/effects/qc/damageeffects.qh [new file with mode: 0644]
qcsrc/common/effects/qc/gibs.qc
qcsrc/common/effects/qc/globalsound.qc
qcsrc/common/effects/qc/globalsound.qh
qcsrc/common/effects/qc/modeleffects.qc
qcsrc/common/effects/qc/rubble.qh
qcsrc/common/ent_cs.qc
qcsrc/common/ent_cs.qh
qcsrc/common/gamemodes/_mod.inc
qcsrc/common/gamemodes/_mod.qh
qcsrc/common/gamemodes/all.inc [deleted file]
qcsrc/common/gamemodes/all.qc [deleted file]
qcsrc/common/gamemodes/all.qh [deleted file]
qcsrc/common/gamemodes/gamemode/_mod.inc
qcsrc/common/gamemodes/gamemode/_mod.qh
qcsrc/common/gamemodes/gamemode/nexball/module.inc [deleted file]
qcsrc/common/gamemodes/gamemode/nexball/nexball.qc
qcsrc/common/gamemodes/gamemode/nexball/nexball.qh
qcsrc/common/gamemodes/gamemode/nexball/weapon.qc
qcsrc/common/gamemodes/gamemode/nexball/weapon.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc
qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh
qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc
qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh
qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc
qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh
qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/generator.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/generator.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/module.inc [deleted file]
qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc
qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc
qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh
qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh [new file with mode: 0644]
qcsrc/common/impulses/all.qh
qcsrc/common/items/_mod.inc
qcsrc/common/items/_mod.qh
qcsrc/common/items/all.qc
qcsrc/common/items/all.qh
qcsrc/common/items/inventory.qh
qcsrc/common/items/item.qh
qcsrc/common/items/item/ammo.qc
qcsrc/common/items/item/ammo.qh
qcsrc/common/items/item/armor.qc
qcsrc/common/items/item/armor.qh
qcsrc/common/items/item/health.qc
qcsrc/common/items/item/health.qh
qcsrc/common/items/item/jetpack.qc
qcsrc/common/items/item/jetpack.qh [new file with mode: 0644]
qcsrc/common/items/item/pickup.qh
qcsrc/common/items/item/powerup.qc
qcsrc/common/items/item/powerup.qh
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/minigames/_mod.inc
qcsrc/common/minigames/_mod.qh
qcsrc/common/minigames/cl_minigames.qc
qcsrc/common/minigames/cl_minigames.qh
qcsrc/common/minigames/cl_minigames_hud.qc
qcsrc/common/minigames/cl_minigames_hud.qh
qcsrc/common/minigames/minigame/bd.qc
qcsrc/common/minigames/minigame/bd.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/c4.qc
qcsrc/common/minigames/minigame/c4.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/nmm.qc
qcsrc/common/minigames/minigame/nmm.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/pong.qc
qcsrc/common/minigames/minigame/pong.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/pp.qc
qcsrc/common/minigames/minigame/pp.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/ps.qc
qcsrc/common/minigames/minigame/ps.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/snake.qc
qcsrc/common/minigames/minigame/snake.qh [new file with mode: 0644]
qcsrc/common/minigames/minigame/ttt.qc
qcsrc/common/minigames/minigame/ttt.qh [new file with mode: 0644]
qcsrc/common/minigames/minigames.qh
qcsrc/common/minigames/sv_minigames.qc
qcsrc/common/minigames/sv_minigames.qh
qcsrc/common/models/all.qh
qcsrc/common/models/model.qh
qcsrc/common/monsters/_mod.inc
qcsrc/common/monsters/_mod.qh
qcsrc/common/monsters/all.qc
qcsrc/common/monsters/all.qh
qcsrc/common/monsters/monster.qh
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/monster/mage.qh [new file with mode: 0644]
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/shambler.qh [new file with mode: 0644]
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/monster/spider.qh [new file with mode: 0644]
qcsrc/common/monsters/monster/wyvern.qc
qcsrc/common/monsters/monster/wyvern.qh [new file with mode: 0644]
qcsrc/common/monsters/monster/zombie.qc
qcsrc/common/monsters/monster/zombie.qh [new file with mode: 0644]
qcsrc/common/monsters/spawn.qc [deleted file]
qcsrc/common/monsters/spawn.qh [deleted file]
qcsrc/common/monsters/spawner.qc [new file with mode: 0644]
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/monsters/sv_monsters.qh
qcsrc/common/monsters/sv_spawn.qc [new file with mode: 0644]
qcsrc/common/monsters/sv_spawn.qh [new file with mode: 0644]
qcsrc/common/mutators/_mod.inc
qcsrc/common/mutators/_mod.qh
qcsrc/common/mutators/all.inc [deleted file]
qcsrc/common/mutators/all.qc [deleted file]
qcsrc/common/mutators/all.qh [deleted file]
qcsrc/common/mutators/base.qh
qcsrc/common/mutators/events.qh
qcsrc/common/mutators/mutator/_mod.inc
qcsrc/common/mutators/mutator/_mod.qh
qcsrc/common/mutators/mutator/bloodloss/_mod.inc
qcsrc/common/mutators/mutator/bloodloss/_mod.qh
qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc [deleted file]
qcsrc/common/mutators/mutator/bloodloss/module.inc [deleted file]
qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/breakablehook/_mod.inc
qcsrc/common/mutators/mutator/breakablehook/_mod.qh
qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc [deleted file]
qcsrc/common/mutators/mutator/breakablehook/module.inc [deleted file]
qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/_mod.inc
qcsrc/common/mutators/mutator/buffs/_mod.qh
qcsrc/common/mutators/mutator/buffs/all.inc
qcsrc/common/mutators/mutator/buffs/all.qc [deleted file]
qcsrc/common/mutators/mutator/buffs/all.qh [deleted file]
qcsrc/common/mutators/mutator/buffs/buffs.qc
qcsrc/common/mutators/mutator/buffs/buffs.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/cl_buffs.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/cl_buffs.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/module.inc [deleted file]
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc
qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/bugrigs/module.inc [deleted file]
qcsrc/common/mutators/mutator/campcheck/_mod.inc
qcsrc/common/mutators/mutator/campcheck/_mod.qh
qcsrc/common/mutators/mutator/campcheck/campcheck.qc
qcsrc/common/mutators/mutator/campcheck/campcheck.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/campcheck/module.inc [deleted file]
qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/cloaked/_mod.inc
qcsrc/common/mutators/mutator/cloaked/_mod.qh
qcsrc/common/mutators/mutator/cloaked/cloaked.qc [deleted file]
qcsrc/common/mutators/mutator/cloaked/module.inc [deleted file]
qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/damagetext/damagetext.qc
qcsrc/common/mutators/mutator/damagetext/damagetext.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/damagetext/module.inc [deleted file]
qcsrc/common/mutators/mutator/dodging/_mod.inc
qcsrc/common/mutators/mutator/dodging/_mod.qh
qcsrc/common/mutators/mutator/dodging/dodging.qc [deleted file]
qcsrc/common/mutators/mutator/dodging/module.inc [deleted file]
qcsrc/common/mutators/mutator/dodging/sv_dodging.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/dodging/sv_dodging.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/doublejump/doublejump.qc
qcsrc/common/mutators/mutator/doublejump/doublejump.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/doublejump/module.inc [deleted file]
qcsrc/common/mutators/mutator/globalforces/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/globalforces/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/hook/_mod.inc
qcsrc/common/mutators/mutator/hook/_mod.qh
qcsrc/common/mutators/mutator/hook/hook.qc [deleted file]
qcsrc/common/mutators/mutator/hook/module.inc [deleted file]
qcsrc/common/mutators/mutator/hook/sv_hook.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/hook/sv_hook.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/instagib/_mod.inc
qcsrc/common/mutators/mutator/instagib/_mod.qh
qcsrc/common/mutators/mutator/instagib/instagib.qc [deleted file]
qcsrc/common/mutators/mutator/instagib/items.qc
qcsrc/common/mutators/mutator/instagib/items.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/instagib/module.inc [deleted file]
qcsrc/common/mutators/mutator/instagib/sv_instagib.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/instagib/sv_instagib.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/invincibleproj/_mod.inc
qcsrc/common/mutators/mutator/invincibleproj/_mod.qh
qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc [deleted file]
qcsrc/common/mutators/mutator/invincibleproj/module.inc [deleted file]
qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/itemstime.qc [deleted file]
qcsrc/common/mutators/mutator/itemstime/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/itemstime/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/itemstime/itemstime.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/itemstime/itemstime.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/melee_only/_mod.inc
qcsrc/common/mutators/mutator/melee_only/_mod.qh
qcsrc/common/mutators/mutator/melee_only/melee_only.qc [deleted file]
qcsrc/common/mutators/mutator/melee_only/module.inc [deleted file]
qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/midair/_mod.inc
qcsrc/common/mutators/mutator/midair/_mod.qh
qcsrc/common/mutators/mutator/midair/midair.qc [deleted file]
qcsrc/common/mutators/mutator/midair/module.inc [deleted file]
qcsrc/common/mutators/mutator/midair/sv_midair.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/midair/sv_midair.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/multijump/module.inc [deleted file]
qcsrc/common/mutators/mutator/multijump/multijump.qc
qcsrc/common/mutators/mutator/multijump/multijump.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/nades/module.inc [deleted file]
qcsrc/common/mutators/mutator/nades/nades.inc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/nades/nades.qh
qcsrc/common/mutators/mutator/nades/net.qc
qcsrc/common/mutators/mutator/nades/net.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/new_toys/_mod.inc
qcsrc/common/mutators/mutator/new_toys/_mod.qh
qcsrc/common/mutators/mutator/new_toys/module.inc [deleted file]
qcsrc/common/mutators/mutator/new_toys/new_toys.qc [deleted file]
qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/nix/_mod.inc
qcsrc/common/mutators/mutator/nix/_mod.qh
qcsrc/common/mutators/mutator/nix/module.inc [deleted file]
qcsrc/common/mutators/mutator/nix/nix.qc [deleted file]
qcsrc/common/mutators/mutator/nix/sv_nix.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/nix/sv_nix.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/_mod.inc
qcsrc/common/mutators/mutator/overkill/_mod.qh
qcsrc/common/mutators/mutator/overkill/cl_overkill.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/cl_overkill.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/hmg.qc
qcsrc/common/mutators/mutator/overkill/hmg.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/module.inc [deleted file]
qcsrc/common/mutators/mutator/overkill/overkill.qc
qcsrc/common/mutators/mutator/overkill/overkill.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/rpc.qc
qcsrc/common/mutators/mutator/overkill/rpc.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/sv_overkill.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/physical_items/_mod.inc
qcsrc/common/mutators/mutator/physical_items/_mod.qh
qcsrc/common/mutators/mutator/physical_items/module.inc [deleted file]
qcsrc/common/mutators/mutator/physical_items/physical_items.qc [deleted file]
qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/pinata/_mod.inc
qcsrc/common/mutators/mutator/pinata/_mod.qh
qcsrc/common/mutators/mutator/pinata/module.inc [deleted file]
qcsrc/common/mutators/mutator/pinata/pinata.qc [deleted file]
qcsrc/common/mutators/mutator/pinata/sv_pinata.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/pinata/sv_pinata.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_gravity/_mod.inc
qcsrc/common/mutators/mutator/random_gravity/_mod.qh
qcsrc/common/mutators/mutator/random_gravity/module.inc [deleted file]
qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc [deleted file]
qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketflying/_mod.inc
qcsrc/common/mutators/mutator/rocketflying/_mod.qh
qcsrc/common/mutators/mutator/rocketflying/module.inc [deleted file]
qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc [deleted file]
qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketminsta/_mod.inc
qcsrc/common/mutators/mutator/rocketminsta/_mod.qh
qcsrc/common/mutators/mutator/rocketminsta/module.inc [deleted file]
qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc [deleted file]
qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/running_guns/_mod.inc
qcsrc/common/mutators/mutator/running_guns/_mod.qh
qcsrc/common/mutators/mutator/running_guns/module.inc [deleted file]
qcsrc/common/mutators/mutator/running_guns/running_guns.qc [deleted file]
qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/sandbox/_mod.inc
qcsrc/common/mutators/mutator/sandbox/_mod.qh
qcsrc/common/mutators/mutator/sandbox/module.inc [deleted file]
qcsrc/common/mutators/mutator/sandbox/sandbox.qc [deleted file]
qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc
qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh
qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc [deleted file]
qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc [deleted file]
qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/superspec/_mod.inc
qcsrc/common/mutators/mutator/superspec/_mod.qh
qcsrc/common/mutators/mutator/superspec/module.inc [deleted file]
qcsrc/common/mutators/mutator/superspec/superspec.qc [deleted file]
qcsrc/common/mutators/mutator/superspec/sv_superspec.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/superspec/sv_superspec.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/touchexplode/_mod.inc
qcsrc/common/mutators/mutator/touchexplode/_mod.qh
qcsrc/common/mutators/mutator/touchexplode/module.inc [deleted file]
qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc [deleted file]
qcsrc/common/mutators/mutator/vampire/_mod.inc
qcsrc/common/mutators/mutator/vampire/_mod.qh
qcsrc/common/mutators/mutator/vampire/module.inc [deleted file]
qcsrc/common/mutators/mutator/vampire/sv_vampire.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampire/sv_vampire.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampire/vampire.qc [deleted file]
qcsrc/common/mutators/mutator/vampirehook/_mod.inc
qcsrc/common/mutators/mutator/vampirehook/_mod.qh
qcsrc/common/mutators/mutator/vampirehook/module.inc [deleted file]
qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc [deleted file]
qcsrc/common/mutators/mutator/walljump/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/walljump/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/walljump/walljump.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/walljump/walljump.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/waypoints/all.inc
qcsrc/common/mutators/mutator/waypoints/all.qh
qcsrc/common/mutators/mutator/waypoints/module.inc [deleted file]
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh
qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc
qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh
qcsrc/common/mutators/mutator/weaponarena_random/module.inc [deleted file]
qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc [deleted file]
qcsrc/common/net_linked.qh [new file with mode: 0644]
qcsrc/common/net_notice.qc
qcsrc/common/net_notice.qh
qcsrc/common/notifications/all.inc
qcsrc/common/notifications/all.qc
qcsrc/common/notifications/all.qh
qcsrc/common/physics/_mod.inc
qcsrc/common/physics/_mod.qh
qcsrc/common/physics/movelib.qh
qcsrc/common/physics/movetypes/_mod.inc
qcsrc/common/physics/movetypes/_mod.qh
qcsrc/common/physics/movetypes/all.inc
qcsrc/common/physics/movetypes/follow.qc
qcsrc/common/physics/movetypes/follow.qh [new file with mode: 0644]
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/physics/movetypes/movetypes.qh
qcsrc/common/physics/movetypes/push.qc [deleted file]
qcsrc/common/physics/movetypes/step.qc
qcsrc/common/physics/movetypes/step.qh [new file with mode: 0644]
qcsrc/common/physics/movetypes/toss.qc
qcsrc/common/physics/movetypes/toss.qh [new file with mode: 0644]
qcsrc/common/physics/movetypes/walk.qc
qcsrc/common/physics/movetypes/walk.qh [new file with mode: 0644]
qcsrc/common/physics/player.qc
qcsrc/common/physics/player.qh
qcsrc/common/playerstats.qc
qcsrc/common/playerstats.qh
qcsrc/common/sounds/all.inc
qcsrc/common/sounds/all.qc
qcsrc/common/sounds/all.qh
qcsrc/common/sounds/sound.qh
qcsrc/common/state.qc
qcsrc/common/state.qh
qcsrc/common/stats.qh
qcsrc/common/t_items.qc
qcsrc/common/t_items.qh
qcsrc/common/teams.qh
qcsrc/common/triggers/_mod.inc
qcsrc/common/triggers/_mod.qh
qcsrc/common/triggers/func/bobbing.qc
qcsrc/common/triggers/func/bobbing.qh [new file with mode: 0644]
qcsrc/common/triggers/func/breakable.qc
qcsrc/common/triggers/func/breakable.qh
qcsrc/common/triggers/func/button.qc
qcsrc/common/triggers/func/button.qh [new file with mode: 0644]
qcsrc/common/triggers/func/conveyor.qc
qcsrc/common/triggers/func/conveyor.qh [new file with mode: 0644]
qcsrc/common/triggers/func/door.qc
qcsrc/common/triggers/func/door_rotating.qc
qcsrc/common/triggers/func/door_rotating.qh [new file with mode: 0644]
qcsrc/common/triggers/func/door_secret.qc
qcsrc/common/triggers/func/door_secret.qh [new file with mode: 0644]
qcsrc/common/triggers/func/fourier.qc
qcsrc/common/triggers/func/fourier.qh [new file with mode: 0644]
qcsrc/common/triggers/func/include.qh
qcsrc/common/triggers/func/ladder.qc
qcsrc/common/triggers/func/pendulum.qc
qcsrc/common/triggers/func/pendulum.qh [new file with mode: 0644]
qcsrc/common/triggers/func/plat.qc
qcsrc/common/triggers/func/plat.qh [new file with mode: 0644]
qcsrc/common/triggers/func/pointparticles.qc
qcsrc/common/triggers/func/pointparticles.qh [new file with mode: 0644]
qcsrc/common/triggers/func/rainsnow.qc
qcsrc/common/triggers/func/rainsnow.qh [new file with mode: 0644]
qcsrc/common/triggers/func/rotating.qc
qcsrc/common/triggers/func/rotating.qh [new file with mode: 0644]
qcsrc/common/triggers/func/stardust.qc
qcsrc/common/triggers/func/stardust.qh [new file with mode: 0644]
qcsrc/common/triggers/func/train.qc
qcsrc/common/triggers/func/vectormamamam.qc
qcsrc/common/triggers/func/vectormamamam.qh [new file with mode: 0644]
qcsrc/common/triggers/include.qh
qcsrc/common/triggers/misc/corner.qc
qcsrc/common/triggers/misc/corner.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/follow.qc
qcsrc/common/triggers/misc/follow.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/include.qc
qcsrc/common/triggers/misc/include.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/laser.qc
qcsrc/common/triggers/misc/laser.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/teleport_dest.qc
qcsrc/common/triggers/misc/teleport_dest.qh [new file with mode: 0644]
qcsrc/common/triggers/platforms.qc
qcsrc/common/triggers/platforms.qh
qcsrc/common/triggers/subs.qc
qcsrc/common/triggers/subs.qh
qcsrc/common/triggers/target/changelevel.qc
qcsrc/common/triggers/target/changelevel.qh [new file with mode: 0644]
qcsrc/common/triggers/target/include.qh
qcsrc/common/triggers/target/location.qc
qcsrc/common/triggers/target/location.qh [new file with mode: 0644]
qcsrc/common/triggers/target/music.qc
qcsrc/common/triggers/target/music.qh
qcsrc/common/triggers/target/spawn.qc
qcsrc/common/triggers/target/spawn.qh [new file with mode: 0644]
qcsrc/common/triggers/target/speaker.qc
qcsrc/common/triggers/target/speaker.qh [new file with mode: 0644]
qcsrc/common/triggers/target/voicescript.qc
qcsrc/common/triggers/target/voicescript.qh [new file with mode: 0644]
qcsrc/common/triggers/teleporters.qc
qcsrc/common/triggers/teleporters.qh
qcsrc/common/triggers/trigger/counter.qc
qcsrc/common/triggers/trigger/counter.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/delay.qc
qcsrc/common/triggers/trigger/delay.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/disablerelay.qc
qcsrc/common/triggers/trigger/disablerelay.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/flipflop.qc
qcsrc/common/triggers/trigger/flipflop.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/gamestart.qc
qcsrc/common/triggers/trigger/gamestart.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/gravity.qc
qcsrc/common/triggers/trigger/gravity.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/heal.qc
qcsrc/common/triggers/trigger/heal.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/hurt.qc
qcsrc/common/triggers/trigger/hurt.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/impulse.qc
qcsrc/common/triggers/trigger/impulse.qh
qcsrc/common/triggers/trigger/include.qh
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/triggers/trigger/jumppads.qh
qcsrc/common/triggers/trigger/keylock.qc
qcsrc/common/triggers/trigger/magicear.qc
qcsrc/common/triggers/trigger/magicear.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/monoflop.qc
qcsrc/common/triggers/trigger/monoflop.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/multi.qc
qcsrc/common/triggers/trigger/multivibrator.qc
qcsrc/common/triggers/trigger/multivibrator.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay.qc
qcsrc/common/triggers/trigger/relay.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_activators.qc
qcsrc/common/triggers/trigger/relay_activators.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_if.qc
qcsrc/common/triggers/trigger/relay_if.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_teamcheck.qc
qcsrc/common/triggers/trigger/relay_teamcheck.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/secret.qc
qcsrc/common/triggers/trigger/secret.qh
qcsrc/common/triggers/trigger/swamp.qc
qcsrc/common/triggers/trigger/swamp.qh
qcsrc/common/triggers/trigger/teleport.qc
qcsrc/common/triggers/trigger/teleport.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/viewloc.qc
qcsrc/common/triggers/trigger/viewloc.qh
qcsrc/common/triggers/triggers.qc
qcsrc/common/triggers/triggers.qh
qcsrc/common/turrets/_all.inc [new file with mode: 0644]
qcsrc/common/turrets/_all.qh [new file with mode: 0644]
qcsrc/common/turrets/_mod.inc
qcsrc/common/turrets/_mod.qh
qcsrc/common/turrets/all.qh
qcsrc/common/turrets/checkpoint.qc
qcsrc/common/turrets/checkpoint.qh [new file with mode: 0644]
qcsrc/common/turrets/cl_turrets.qc
qcsrc/common/turrets/cl_turrets.qh [new file with mode: 0644]
qcsrc/common/turrets/config.qc
qcsrc/common/turrets/config.qh
qcsrc/common/turrets/sv_turrets.qc
qcsrc/common/turrets/sv_turrets.qh
qcsrc/common/turrets/targettrigger.qc
qcsrc/common/turrets/targettrigger.qh [new file with mode: 0644]
qcsrc/common/turrets/turret.qh
qcsrc/common/turrets/turret/ewheel.qc
qcsrc/common/turrets/turret/ewheel.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/ewheel_weapon.qc
qcsrc/common/turrets/turret/flac.qc
qcsrc/common/turrets/turret/flac.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/flac_weapon.qc
qcsrc/common/turrets/turret/fusionreactor.qc
qcsrc/common/turrets/turret/fusionreactor.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/hellion.qc
qcsrc/common/turrets/turret/hellion.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/hellion_weapon.qc
qcsrc/common/turrets/turret/hk.qc
qcsrc/common/turrets/turret/hk.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/hk_weapon.qc
qcsrc/common/turrets/turret/machinegun.qc
qcsrc/common/turrets/turret/machinegun.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/machinegun_weapon.qc
qcsrc/common/turrets/turret/mlrs.qc
qcsrc/common/turrets/turret/mlrs.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/mlrs_weapon.qc
qcsrc/common/turrets/turret/phaser.qc
qcsrc/common/turrets/turret/phaser.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/phaser_weapon.qc
qcsrc/common/turrets/turret/plasma.qc
qcsrc/common/turrets/turret/plasma.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/plasma_dual.qc
qcsrc/common/turrets/turret/plasma_dual.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/plasma_weapon.qc
qcsrc/common/turrets/turret/tesla.qc
qcsrc/common/turrets/turret/tesla.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/tesla_weapon.qc
qcsrc/common/turrets/turret/walker.qc
qcsrc/common/turrets/turret/walker.qh [new file with mode: 0644]
qcsrc/common/turrets/turret/walker_weapon.qc
qcsrc/common/turrets/turrets.qc [new file with mode: 0644]
qcsrc/common/turrets/turrets.qh [new file with mode: 0644]
qcsrc/common/turrets/util.qc
qcsrc/common/turrets/util.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/common/vehicles/_all.inc [new file with mode: 0644]
qcsrc/common/vehicles/_all.qh [new file with mode: 0644]
qcsrc/common/vehicles/_mod.inc
qcsrc/common/vehicles/_mod.qh
qcsrc/common/vehicles/all.qc
qcsrc/common/vehicles/all.qh
qcsrc/common/vehicles/cl_vehicles.qc
qcsrc/common/vehicles/cl_vehicles.qh
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/sv_vehicles.qh
qcsrc/common/vehicles/vehicle.qh
qcsrc/common/vehicles/vehicle/bumblebee.qc
qcsrc/common/vehicles/vehicle/bumblebee.qh
qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc
qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh
qcsrc/common/vehicles/vehicle/racer.qc
qcsrc/common/vehicles/vehicle/racer.qh [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/racer_weapon.qc
qcsrc/common/vehicles/vehicle/racer_weapon.qh
qcsrc/common/vehicles/vehicle/raptor.qc
qcsrc/common/vehicles/vehicle/raptor.qh
qcsrc/common/vehicles/vehicle/raptor_weapons.qc
qcsrc/common/vehicles/vehicle/raptor_weapons.qh
qcsrc/common/vehicles/vehicle/spiderbot.qc
qcsrc/common/vehicles/vehicle/spiderbot.qh [new file with mode: 0644]
qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc
qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh
qcsrc/common/vehicles/vehicles.qc [new file with mode: 0644]
qcsrc/common/vehicles/vehicles.qh [new file with mode: 0644]
qcsrc/common/viewloc.qc
qcsrc/common/viewloc.qh
qcsrc/common/weapons/_all.inc [new file with mode: 0644]
qcsrc/common/weapons/_all.qh [new file with mode: 0644]
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/calculations.qc
qcsrc/common/weapons/calculations.qh
qcsrc/common/weapons/config.qc
qcsrc/common/weapons/config.qh
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/arc.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/blaster.qc
qcsrc/common/weapons/weapon/blaster.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/crylink.qc
qcsrc/common/weapons/weapon/crylink.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/devastator.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/electro.qc
qcsrc/common/weapons/weapon/electro.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/fireball.qc
qcsrc/common/weapons/weapon/fireball.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/hagar.qc
qcsrc/common/weapons/weapon/hagar.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/hlac.qc
qcsrc/common/weapons/weapon/hlac.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/hook.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/machinegun.qc
qcsrc/common/weapons/weapon/machinegun.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/minelayer.qc
qcsrc/common/weapons/weapon/minelayer.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/mortar.qc
qcsrc/common/weapons/weapon/mortar.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/porto.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/common/weapons/weapon/rifle.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/seeker.qc
qcsrc/common/weapons/weapon/seeker.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shockwave.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/shotgun.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/tuba.qc
qcsrc/common/weapons/weapon/tuba.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/weapons/weapon/vaporizer.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/common/weapons/weapon/vortex.qh [new file with mode: 0644]
qcsrc/dpdefs/csprogsdefs.qh
qcsrc/dpdefs/doc.md
qcsrc/dpdefs/dpextensions.qh
qcsrc/dpdefs/keycodes.qh
qcsrc/dpdefs/menudefs.qh
qcsrc/dpdefs/post.qh
qcsrc/dpdefs/pre.qh
qcsrc/dpdefs/progsdefs.qh
qcsrc/dpdefs/upstream/dpextensions.qc
qcsrc/ecs/README.md [new file with mode: 0644]
qcsrc/ecs/_mod.inc [new file with mode: 0644]
qcsrc/ecs/_mod.qh [new file with mode: 0644]
qcsrc/ecs/components/_mod.inc [new file with mode: 0644]
qcsrc/ecs/components/_mod.qh [new file with mode: 0644]
qcsrc/ecs/components/input.qc [new file with mode: 0644]
qcsrc/ecs/components/input.qh [new file with mode: 0644]
qcsrc/ecs/components/physics.qc [new file with mode: 0644]
qcsrc/ecs/components/physics.qh [new file with mode: 0644]
qcsrc/ecs/events/_mod.inc [new file with mode: 0644]
qcsrc/ecs/events/_mod.qh [new file with mode: 0644]
qcsrc/ecs/events/physics.qc [new file with mode: 0644]
qcsrc/ecs/events/physics.qh [new file with mode: 0644]
qcsrc/ecs/lib.qh [new file with mode: 0644]
qcsrc/ecs/main.qc [new file with mode: 0644]
qcsrc/ecs/main.qh [new file with mode: 0644]
qcsrc/ecs/systems/_mod.inc [new file with mode: 0644]
qcsrc/ecs/systems/_mod.qh [new file with mode: 0644]
qcsrc/ecs/systems/cl_physics.qc [new file with mode: 0644]
qcsrc/ecs/systems/input.qc [new file with mode: 0644]
qcsrc/ecs/systems/input.qh [new file with mode: 0644]
qcsrc/ecs/systems/physics.qc [new file with mode: 0644]
qcsrc/ecs/systems/physics.qh [new file with mode: 0644]
qcsrc/ecs/systems/sv_physics.qc [new file with mode: 0644]
qcsrc/lib/_all.inc
qcsrc/lib/angle.qh [new file with mode: 0644]
qcsrc/lib/arraylist.qh
qcsrc/lib/counting.qh
qcsrc/lib/csqcmodel/_mod.inc
qcsrc/lib/csqcmodel/_mod.qh
qcsrc/lib/csqcmodel/cl_model.qc
qcsrc/lib/csqcmodel/cl_model.qh
qcsrc/lib/csqcmodel/cl_player.qc
qcsrc/lib/csqcmodel/cl_player.qh
qcsrc/lib/csqcmodel/common.qh
qcsrc/lib/csqcmodel/interpolate.qh
qcsrc/lib/csqcmodel/model.qc [new file with mode: 0644]
qcsrc/lib/csqcmodel/model.qh [new file with mode: 0644]
qcsrc/lib/csqcmodel/player.qc [new file with mode: 0644]
qcsrc/lib/csqcmodel/player.qh [new file with mode: 0644]
qcsrc/lib/csqcmodel/settings.qh
qcsrc/lib/csqcmodel/sv_model.qc
qcsrc/lib/csqcmodel/sv_model.qh
qcsrc/lib/defer.qh
qcsrc/lib/draw.qh
qcsrc/lib/i18n.qh
qcsrc/lib/intrusivelist.qh [new file with mode: 0644]
qcsrc/lib/iter.qh
qcsrc/lib/json.qh [new file with mode: 0644]
qcsrc/lib/lazy.qh
qcsrc/lib/linkedlist.qh
qcsrc/lib/log.qh
qcsrc/lib/macro.qh
qcsrc/lib/map.qh
qcsrc/lib/math.qh
qcsrc/lib/matrix/_mod.inc
qcsrc/lib/matrix/_mod.qh
qcsrc/lib/matrix/command.qc
qcsrc/lib/matrix/matrix.qc
qcsrc/lib/misc.qh
qcsrc/lib/net.qh
qcsrc/lib/oo.qh
qcsrc/lib/p99.qh
qcsrc/lib/random.qc
qcsrc/lib/random.qh
qcsrc/lib/registry.qh
qcsrc/lib/replicate.qh
qcsrc/lib/self.qh
qcsrc/lib/spawnfunc.qh
qcsrc/lib/static.qh
qcsrc/lib/stats.qh
qcsrc/lib/string.qh
qcsrc/lib/test.qh
qcsrc/lib/unsafe.qh
qcsrc/lib/urllib.qc
qcsrc/lib/vector.qh
qcsrc/lib/warpzone/anglestransform.qh
qcsrc/lib/warpzone/client.qc
qcsrc/lib/warpzone/client.qh
qcsrc/lib/warpzone/common.qc
qcsrc/lib/warpzone/common.qh
qcsrc/lib/warpzone/mathlib.qc
qcsrc/lib/warpzone/mathlib.qh
qcsrc/lib/warpzone/server.qc
qcsrc/lib/warpzone/server.qh
qcsrc/lib/warpzone/util_server.qc
qcsrc/lib/warpzone/util_server.qh
qcsrc/menu/_all.inc [new file with mode: 0644]
qcsrc/menu/anim/animation.qh
qcsrc/menu/anim/animhost.qc
qcsrc/menu/anim/animhost.qh
qcsrc/menu/anim/easing.qh
qcsrc/menu/anim/keyframe.qh
qcsrc/menu/auto-super.pl [deleted file]
qcsrc/menu/command/_mod.inc
qcsrc/menu/command/_mod.qh
qcsrc/menu/command/all.qc [deleted file]
qcsrc/menu/command/menu_cmd.qc
qcsrc/menu/draw.qc
qcsrc/menu/item.qh
qcsrc/menu/item/borderimage.qh
qcsrc/menu/item/button.qh
qcsrc/menu/item/checkbox.qh
qcsrc/menu/item/container.qh
qcsrc/menu/item/dialog.qc
qcsrc/menu/item/dialog.qh
qcsrc/menu/item/image.qc
qcsrc/menu/item/image.qh
qcsrc/menu/item/inputbox.qh
qcsrc/menu/item/inputcontainer.qc
qcsrc/menu/item/inputcontainer.qh
qcsrc/menu/item/label.qc
qcsrc/menu/item/label.qh
qcsrc/menu/item/listbox.qc
qcsrc/menu/item/listbox.qh
qcsrc/menu/item/modalcontroller.qh
qcsrc/menu/item/nexposee.qh
qcsrc/menu/item/radiobutton.qh
qcsrc/menu/item/slider.qh
qcsrc/menu/item/tab.qh
qcsrc/menu/item/textslider.qh
qcsrc/menu/matrix.qc
qcsrc/menu/matrix.qh [new file with mode: 0644]
qcsrc/menu/menu.qc
qcsrc/menu/progs.inc
qcsrc/menu/skin.qh
qcsrc/menu/xonotic/_mod.inc
qcsrc/menu/xonotic/_mod.qh
qcsrc/menu/xonotic/bigbutton.qh
qcsrc/menu/xonotic/bigcommandbutton.qh
qcsrc/menu/xonotic/button.qh
qcsrc/menu/xonotic/campaign.qh
qcsrc/menu/xonotic/charmap.qh
qcsrc/menu/xonotic/checkbox.qc
qcsrc/menu/xonotic/checkbox.qh
qcsrc/menu/xonotic/checkbox_slider_invalid.qh
qcsrc/menu/xonotic/checkbox_string.qh
qcsrc/menu/xonotic/colorbutton.qh
qcsrc/menu/xonotic/colorpicker.qc
qcsrc/menu/xonotic/colorpicker.qh
qcsrc/menu/xonotic/colorpicker_string.qh
qcsrc/menu/xonotic/commandbutton.qh
qcsrc/menu/xonotic/credits.qc
qcsrc/menu/xonotic/credits.qh
qcsrc/menu/xonotic/crosshairpicker.qh
qcsrc/menu/xonotic/crosshairpreview.qh
qcsrc/menu/xonotic/cvarlist.qh
qcsrc/menu/xonotic/datasource.qh
qcsrc/menu/xonotic/demolist.qh
qcsrc/menu/xonotic/dialog.qh
qcsrc/menu/xonotic/dialog_credits.qh
qcsrc/menu/xonotic/dialog_firstrun.qh
qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh
qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh
qcsrc/menu/xonotic/dialog_hudpanel_chat.qh
qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh
qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh
qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh
qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc
qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh
qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh
qcsrc/menu/xonotic/dialog_hudpanel_notification.qh
qcsrc/menu/xonotic/dialog_hudpanel_physics.qh
qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh
qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh
qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh
qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh
qcsrc/menu/xonotic/dialog_hudpanel_radar.qh
qcsrc/menu/xonotic/dialog_hudpanel_score.qh
qcsrc/menu/xonotic/dialog_hudpanel_timer.qh
qcsrc/menu/xonotic/dialog_hudpanel_vote.qh
qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh
qcsrc/menu/xonotic/dialog_hudsetup_exit.qh
qcsrc/menu/xonotic/dialog_monstertools.qh
qcsrc/menu/xonotic/dialog_multiplayer.qh
qcsrc/menu/xonotic/dialog_multiplayer_create.qc
qcsrc/menu/xonotic/dialog_multiplayer_create.qh
qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc
qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh
qcsrc/menu/xonotic/dialog_multiplayer_join.qc
qcsrc/menu/xonotic/dialog_multiplayer_join.qh
qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc
qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh
qcsrc/menu/xonotic/dialog_multiplayer_media.qh
qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qh
qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh
qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh
qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qh
qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qh
qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qh
qcsrc/menu/xonotic/dialog_multiplayer_profile.qh
qcsrc/menu/xonotic/dialog_quit.qh
qcsrc/menu/xonotic/dialog_sandboxtools.qh
qcsrc/menu/xonotic/dialog_settings.qh
qcsrc/menu/xonotic/dialog_settings_audio.qh
qcsrc/menu/xonotic/dialog_settings_effects.qh
qcsrc/menu/xonotic/dialog_settings_game.qh
qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh
qcsrc/menu/xonotic/dialog_settings_game_hud.qc
qcsrc/menu/xonotic/dialog_settings_game_hud.qh
qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh
qcsrc/menu/xonotic/dialog_settings_game_messages.qc
qcsrc/menu/xonotic/dialog_settings_game_messages.qh
qcsrc/menu/xonotic/dialog_settings_game_model.qc
qcsrc/menu/xonotic/dialog_settings_game_model.qh
qcsrc/menu/xonotic/dialog_settings_game_view.qh
qcsrc/menu/xonotic/dialog_settings_game_weapons.qh
qcsrc/menu/xonotic/dialog_settings_input.qh
qcsrc/menu/xonotic/dialog_settings_input_userbind.qh
qcsrc/menu/xonotic/dialog_settings_misc.qc
qcsrc/menu/xonotic/dialog_settings_misc.qh
qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh
qcsrc/menu/xonotic/dialog_settings_misc_reset.qh
qcsrc/menu/xonotic/dialog_settings_user.qh
qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh
qcsrc/menu/xonotic/dialog_settings_video.qc
qcsrc/menu/xonotic/dialog_settings_video.qh
qcsrc/menu/xonotic/dialog_singleplayer.qh
qcsrc/menu/xonotic/dialog_singleplayer_winner.qh
qcsrc/menu/xonotic/dialog_teamselect.qh
qcsrc/menu/xonotic/dialog_uid2name.qc [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_uid2name.qh [new file with mode: 0644]
qcsrc/menu/xonotic/gametypelist.qc
qcsrc/menu/xonotic/gametypelist.qh
qcsrc/menu/xonotic/hudskinlist.qh
qcsrc/menu/xonotic/inputbox.qh
qcsrc/menu/xonotic/keybinder.qc
qcsrc/menu/xonotic/keybinder.qh
qcsrc/menu/xonotic/languagelist.qh
qcsrc/menu/xonotic/listbox.qh
qcsrc/menu/xonotic/mainwindow.qc
qcsrc/menu/xonotic/mainwindow.qh
qcsrc/menu/xonotic/maplist.qc
qcsrc/menu/xonotic/maplist.qh
qcsrc/menu/xonotic/picker.qh
qcsrc/menu/xonotic/playerlist.qh
qcsrc/menu/xonotic/playermodel.qc
qcsrc/menu/xonotic/playermodel.qh
qcsrc/menu/xonotic/playlist.qh
qcsrc/menu/xonotic/radiobutton.qh
qcsrc/menu/xonotic/rootdialog.qh
qcsrc/menu/xonotic/screenshotimage.qc
qcsrc/menu/xonotic/screenshotimage.qh
qcsrc/menu/xonotic/screenshotlist.qh
qcsrc/menu/xonotic/serverlist.qc
qcsrc/menu/xonotic/serverlist.qh
qcsrc/menu/xonotic/skinlist.qh
qcsrc/menu/xonotic/slider.qh
qcsrc/menu/xonotic/slider_decibels.qc
qcsrc/menu/xonotic/slider_picmip.qh
qcsrc/menu/xonotic/slider_resolution.qc
qcsrc/menu/xonotic/slider_resolution.qh
qcsrc/menu/xonotic/slider_sbfadetime.qc
qcsrc/menu/xonotic/soundlist.qh
qcsrc/menu/xonotic/statslist.qc
qcsrc/menu/xonotic/statslist.qh
qcsrc/menu/xonotic/tab.qh
qcsrc/menu/xonotic/tabcontroller.qh
qcsrc/menu/xonotic/textlabel.qh
qcsrc/menu/xonotic/textslider.qh
qcsrc/menu/xonotic/util.qc
qcsrc/menu/xonotic/util.qh
qcsrc/menu/xonotic/weaponarenacheckbox.qh
qcsrc/menu/xonotic/weaponslist.qc
qcsrc/menu/xonotic/weaponslist.qh
qcsrc/server-testcase/framework.qc
qcsrc/server/_all.inc [new file with mode: 0644]
qcsrc/server/_all.qh
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/anticheat.qc
qcsrc/server/anticheat.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/_mod.inc
qcsrc/server/bot/_mod.qh
qcsrc/server/bot/aim.qc [deleted file]
qcsrc/server/bot/aim.qh [deleted file]
qcsrc/server/bot/api.qc [new file with mode: 0644]
qcsrc/server/bot/api.qh [new file with mode: 0644]
qcsrc/server/bot/bot.qc [deleted file]
qcsrc/server/bot/bot.qh [deleted file]
qcsrc/server/bot/default/_mod.inc [new file with mode: 0644]
qcsrc/server/bot/default/_mod.qh [new file with mode: 0644]
qcsrc/server/bot/default/aim.qc [new file with mode: 0644]
qcsrc/server/bot/default/aim.qh [new file with mode: 0644]
qcsrc/server/bot/default/bot.qc [new file with mode: 0644]
qcsrc/server/bot/default/bot.qh [new file with mode: 0644]
qcsrc/server/bot/default/cvars.qc [new file with mode: 0644]
qcsrc/server/bot/default/cvars.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/_mod.inc [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/_mod.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/havocbot.qc [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/havocbot.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/roles.qc [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/roles.qh [new file with mode: 0644]
qcsrc/server/bot/default/havocbot/scripting.qh [new file with mode: 0644]
qcsrc/server/bot/default/navigation.qc [new file with mode: 0644]
qcsrc/server/bot/default/navigation.qh [new file with mode: 0644]
qcsrc/server/bot/default/scripting.qc [new file with mode: 0644]
qcsrc/server/bot/default/scripting.qh [new file with mode: 0644]
qcsrc/server/bot/default/waypoints.qc [new file with mode: 0644]
qcsrc/server/bot/default/waypoints.qh [new file with mode: 0644]
qcsrc/server/bot/havocbot/_mod.inc [deleted file]
qcsrc/server/bot/havocbot/_mod.qh [deleted file]
qcsrc/server/bot/havocbot/havocbot.qc [deleted file]
qcsrc/server/bot/havocbot/havocbot.qh [deleted file]
qcsrc/server/bot/havocbot/roles.qc [deleted file]
qcsrc/server/bot/havocbot/roles.qh [deleted file]
qcsrc/server/bot/havocbot/scripting.qh [deleted file]
qcsrc/server/bot/navigation.qc [deleted file]
qcsrc/server/bot/navigation.qh [deleted file]
qcsrc/server/bot/null/_mod.inc [new file with mode: 0644]
qcsrc/server/bot/null/_mod.qh [new file with mode: 0644]
qcsrc/server/bot/null/bot_null.qc [new file with mode: 0644]
qcsrc/server/bot/null/bot_null.qh [new file with mode: 0644]
qcsrc/server/bot/scripting.qc [deleted file]
qcsrc/server/bot/scripting.qh [deleted file]
qcsrc/server/bot/waypoints.qc [deleted file]
qcsrc/server/bot/waypoints.qh [deleted file]
qcsrc/server/campaign.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc [deleted file]
qcsrc/server/cl_client.qh [deleted file]
qcsrc/server/cl_impulse.qc [deleted file]
qcsrc/server/cl_impulse.qh [deleted file]
qcsrc/server/cl_player.qc [deleted file]
qcsrc/server/cl_player.qh [deleted file]
qcsrc/server/client.qc [new file with mode: 0644]
qcsrc/server/client.qh [new file with mode: 0644]
qcsrc/server/command/_mod.inc
qcsrc/server/command/_mod.qh
qcsrc/server/command/all.qc [deleted file]
qcsrc/server/command/all.qh [deleted file]
qcsrc/server/command/banning.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/cmd.qh
qcsrc/server/command/common.qc
qcsrc/server/command/common.qh
qcsrc/server/command/getreplies.qc
qcsrc/server/command/radarmap.qc
qcsrc/server/command/reg.qc [new file with mode: 0644]
qcsrc/server/command/reg.qh [new file with mode: 0644]
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/compat/_mod.inc [new file with mode: 0644]
qcsrc/server/compat/_mod.qh [new file with mode: 0644]
qcsrc/server/compat/halflife.qc [new file with mode: 0644]
qcsrc/server/compat/halflife.qh [new file with mode: 0644]
qcsrc/server/compat/quake.qc [new file with mode: 0644]
qcsrc/server/compat/quake.qh [new file with mode: 0644]
qcsrc/server/compat/quake3.qc [new file with mode: 0644]
qcsrc/server/compat/quake3.qh [new file with mode: 0644]
qcsrc/server/compat/wop.qc [new file with mode: 0644]
qcsrc/server/compat/wop.qh [new file with mode: 0644]
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_damage.qh
qcsrc/server/g_hook.qc
qcsrc/server/g_lights.qc
qcsrc/server/g_models.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_subs.qh
qcsrc/server/g_world.qc
qcsrc/server/impulse.qc [new file with mode: 0644]
qcsrc/server/impulse.qh [new file with mode: 0644]
qcsrc/server/ipban.qc
qcsrc/server/item_key.qc
qcsrc/server/mapvoting.qc
qcsrc/server/matrix.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/_mod.inc
qcsrc/server/mutators/_mod.qh
qcsrc/server/mutators/all.inc [deleted file]
qcsrc/server/mutators/all.qc [deleted file]
qcsrc/server/mutators/all.qh [deleted file]
qcsrc/server/mutators/events.qh
qcsrc/server/mutators/gamemode.qh
qcsrc/server/mutators/loader.qc [new file with mode: 0644]
qcsrc/server/mutators/loader.qh [new file with mode: 0644]
qcsrc/server/mutators/mutator.qh
qcsrc/server/mutators/mutator/gamemode_assault.qc
qcsrc/server/mutators/mutator/gamemode_assault.qh
qcsrc/server/mutators/mutator/gamemode_ca.qc
qcsrc/server/mutators/mutator/gamemode_ca.qh
qcsrc/server/mutators/mutator/gamemode_ctf.qc
qcsrc/server/mutators/mutator/gamemode_ctf.qh
qcsrc/server/mutators/mutator/gamemode_cts.qc
qcsrc/server/mutators/mutator/gamemode_cts.qh
qcsrc/server/mutators/mutator/gamemode_deathmatch.qc
qcsrc/server/mutators/mutator/gamemode_deathmatch.qh
qcsrc/server/mutators/mutator/gamemode_domination.qc
qcsrc/server/mutators/mutator/gamemode_domination.qh
qcsrc/server/mutators/mutator/gamemode_freezetag.qc
qcsrc/server/mutators/mutator/gamemode_freezetag.qh
qcsrc/server/mutators/mutator/gamemode_invasion.qc
qcsrc/server/mutators/mutator/gamemode_invasion.qh
qcsrc/server/mutators/mutator/gamemode_keepaway.qc
qcsrc/server/mutators/mutator/gamemode_keepaway.qh
qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
qcsrc/server/mutators/mutator/gamemode_keyhunt.qh
qcsrc/server/mutators/mutator/gamemode_lms.qc
qcsrc/server/mutators/mutator/gamemode_lms.qh
qcsrc/server/mutators/mutator/gamemode_race.qc
qcsrc/server/mutators/mutator/gamemode_race.qh
qcsrc/server/mutators/mutator/gamemode_tdm.qc
qcsrc/server/mutators/mutator/gamemode_tdm.qh
qcsrc/server/pathlib/_all.inc [deleted file]
qcsrc/server/pathlib/costs.qc
qcsrc/server/pathlib/costs.qh
qcsrc/server/pathlib/debug.qc
qcsrc/server/pathlib/debug.qh
qcsrc/server/pathlib/main.qc
qcsrc/server/pathlib/movenode.qc
qcsrc/server/pathlib/path_waypoint.qc
qcsrc/server/pathlib/pathlib.qh
qcsrc/server/player.qc [new file with mode: 0644]
qcsrc/server/player.qh [new file with mode: 0644]
qcsrc/server/playerdemo.qc
qcsrc/server/portals.qc
qcsrc/server/progs.inc
qcsrc/server/race.qc
qcsrc/server/race.qh
qcsrc/server/round_handler.qc
qcsrc/server/scores.qc
qcsrc/server/scores.qh
qcsrc/server/scores_rules.qc
qcsrc/server/scores_rules.qh
qcsrc/server/spawnpoints.qc
qcsrc/server/steerlib.qc
qcsrc/server/steerlib.qh
qcsrc/server/sv_main.qc
qcsrc/server/sys-post.qh
qcsrc/server/sys-pre.qh
qcsrc/server/t_halflife.qc [deleted file]
qcsrc/server/t_halflife.qh [deleted file]
qcsrc/server/t_quake.qc [deleted file]
qcsrc/server/t_quake.qh [deleted file]
qcsrc/server/t_quake3.qc [deleted file]
qcsrc/server/t_quake3.qh [deleted file]
qcsrc/server/teamplay.qc
qcsrc/server/teamplay.qh
qcsrc/server/tests.qc
qcsrc/server/tests.qh
qcsrc/server/weapons/accuracy.qc
qcsrc/server/weapons/common.qc
qcsrc/server/weapons/csqcprojectile.qc
qcsrc/server/weapons/hitplot.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/spawning.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/throwing.qh
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/tracing.qh
qcsrc/server/weapons/weaponstats.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/server/weapons/weaponsystem.qh
qcsrc/tools/auto-super.pl [new file with mode: 0644]
qcsrc/tools/cloc.txt [new file with mode: 0644]
qcsrc/tools/compilationunits.sh
qcsrc/tools/genmod.sh
qcsrc/tools/headerstyle.sh
qcsrc/tools/qcc.sh
qcsrc/tools/whitespace.sh
qcsrc/uncrustify.cfg
scripts/fireball.shader
scripts/luma.shader
scripts/simpleitems.shader
textures/fireball.tga
textures/fireball_gloss.tga
textures/fireball_glow.tga
textures/fireball_norm.tga [new file with mode: 0644]
textures/fireball_pants.tga [new file with mode: 0644]
textures/fireball_shirt.tga [new file with mode: 0644]
textures/items/a_rocket_bottom.jpg [new file with mode: 0644]

index 3cdd43f30510917818e9a8ab0b4365574d83d84d..39414d108ae8b1b3d395537006c571182d4fd6cf 100644 (file)
@@ -5,6 +5,12 @@ before_script:
   - cd gmqcc && make -j $(nproc) && export QCC="$PWD/gmqcc"
   - cd ..
 
+report_cloc:
+  stage: test
+  script:
+    - cloc --force-lang-def=qcsrc/tools/cloc.txt --sql 1 --sql-project xonotic qcsrc | sqlite3 code.db
+    - sqlite3 code.db 'select file,nCode from t where nCode > 1000 order by nBlank+nComment+nCode desc'
+
 test_compilation_units:
   stage: test
   script:
@@ -24,7 +30,7 @@ test_sv_game:
     - wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache
     - wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired
     - make
-    - EXPECT=4bd5b0276cdd100c831c73f0400eca71
+    - EXPECT=37be9dc5489925451eb497390fdf58b9
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index c2b8564351a1b9ce23b56fa0b69a7135d2df527a..2f38e43ba2be7ad75167b6683b238baae72dbc1a 100644 (file)
@@ -4,6 +4,7 @@ project(xonotic-data LANGUAGES ASM)
 
 include_directories(qcsrc)
 
+add_definitions(-DXONOTIC=1)
 add_definitions(-DNDEBUG=1)
 
 find_package(Git REQUIRED)
@@ -25,11 +26,11 @@ set_source_files_properties(
 )
 
 add_executable(csprogs qcsrc/client/progs.inc)
-target_compile_definitions(csprogs PRIVATE -DCSQC)
+target_compile_definitions(csprogs PRIVATE -DGAMEQC -DCSQC)
 add_dependencies(csprogs gmqcc)
 
 add_executable(progs qcsrc/server/progs.inc)
-target_compile_definitions(progs PRIVATE -DSVQC)
+target_compile_definitions(progs PRIVATE -DGAMEQC -DSVQC)
 add_dependencies(progs gmqcc)
 
 add_executable(menu qcsrc/menu/progs.inc)
index d8c7713f1965e1d837cfb33d871d969f3f2a4891..6e88873e960eca4ad0429d1f8dd9141e4e84f8a6 100644 (file)
@@ -37,13 +37,15 @@ seta hud_panel_engineinfo       0 "enable this panel"
 seta hud_panel_infomessages     1 "enable this panel"
 seta hud_panel_physics          3 "enable this panel, 1 = show if not observing, 2 = show always, 3 = show only in race/cts if not observing"
 seta hud_panel_centerprint      1 "enable this panel"
-seta hud_panel_minigameboard    1 "enable this panel"
-seta hud_panel_minigamestatus   1 "enable this panel"
-seta hud_panel_minigamehelp     1 "enable this panel"
-seta hud_panel_minigamemenu     0 "enable this panel"
-seta hud_panel_mapvote          1 "enable this panel"
+//seta hud_panel_minigameboard    1 "enable this panel"
+//seta hud_panel_minigamestatus   1 "enable this panel"
+//seta hud_panel_minigamehelp     1 "enable this panel"
+//seta hud_panel_minigamemenu     0 "enable this panel"
+//seta hud_panel_mapvote          1 "enable this panel"
 seta hud_panel_itemstime        2 "enable this panel, 1 = show when spectating, 2 = even playing in warmup stage"
-seta hud_panel_quickmenu        1 "enable this panel"
+//seta hud_panel_quickmenu        1 "enable this panel"
+//seta hud_panel_scoreboard       1 "enable this panel"
+seta hud_panel_scoreboard_accuracy 1 "show weapon accuracy stats panel on scoreboard; colors can be configured with accuracy_color* cvars"
 
 seta hud_panel_weapons_dynamichud          1 "apply the dynamic hud effects to this panel"
 seta hud_panel_ammo_dynamichud             1 "apply the dynamic hud effects to this panel"
@@ -62,6 +64,7 @@ seta hud_panel_infomessages_dynamichud     0 "apply the dynamic hud effects to t
 seta hud_panel_physics_dynamichud          1 "apply the dynamic hud effects to this panel"
 seta hud_panel_centerprint_dynamichud      1 "apply the dynamic hud effects to this panel"
 seta hud_panel_itemstime_dynamichud        1 "apply the dynamic hud effects to this panel"
+seta hud_panel_scoreboard_dynamichud       0 "apply the dynamic hud effects to this panel"
 
 seta hud_panel_weapons_ammo_full_shells 60 "show 100% of the status bar at this ammo count"
 seta hud_panel_weapons_ammo_full_nails 320 "show 100% of the status bar at this ammo count"
@@ -69,13 +72,18 @@ seta hud_panel_weapons_ammo_full_cells 180 "show 100% of the status bar at this
 seta hud_panel_weapons_ammo_full_plasma 180 "show 100% of the status bar at this ammo count"
 seta hud_panel_weapons_ammo_full_rockets 160 "show 100% of the status bar at this ammo count"
 seta hud_panel_weapons_ammo_full_fuel 100 "show 100% of the status bar at this ammo count"
+seta hud_panel_weapons_hide_ondeath 0 "hide this panel when dead"
 
 seta hud_panel_ammo_maxammo "40" "when you have this much ammo, the ammo status bar is full"
+seta hud_panel_ammo_hide_ondeath 0 "hide this panel when dead"
+
+seta hud_panel_powerups_hide_ondeath 0 "hide this panel when dead"
 
 seta hud_panel_healtharmor_maxhealth "200" "when you have this much health, the health status bar is full"
 seta hud_panel_healtharmor_maxarmor "200" "when you have this much armor, the armor status bar is full"
 seta hud_panel_healtharmor_progressbar_gfx_damage 5 "show damage effect when damaged at least by this amount; 0 disables the effect"
 seta hud_panel_healtharmor_progressbar_gfx_lowhealth 40 "health progressbar blinks when health is lower than this amount"
+seta hud_panel_healtharmor_hide_ondeath 0 "hide this panel when dead"
 
 seta hud_panel_timer_increment "0" "show elapsed time instead of remaining time"
 
@@ -84,17 +92,28 @@ seta hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight 0.1 "
 seta hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold 0.5 "threshold for fps change when to update instantly, to make big fps changes update faster"
 
 seta hud_panel_physics_acceleration_movingaverage 1 "use an averaging method for calculating acceleration instead of the real value"
-seta hud_panel_phisics_update_interval 0.0666 "how often (in seconds) numeric values get updated on screen"
+seta hud_panel_physics_update_interval 0.0666 "how often (in seconds) numeric values get updated on screen"
 seta hud_panel_physics_speed_unit "1" "speed unit (1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots)"
 
 seta hud_panel_itemstime_progressbar_maxtime "30" "when left time is at least this amount, the status bar is full"
 seta hud_panel_itemstime_hidespawned "1" "if 1 hide an item from the panel when all the occurrences of it are available again; if 2 hide it when at least one occurrence is available again"
-seta hud_panel_itemstime_hidelarge "0" "if 1 hide large armor and health from the panel"
+seta hud_panel_itemstime_hidebig "0" "if 1 hide big armor and health from the panel"
 
 seta hud_panel_quickmenu_file "" "load the quick menu from this file (empty or 0 to disable)"
 seta hud_panel_quickmenu_translatecommands 0 "when the game is translated, translate strings inside commands too (useful for chat commands)"
 seta hud_panel_quickmenu_time 5 "quickmenu expires after this number of seconds in the same page"
 
+seta hud_panel_infomessages_group0 1 "show group 0 messages (showing keys for non-crucial actions you can do while spectating/observing)"
+seta hud_panel_infomessages_group_time 6 "number of seconds a message of a group lasts before it gets changed"
+seta hud_panel_infomessages_group_fadetime 0.4 "group message fade in/out time"
+
+seta hud_panel_scoreboard_namesize 15 "size limit of player names and relative column (multiplied by fontsize)"
+seta hud_panel_scoreboard_maxheight 0.6 "max height of the scoreboard; a few players that wouldn't fit into the scoreboard are listed in the last row"
+seta hud_panel_scoreboard_others_showscore 1 "show scores of players listed in the last row when the scoreboard reaches the max height"
+seta hud_panel_scoreboard_spectators_showping 1 "show ping of spectators"
+seta hud_panel_scoreboard_spectators_aligned 0 "align spectators in columns"
+seta hud_panel_scoreboard_minwidth 0.4 "minimum width of the scoreboard"
+
 // hud panel aliases
 alias quickmenu "cl_cmd hud quickmenu ${* ?}"
 
index ee46898904a77f6d992b974f867c8fa798169aa8..9a1654f83abd1a177f392a12fd6d785ce883a706 100644 (file)
@@ -329,6 +329,7 @@ seta hud_panel_mapvote_bg_color_team "" "override panel color with team color in
 seta hud_panel_mapvote_bg_alpha "" "if set to something else than \"\" = override default panel background alpha"
 seta hud_panel_mapvote_bg_border "" "if set to something else than \"\" = override default size of border around the background"
 seta hud_panel_mapvote_bg_padding "" "if set to something else than \"\" = override default padding of contents from border"
+seta hud_panel_mapvote_highlight_border "" "highlight border size of the selected map"
 
 seta hud_panel_itemstime_pos "" "position of this base of the panel"
 seta hud_panel_itemstime_size "" "size of this panel"
@@ -355,3 +356,25 @@ seta hud_panel_quickmenu_bg_alpha "" "if set to something else than \"\" = overr
 seta hud_panel_quickmenu_bg_border "" "if set to something else than \"\" = override default size of border around the background"
 seta hud_panel_quickmenu_bg_padding "" "if set to something else than \"\" = override default padding of contents from border"
 seta hud_panel_quickmenu_align "" "text alignment: 0 left, 0.5 center, 1 right"
+
+seta hud_panel_scoreboard_pos "" "position of this base of the panel"
+seta hud_panel_scoreboard_size "" "size of this panel"
+seta hud_panel_scoreboard_bg "" "if set to something else than \"\" = override default background"
+seta hud_panel_scoreboard_bg_color "" "if set to something else than \"\" = override default panel background color"
+seta hud_panel_scoreboard_bg_color_team "" "override panel color with team color in team based games"
+seta hud_panel_scoreboard_bg_alpha "" "if set to something else than \"\" = override default panel background alpha"
+seta hud_panel_scoreboard_bg_border "" "if set to something else than \"\" = override default size of border around the background"
+seta hud_panel_scoreboard_bg_padding "" "if set to something else than \"\" = override default padding of contents from border"
+seta hud_panel_scoreboard_fadeinspeed "" "speed at which scoreboard fades in, higher is faster (0 = instant)"
+seta hud_panel_scoreboard_fadeoutspeed "" "speed at which scoreboard fades out, higher is faster (0 = instant)"
+seta hud_panel_scoreboard_respawntime_decimals "" "decimal places to show for the respawntime countdown display on the scoreboard"
+seta hud_panel_scoreboard_table_bg_alpha "" "background alpha of a scoreboard table"
+seta hud_panel_scoreboard_table_bg_scale "" "scale for the tiled scoreboard background"
+seta hud_panel_scoreboard_table_fg_alpha "" "foreground alpha of a scoreboard table"
+seta hud_panel_scoreboard_table_fg_alpha_self "" "self foreground alpha of a scoreboard table"
+seta hud_panel_scoreboard_table_highlight "" "enable highlighting for rows and columns in scoreboard tables"
+seta hud_panel_scoreboard_table_highlight_alpha "" "highlight alpha of a scoreboard table"
+seta hud_panel_scoreboard_table_highlight_alpha_self "" "self highlight alpha of a scoreboard table"
+seta hud_panel_scoreboard_bg_teams_color_team "" "override panel team color in team tables"
+seta hud_panel_scoreboard_accuracy_doublerows "" "use two rows instead of one"
+seta hud_panel_scoreboard_accuracy_nocolors "" "don't use colors displaying accuracy stats"
diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg
new file mode 100644 (file)
index 0000000..d3b1066
--- /dev/null
@@ -0,0 +1,820 @@
+// {{{ #1: Blaster
+set g_balance_blaster_primary_animtime 0.2
+set g_balance_blaster_primary_damage 25
+set g_balance_blaster_primary_delay 0
+set g_balance_blaster_primary_edgedamage 12.5
+set g_balance_blaster_primary_force 300
+set g_balance_blaster_primary_force_zscale 1.25
+set g_balance_blaster_primary_lifetime 5
+set g_balance_blaster_primary_radius 60
+set g_balance_blaster_primary_refire 0.7
+set g_balance_blaster_primary_shotangle 0
+set g_balance_blaster_primary_speed 6000
+set g_balance_blaster_primary_spread 0
+set g_balance_blaster_secondary 0
+set g_balance_blaster_secondary_animtime 0.2
+set g_balance_blaster_secondary_damage 25
+set g_balance_blaster_secondary_delay 0
+set g_balance_blaster_secondary_edgedamage 12.5
+set g_balance_blaster_secondary_force 300
+set g_balance_blaster_secondary_force_zscale 1.2
+set g_balance_blaster_secondary_lifetime 5
+set g_balance_blaster_secondary_radius 70
+set g_balance_blaster_secondary_refire 0.7
+set g_balance_blaster_secondary_shotangle 0
+set g_balance_blaster_secondary_speed 6000
+set g_balance_blaster_secondary_spread 0
+set g_balance_blaster_switchdelay_drop 0.2
+set g_balance_blaster_switchdelay_raise 0.2
+set g_balance_blaster_weaponreplace ""
+set g_balance_blaster_weaponstart 1
+set g_balance_blaster_weaponstartoverride -1
+set g_balance_blaster_weaponthrowable 0
+// }}}
+// {{{ #2: Shotgun
+set g_balance_shotgun_primary_ammo 1
+set g_balance_shotgun_primary_animtime 0.2
+set g_balance_shotgun_primary_bullets 12
+set g_balance_shotgun_primary_damage 4
+set g_balance_shotgun_primary_force 15
+set g_balance_shotgun_primary_refire 0.75
+set g_balance_shotgun_primary_solidpenetration 3.8
+set g_balance_shotgun_primary_spread 0.12
+set g_balance_shotgun_reload_ammo 0
+set g_balance_shotgun_reload_time 2
+set g_balance_shotgun_secondary 1
+set g_balance_shotgun_secondary_animtime 1.15
+set g_balance_shotgun_secondary_damage 70
+set g_balance_shotgun_secondary_force 200
+set g_balance_shotgun_secondary_melee_delay 0.25
+set g_balance_shotgun_secondary_melee_multihit 1
+set g_balance_shotgun_secondary_melee_no_doubleslap 1
+set g_balance_shotgun_secondary_melee_nonplayerdamage 40
+set g_balance_shotgun_secondary_melee_range 120
+set g_balance_shotgun_secondary_melee_swing_side 120
+set g_balance_shotgun_secondary_melee_swing_up 30
+set g_balance_shotgun_secondary_melee_time 0.15
+set g_balance_shotgun_secondary_melee_traces 10
+set g_balance_shotgun_secondary_refire 1.25
+set g_balance_shotgun_secondary_alt_animtime 0.2
+set g_balance_shotgun_secondary_alt_refire 1.2
+set g_balance_shotgun_switchdelay_drop 0.2
+set g_balance_shotgun_switchdelay_raise 0.2
+set g_balance_shotgun_weaponreplace ""
+set g_balance_shotgun_weaponstart 1
+set g_balance_shotgun_weaponstartoverride -1
+set g_balance_shotgun_weaponthrowable 1
+// }}}
+// {{{ #3: Machine Gun
+set g_balance_machinegun_burst 3
+set g_balance_machinegun_burst_ammo 3
+set g_balance_machinegun_burst_animtime 0.3
+set g_balance_machinegun_burst_refire 0.06
+set g_balance_machinegun_burst_refire2 0.45
+set g_balance_machinegun_burst_speed 0
+set g_balance_machinegun_first 1
+set g_balance_machinegun_first_ammo 1
+set g_balance_machinegun_first_damage 14
+set g_balance_machinegun_first_force 5
+set g_balance_machinegun_first_refire 0.125
+set g_balance_machinegun_first_spread 0.03
+set g_balance_machinegun_mode 1
+set g_balance_machinegun_reload_ammo 60
+set g_balance_machinegun_reload_time 2
+set g_balance_machinegun_solidpenetration 13.1
+set g_balance_machinegun_spread_add 0.012
+set g_balance_machinegun_spread_max 0.05
+set g_balance_machinegun_spread_min 0.02
+set g_balance_machinegun_sustained_ammo 1
+set g_balance_machinegun_sustained_damage 10
+set g_balance_machinegun_sustained_force 5
+set g_balance_machinegun_sustained_refire 0.1
+set g_balance_machinegun_sustained_spread 0.03
+set g_balance_machinegun_switchdelay_drop 0.2
+set g_balance_machinegun_switchdelay_raise 0.2
+set g_balance_machinegun_weaponreplace ""
+set g_balance_machinegun_weaponstart 0
+set g_balance_machinegun_weaponstartoverride -1
+set g_balance_machinegun_weaponthrowable 1
+// }}}
+// {{{ #4: Mortar
+set g_balance_mortar_bouncefactor 0.5
+set g_balance_mortar_bouncestop 0.075
+set g_balance_mortar_primary_ammo 2
+set g_balance_mortar_primary_animtime 0.3
+set g_balance_mortar_primary_damage 55
+set g_balance_mortar_primary_damageforcescale 0
+set g_balance_mortar_primary_edgedamage 25
+set g_balance_mortar_primary_force 250
+set g_balance_mortar_primary_health 15
+set g_balance_mortar_primary_lifetime 5
+set g_balance_mortar_primary_lifetime_stick 0
+set g_balance_mortar_primary_radius 120
+set g_balance_mortar_primary_refire 0.8
+set g_balance_mortar_primary_remote_minbouncecnt 0
+set g_balance_mortar_primary_speed 1900
+set g_balance_mortar_primary_speed_up 225
+set g_balance_mortar_primary_speed_z 0
+set g_balance_mortar_primary_spread 0
+set g_balance_mortar_primary_type 0
+set g_balance_mortar_reload_ammo 0
+set g_balance_mortar_reload_time 2
+set g_balance_mortar_secondary_ammo 2
+set g_balance_mortar_secondary_animtime 0.3
+set g_balance_mortar_secondary_damage 55
+set g_balance_mortar_secondary_damageforcescale 4
+set g_balance_mortar_secondary_edgedamage 30
+set g_balance_mortar_secondary_force 250
+set g_balance_mortar_secondary_health 30
+set g_balance_mortar_secondary_lifetime 5
+set g_balance_mortar_secondary_lifetime_bounce 0.5
+set g_balance_mortar_secondary_lifetime_stick 0
+set g_balance_mortar_secondary_radius 120
+set g_balance_mortar_secondary_refire 0.7
+set g_balance_mortar_secondary_remote_detonateprimary 0
+set g_balance_mortar_secondary_speed 1400
+set g_balance_mortar_secondary_speed_up 150
+set g_balance_mortar_secondary_speed_z 0
+set g_balance_mortar_secondary_spread 0
+set g_balance_mortar_secondary_type 1
+set g_balance_mortar_switchdelay_drop 0.2
+set g_balance_mortar_switchdelay_raise 0.2
+set g_balance_mortar_weaponreplace ""
+set g_balance_mortar_weaponstart 0
+set g_balance_mortar_weaponstartoverride -1
+set g_balance_mortar_weaponthrowable 1
+// }}}
+// {{{ #5: Mine Layer (MUTATOR WEAPON)
+set g_balance_minelayer_ammo 4
+set g_balance_minelayer_animtime 0.4
+set g_balance_minelayer_damage 40
+set g_balance_minelayer_damageforcescale 0
+set g_balance_minelayer_detonatedelay -1
+set g_balance_minelayer_edgedamage 20
+set g_balance_minelayer_force 250
+set g_balance_minelayer_health 15
+set g_balance_minelayer_lifetime 10
+set g_balance_minelayer_lifetime_countdown 0.5
+set g_balance_minelayer_limit 3
+set g_balance_minelayer_protection 0
+set g_balance_minelayer_proximityradius 150
+set g_balance_minelayer_radius 175
+set g_balance_minelayer_refire 1.5
+set g_balance_minelayer_reload_ammo 0
+set g_balance_minelayer_reload_time 2
+set g_balance_minelayer_remote_damage 45
+set g_balance_minelayer_remote_edgedamage 40
+set g_balance_minelayer_remote_force 300
+set g_balance_minelayer_remote_radius 200
+set g_balance_minelayer_speed 1000
+set g_balance_minelayer_switchdelay_drop 0.2
+set g_balance_minelayer_switchdelay_raise 0.2
+set g_balance_minelayer_time 0.5
+set g_balance_minelayer_weaponreplace ""
+set g_balance_minelayer_weaponstart 0
+set g_balance_minelayer_weaponstartoverride -1
+set g_balance_minelayer_weaponthrowable 1
+// }}}
+// {{{ #6: Electro
+set g_balance_electro_combo_comboradius 300
+set g_balance_electro_combo_comboradius_thruwall 200
+set g_balance_electro_combo_damage 50
+set g_balance_electro_combo_edgedamage 25
+set g_balance_electro_combo_force 120
+set g_balance_electro_combo_radius 150
+set g_balance_electro_combo_safeammocheck 1
+set g_balance_electro_combo_speed 2000
+set g_balance_electro_primary_ammo 4
+set g_balance_electro_primary_animtime 0.3
+set g_balance_electro_primary_comboradius 300
+set g_balance_electro_primary_damage 40
+set g_balance_electro_primary_edgedamage 20
+set g_balance_electro_primary_force 200
+set g_balance_electro_primary_lifetime 5
+set g_balance_electro_primary_midaircombo_explode 1
+set g_balance_electro_primary_midaircombo_interval 0.1
+set g_balance_electro_primary_midaircombo_radius 0
+set g_balance_electro_primary_radius 100
+set g_balance_electro_primary_refire 0.6
+set g_balance_electro_primary_speed 2500
+set g_balance_electro_primary_spread 0
+set g_balance_electro_reload_ammo 0
+set g_balance_electro_reload_time 2
+set g_balance_electro_secondary_ammo 2
+set g_balance_electro_secondary_animtime 0.2
+set g_balance_electro_secondary_bouncefactor 0.3
+set g_balance_electro_secondary_bouncestop 0.05
+set g_balance_electro_secondary_count 3
+set g_balance_electro_secondary_damage 30
+set g_balance_electro_secondary_damagedbycontents 1
+set g_balance_electro_secondary_damageforcescale 4
+set g_balance_electro_secondary_edgedamage 15
+set g_balance_electro_secondary_force 50
+set g_balance_electro_secondary_health 5
+set g_balance_electro_secondary_lifetime 4
+set g_balance_electro_secondary_radius 150
+set g_balance_electro_secondary_refire 0.2
+set g_balance_electro_secondary_refire2 1.6
+set g_balance_electro_secondary_speed 1000
+set g_balance_electro_secondary_speed_up 200
+set g_balance_electro_secondary_speed_z 0
+set g_balance_electro_secondary_spread 0
+set g_balance_electro_secondary_stick 1
+set g_balance_electro_secondary_touchexplode 1
+set g_balance_electro_switchdelay_drop 0.2
+set g_balance_electro_switchdelay_raise 0.2
+set g_balance_electro_weaponreplace ""
+set g_balance_electro_weaponstart 0
+set g_balance_electro_weaponstartoverride -1
+set g_balance_electro_weaponthrowable 1
+// }}}
+// {{{ #7: Crylink
+set g_balance_crylink_primary_ammo 3
+set g_balance_crylink_primary_animtime 0.3
+set g_balance_crylink_primary_bouncedamagefactor 0.5
+set g_balance_crylink_primary_bounces 1
+set g_balance_crylink_primary_damage 12
+set g_balance_crylink_primary_edgedamage 6
+set g_balance_crylink_primary_force -50
+set g_balance_crylink_primary_joindelay 0.1
+set g_balance_crylink_primary_joinexplode 1
+set g_balance_crylink_primary_joinexplode_damage 0
+set g_balance_crylink_primary_joinexplode_edgedamage 0
+set g_balance_crylink_primary_joinexplode_force 0
+set g_balance_crylink_primary_joinexplode_radius 0
+set g_balance_crylink_primary_joinspread 0.2
+set g_balance_crylink_primary_linkexplode 0
+set g_balance_crylink_primary_middle_fadetime 5
+set g_balance_crylink_primary_middle_lifetime 5
+set g_balance_crylink_primary_other_fadetime 5
+set g_balance_crylink_primary_other_lifetime 5
+set g_balance_crylink_primary_radius 80
+set g_balance_crylink_primary_refire 0.7
+set g_balance_crylink_primary_shots 6
+set g_balance_crylink_primary_speed 2000
+set g_balance_crylink_primary_spread 0.08
+set g_balance_crylink_reload_ammo 0
+set g_balance_crylink_reload_time 2
+set g_balance_crylink_secondary 1
+set g_balance_crylink_secondary_ammo 2
+set g_balance_crylink_secondary_animtime 0.2
+set g_balance_crylink_secondary_bouncedamagefactor 0.5
+set g_balance_crylink_secondary_bounces 0
+set g_balance_crylink_secondary_damage 10
+set g_balance_crylink_secondary_edgedamage 5
+set g_balance_crylink_secondary_force -250
+set g_balance_crylink_secondary_joindelay 0
+set g_balance_crylink_secondary_joinexplode 0
+set g_balance_crylink_secondary_joinexplode_damage 0
+set g_balance_crylink_secondary_joinexplode_edgedamage 0
+set g_balance_crylink_secondary_joinexplode_force 0
+set g_balance_crylink_secondary_joinexplode_radius 0
+set g_balance_crylink_secondary_joinspread 0
+set g_balance_crylink_secondary_linkexplode 1
+set g_balance_crylink_secondary_middle_fadetime 5
+set g_balance_crylink_secondary_middle_lifetime 5
+set g_balance_crylink_secondary_other_fadetime 5
+set g_balance_crylink_secondary_other_lifetime 5
+set g_balance_crylink_secondary_radius 100
+set g_balance_crylink_secondary_refire 0.7
+set g_balance_crylink_secondary_shots 5
+set g_balance_crylink_secondary_speed 3000
+set g_balance_crylink_secondary_spread 0.01
+set g_balance_crylink_secondary_spreadtype 1
+set g_balance_crylink_switchdelay_drop 0.2
+set g_balance_crylink_switchdelay_raise 0.2
+set g_balance_crylink_weaponreplace ""
+set g_balance_crylink_weaponstart 0
+set g_balance_crylink_weaponstartoverride -1
+set g_balance_crylink_weaponthrowable 1
+// }}}
+// {{{ #8: Vortex
+set g_balance_vortex_charge 0
+set g_balance_vortex_charge_animlimit 0.5
+set g_balance_vortex_charge_limit 1
+set g_balance_vortex_charge_maxspeed 800
+set g_balance_vortex_charge_mindmg 40
+set g_balance_vortex_charge_minspeed 400
+set g_balance_vortex_charge_rate 0.6
+set g_balance_vortex_charge_rot_pause 0
+set g_balance_vortex_charge_rot_rate 0
+set g_balance_vortex_charge_shot_multiplier 0
+set g_balance_vortex_charge_start 0.5
+set g_balance_vortex_charge_velocity_rate 0
+set g_balance_vortex_primary_ammo 6
+set g_balance_vortex_primary_animtime 0.4
+set g_balance_vortex_primary_damage 70
+set g_balance_vortex_primary_damagefalloff_forcehalflife 0
+set g_balance_vortex_primary_damagefalloff_halflife 0
+set g_balance_vortex_primary_damagefalloff_maxdist 0
+set g_balance_vortex_primary_damagefalloff_mindist 0
+set g_balance_vortex_primary_force 400
+set g_balance_vortex_primary_refire 1.5
+set g_balance_vortex_reload_ammo 0
+set g_balance_vortex_reload_time 2
+set g_balance_vortex_secondary 0
+set g_balance_vortex_secondary_ammo 2
+set g_balance_vortex_secondary_animtime 0
+set g_balance_vortex_secondary_chargepool 0
+set g_balance_vortex_secondary_chargepool_pause_regen 1
+set g_balance_vortex_secondary_chargepool_regen 0.15
+set g_balance_vortex_secondary_damage 0
+set g_balance_vortex_secondary_damagefalloff_forcehalflife 0
+set g_balance_vortex_secondary_damagefalloff_halflife 0
+set g_balance_vortex_secondary_damagefalloff_maxdist 0
+set g_balance_vortex_secondary_damagefalloff_mindist 0
+set g_balance_vortex_secondary_force 0
+set g_balance_vortex_secondary_refire 0
+set g_balance_vortex_switchdelay_drop 0.2
+set g_balance_vortex_switchdelay_raise 0.2
+set g_balance_vortex_weaponreplace ""
+set g_balance_vortex_weaponstart 0
+set g_balance_vortex_weaponstartoverride -1
+set g_balance_vortex_weaponthrowable 1
+// }}}
+// {{{ #9: Hagar
+set g_balance_hagar_primary_ammo 1
+set g_balance_hagar_primary_damage 25
+set g_balance_hagar_primary_damageforcescale 0
+set g_balance_hagar_primary_edgedamage 12.5
+set g_balance_hagar_primary_force 100
+set g_balance_hagar_primary_health 15
+set g_balance_hagar_primary_lifetime 5
+set g_balance_hagar_primary_radius 65
+set g_balance_hagar_primary_refire 0.16667
+set g_balance_hagar_primary_speed 2200
+set g_balance_hagar_primary_spread 0
+set g_balance_hagar_reload_ammo 0
+set g_balance_hagar_reload_time 2
+set g_balance_hagar_secondary 1
+set g_balance_hagar_secondary_ammo 1
+set g_balance_hagar_secondary_damage 35
+set g_balance_hagar_secondary_damageforcescale 0
+set g_balance_hagar_secondary_edgedamage 17.5
+set g_balance_hagar_secondary_force 75
+set g_balance_hagar_secondary_health 15
+set g_balance_hagar_secondary_lifetime_min 10
+set g_balance_hagar_secondary_lifetime_rand 0
+set g_balance_hagar_secondary_load 1
+set g_balance_hagar_secondary_load_abort 1
+set g_balance_hagar_secondary_load_animtime 0.2
+set g_balance_hagar_secondary_load_hold 4
+set g_balance_hagar_secondary_load_linkexplode 0
+set g_balance_hagar_secondary_load_max 4
+set g_balance_hagar_secondary_load_releasedeath 0
+set g_balance_hagar_secondary_load_speed 0.5
+set g_balance_hagar_secondary_load_spread 0.075
+set g_balance_hagar_secondary_load_spread_bias 0.5
+set g_balance_hagar_secondary_radius 80
+set g_balance_hagar_secondary_refire 0.5
+set g_balance_hagar_secondary_speed 2000
+set g_balance_hagar_secondary_spread 0
+set g_balance_hagar_switchdelay_drop 0.2
+set g_balance_hagar_switchdelay_raise 0.2
+set g_balance_hagar_weaponreplace ""
+set g_balance_hagar_weaponstart 0
+set g_balance_hagar_weaponstartoverride -1
+set g_balance_hagar_weaponthrowable 1
+// }}}
+// {{{ #10: Devastator
+set g_balance_devastator_ammo 4
+set g_balance_devastator_animtime 0.4
+set g_balance_devastator_damage 80
+set g_balance_devastator_damageforcescale 1
+set g_balance_devastator_detonatedelay 0.02
+set g_balance_devastator_edgedamage 40
+set g_balance_devastator_force 400
+set g_balance_devastator_guidedelay 0.2
+set g_balance_devastator_guidegoal 512
+set g_balance_devastator_guiderate 90
+set g_balance_devastator_guideratedelay 0.01
+set g_balance_devastator_guidestop 0
+set g_balance_devastator_health 30
+set g_balance_devastator_lifetime 10
+set g_balance_devastator_radius 110
+set g_balance_devastator_refire 1.1
+set g_balance_devastator_reload_ammo 0
+set g_balance_devastator_reload_time 2
+set g_balance_devastator_remote_damage 70
+set g_balance_devastator_remote_edgedamage 35
+set g_balance_devastator_remote_force 300
+set g_balance_devastator_remote_jump_damage 70
+set g_balance_devastator_remote_jump_force 450
+set g_balance_devastator_remote_jump_radius 100
+set g_balance_devastator_remote_jump_velocity_z_add 0
+set g_balance_devastator_remote_jump_velocity_z_max 1500
+set g_balance_devastator_remote_jump_velocity_z_min 400
+set g_balance_devastator_remote_radius 110
+set g_balance_devastator_speed 1300
+set g_balance_devastator_speedaccel 1300
+set g_balance_devastator_speedstart 1000
+set g_balance_devastator_switchdelay_drop 0.2
+set g_balance_devastator_switchdelay_raise 0.2
+set g_balance_devastator_weaponreplace ""
+set g_balance_devastator_weaponstart 0
+set g_balance_devastator_weaponstartoverride -1
+set g_balance_devastator_weaponthrowable 1
+// }}}
+// {{{ #11: Port-O-Launch
+set g_balance_porto_primary_animtime 0.3
+set g_balance_porto_primary_lifetime 5
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_speed 1000
+set g_balance_porto_secondary 1
+set g_balance_porto_secondary_animtime 0.3
+set g_balance_porto_secondary_lifetime 5
+set g_balance_porto_secondary_refire 1.5
+set g_balance_porto_secondary_speed 1000
+set g_balance_porto_switchdelay_drop 0.2
+set g_balance_porto_switchdelay_raise 0.2
+set g_balance_porto_weaponreplace ""
+set g_balance_porto_weaponstart 0
+set g_balance_porto_weaponstartoverride -1
+set g_balance_porto_weaponthrowable 1
+// }}}
+// {{{ #12: Vaporizer
+set g_balance_vaporizer_primary_ammo 10
+set g_balance_vaporizer_primary_animtime 0.3
+set g_balance_vaporizer_primary_damage 150
+set g_balance_vaporizer_primary_refire 1
+set g_balance_vaporizer_reload_ammo 0
+set g_balance_vaporizer_reload_time 0
+set g_balance_vaporizer_secondary_ammo 0
+set g_balance_vaporizer_secondary_animtime 0.2
+set g_balance_vaporizer_secondary_damage 25
+set g_balance_vaporizer_secondary_delay 0
+set g_balance_vaporizer_secondary_edgedamage 12.5
+set g_balance_vaporizer_secondary_force 400
+set g_balance_vaporizer_secondary_lifetime 5
+set g_balance_vaporizer_secondary_radius 70
+set g_balance_vaporizer_secondary_refire 0.7
+set g_balance_vaporizer_secondary_shotangle 0
+set g_balance_vaporizer_secondary_speed 6000
+set g_balance_vaporizer_secondary_spread 0
+set g_balance_vaporizer_switchdelay_drop 0.2
+set g_balance_vaporizer_switchdelay_raise 0.2
+set g_balance_vaporizer_weaponreplace ""
+set g_balance_vaporizer_weaponstart 0
+set g_balance_vaporizer_weaponstartoverride -1
+set g_balance_vaporizer_weaponthrowable 0
+// }}}
+// {{{ #13: Grappling Hook
+set g_balance_hook_primary_ammo 5
+set g_balance_hook_primary_animtime 0.3
+set g_balance_hook_primary_hooked_ammo 5
+set g_balance_hook_primary_hooked_time_free 2
+set g_balance_hook_primary_hooked_time_max 0
+set g_balance_hook_primary_refire 0.2
+set g_balance_hook_secondary_animtime 0.3
+set g_balance_hook_secondary_damage 25
+set g_balance_hook_secondary_damageforcescale 0
+set g_balance_hook_secondary_duration 1.5
+set g_balance_hook_secondary_edgedamage 5
+set g_balance_hook_secondary_force -2000
+set g_balance_hook_secondary_gravity 5
+set g_balance_hook_secondary_health 15
+set g_balance_hook_secondary_lifetime 5
+set g_balance_hook_secondary_power 3
+set g_balance_hook_secondary_radius 500
+set g_balance_hook_secondary_refire 3
+set g_balance_hook_secondary_speed 0
+set g_balance_hook_switchdelay_drop 0.2
+set g_balance_hook_switchdelay_raise 0.2
+set g_balance_hook_weaponreplace ""
+set g_balance_hook_weaponstart 0
+set g_balance_hook_weaponstartoverride -1
+set g_balance_hook_weaponthrowable 1
+// }}}
+// {{{ #14: Heavy Laser Assault Cannon (MUTATOR WEAPON)
+set g_balance_hlac_primary_ammo 1
+set g_balance_hlac_primary_animtime 0.4
+set g_balance_hlac_primary_damage 18
+set g_balance_hlac_primary_edgedamage 9
+set g_balance_hlac_primary_force 90
+set g_balance_hlac_primary_lifetime 5
+set g_balance_hlac_primary_radius 70
+set g_balance_hlac_primary_refire 0.15
+set g_balance_hlac_primary_speed 9000
+set g_balance_hlac_primary_spread_add 0.0045
+set g_balance_hlac_primary_spread_crouchmod 0.25
+set g_balance_hlac_primary_spread_max 0.25
+set g_balance_hlac_primary_spread_min 0.01
+set g_balance_hlac_reload_ammo 0
+set g_balance_hlac_reload_time 2
+set g_balance_hlac_secondary 1
+set g_balance_hlac_secondary_ammo 10
+set g_balance_hlac_secondary_animtime 0.3
+set g_balance_hlac_secondary_damage 15
+set g_balance_hlac_secondary_edgedamage 7.5
+set g_balance_hlac_secondary_force 90
+set g_balance_hlac_secondary_lifetime 5
+set g_balance_hlac_secondary_radius 70
+set g_balance_hlac_secondary_refire 1
+set g_balance_hlac_secondary_shots 6
+set g_balance_hlac_secondary_speed 9000
+set g_balance_hlac_secondary_spread 0.15
+set g_balance_hlac_secondary_spread_crouchmod 0.5
+set g_balance_hlac_switchdelay_drop 0.2
+set g_balance_hlac_switchdelay_raise 0.2
+set g_balance_hlac_weaponreplace ""
+set g_balance_hlac_weaponstart 0
+set g_balance_hlac_weaponstartoverride -1
+set g_balance_hlac_weaponthrowable 1
+// }}}
+// {{{ #15: @!#%'n Tuba
+set g_balance_tuba_animtime 0.05
+set g_balance_tuba_attenuation 0.5
+set g_balance_tuba_damage 5
+set g_balance_tuba_edgedamage 0
+set g_balance_tuba_fadetime 0.25
+set g_balance_tuba_force 40
+set g_balance_tuba_pitchstep 6
+set g_balance_tuba_radius 200
+set g_balance_tuba_refire 0.05
+set g_balance_tuba_switchdelay_drop 0.2
+set g_balance_tuba_switchdelay_raise 0.2
+set g_balance_tuba_volume 1
+set g_balance_tuba_weaponreplace ""
+set g_balance_tuba_weaponstart 0
+set g_balance_tuba_weaponstartoverride -1
+set g_balance_tuba_weaponthrowable 1
+// }}}
+// {{{ #16: Rifle (MUTATOR WEAPON)
+set g_balance_rifle_bursttime 0
+set g_balance_rifle_primary_ammo 10
+set g_balance_rifle_primary_animtime 0.4
+set g_balance_rifle_primary_bullethail 0
+set g_balance_rifle_primary_burstcost 0
+set g_balance_rifle_primary_damage 80
+set g_balance_rifle_primary_force 100
+set g_balance_rifle_primary_refire 1.2
+set g_balance_rifle_primary_shots 1
+set g_balance_rifle_primary_solidpenetration 62.2
+set g_balance_rifle_primary_spread 0
+set g_balance_rifle_primary_tracer 1
+set g_balance_rifle_reload_ammo 80
+set g_balance_rifle_reload_time 2
+set g_balance_rifle_secondary 1
+set g_balance_rifle_secondary_ammo 10
+set g_balance_rifle_secondary_animtime 0.3
+set g_balance_rifle_secondary_bullethail 0
+set g_balance_rifle_secondary_burstcost 0
+set g_balance_rifle_secondary_damage 20
+set g_balance_rifle_secondary_force 50
+set g_balance_rifle_secondary_refire 0.9
+set g_balance_rifle_secondary_reload 0
+set g_balance_rifle_secondary_shots 4
+set g_balance_rifle_secondary_solidpenetration 15.5
+set g_balance_rifle_secondary_spread 0.04
+set g_balance_rifle_secondary_tracer 0
+set g_balance_rifle_switchdelay_drop 0.2
+set g_balance_rifle_switchdelay_raise 0.2
+set g_balance_rifle_weaponreplace ""
+set g_balance_rifle_weaponstart 0
+set g_balance_rifle_weaponstartoverride -1
+set g_balance_rifle_weaponthrowable 1
+// }}}
+// {{{ #17: Fireball
+set g_balance_fireball_primary_animtime 0.4
+set g_balance_fireball_primary_bfgdamage 100
+set g_balance_fireball_primary_bfgforce 0
+set g_balance_fireball_primary_bfgradius 1000
+set g_balance_fireball_primary_damage 200
+set g_balance_fireball_primary_damageforcescale 0
+set g_balance_fireball_primary_edgedamage 50
+set g_balance_fireball_primary_force 600
+set g_balance_fireball_primary_health 0
+set g_balance_fireball_primary_laserburntime 0.5
+set g_balance_fireball_primary_laserdamage 80
+set g_balance_fireball_primary_laseredgedamage 20
+set g_balance_fireball_primary_laserradius 256
+set g_balance_fireball_primary_lifetime 15
+set g_balance_fireball_primary_radius 200
+set g_balance_fireball_primary_refire 2
+set g_balance_fireball_primary_refire2 0
+set g_balance_fireball_primary_speed 1200
+set g_balance_fireball_primary_spread 0
+set g_balance_fireball_secondary_animtime 0.3
+set g_balance_fireball_secondary_damage 40
+set g_balance_fireball_secondary_damageforcescale 4
+set g_balance_fireball_secondary_damagetime 5
+set g_balance_fireball_secondary_laserburntime 0.5
+set g_balance_fireball_secondary_laserdamage 50
+set g_balance_fireball_secondary_laseredgedamage 20
+set g_balance_fireball_secondary_laserradius 110
+set g_balance_fireball_secondary_lifetime 7
+set g_balance_fireball_secondary_refire 1.5
+set g_balance_fireball_secondary_speed 900
+set g_balance_fireball_secondary_speed_up 100
+set g_balance_fireball_secondary_speed_z 0
+set g_balance_fireball_secondary_spread 0
+set g_balance_fireball_switchdelay_drop 0.2
+set g_balance_fireball_switchdelay_raise 0.2
+set g_balance_fireball_weaponreplace ""
+set g_balance_fireball_weaponstart 0
+set g_balance_fireball_weaponstartoverride -1
+set g_balance_fireball_weaponthrowable 0
+// }}}
+// {{{ #18: T.A.G. Seeker (MUTATOR WEAPON)
+set g_balance_seeker_flac_ammo 1
+set g_balance_seeker_flac_animtime 0.1
+set g_balance_seeker_flac_damage 15
+set g_balance_seeker_flac_edgedamage 10
+set g_balance_seeker_flac_force 50
+set g_balance_seeker_flac_lifetime 0.1
+set g_balance_seeker_flac_lifetime_rand 0.05
+set g_balance_seeker_flac_radius 100
+set g_balance_seeker_flac_refire 0.1
+set g_balance_seeker_flac_speed 3000
+set g_balance_seeker_flac_speed_up 1000
+set g_balance_seeker_flac_speed_z 0
+set g_balance_seeker_flac_spread 0.4
+set g_balance_seeker_missile_accel 1400
+set g_balance_seeker_missile_ammo 2
+set g_balance_seeker_missile_animtime 0.2
+set g_balance_seeker_missile_count 3
+set g_balance_seeker_missile_damage 30
+set g_balance_seeker_missile_damageforcescale 4
+set g_balance_seeker_missile_decel 1400
+set g_balance_seeker_missile_delay 0.25
+set g_balance_seeker_missile_edgedamage 10
+set g_balance_seeker_missile_force 150
+set g_balance_seeker_missile_health 5
+set g_balance_seeker_missile_lifetime 15
+set g_balance_seeker_missile_proxy 0
+set g_balance_seeker_missile_proxy_delay 0.2
+set g_balance_seeker_missile_proxy_maxrange 45
+set g_balance_seeker_missile_radius 80
+set g_balance_seeker_missile_refire 0.5
+set g_balance_seeker_missile_smart 1
+set g_balance_seeker_missile_smart_mindist 800
+set g_balance_seeker_missile_smart_trace_max 2500
+set g_balance_seeker_missile_smart_trace_min 1000
+set g_balance_seeker_missile_speed 700
+set g_balance_seeker_missile_speed_max 1300
+set g_balance_seeker_missile_speed_up 300
+set g_balance_seeker_missile_speed_z 0
+set g_balance_seeker_missile_spread 0
+set g_balance_seeker_missile_turnrate 0.65
+set g_balance_seeker_reload_ammo 0
+set g_balance_seeker_reload_time 2
+set g_balance_seeker_switchdelay_drop 0.2
+set g_balance_seeker_switchdelay_raise 0.2
+set g_balance_seeker_tag_ammo 1
+set g_balance_seeker_tag_animtime 0.2
+set g_balance_seeker_tag_damageforcescale 4
+set g_balance_seeker_tag_health 5
+set g_balance_seeker_tag_lifetime 15
+set g_balance_seeker_tag_refire 0.75
+set g_balance_seeker_tag_speed 5000
+set g_balance_seeker_tag_spread 0
+set g_balance_seeker_tag_tracker_lifetime 10
+set g_balance_seeker_type 0
+set g_balance_seeker_weaponreplace ""
+set g_balance_seeker_weaponstart 0
+set g_balance_seeker_weaponstartoverride -1
+set g_balance_seeker_weaponthrowable 1
+// }}}
+// {{{ #19: Shockwave (MUTATOR WEAPON)
+set g_balance_shockwave_blast_animtime 0.3
+set g_balance_shockwave_blast_damage 40
+set g_balance_shockwave_blast_distance 1000
+set g_balance_shockwave_blast_edgedamage 0
+set g_balance_shockwave_blast_force 15
+set g_balance_shockwave_blast_force_forwardbias 50
+set g_balance_shockwave_blast_force_zscale 1
+set g_balance_shockwave_blast_jump_damage 20
+set g_balance_shockwave_blast_jump_edgedamage 0
+set g_balance_shockwave_blast_jump_force 100
+set g_balance_shockwave_blast_jump_force_velocitybias 1
+set g_balance_shockwave_blast_jump_force_zscale 1
+set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5
+set g_balance_shockwave_blast_jump_multiplier_distance 0.5
+set g_balance_shockwave_blast_jump_multiplier_min 0
+set g_balance_shockwave_blast_jump_radius 150
+set g_balance_shockwave_blast_multiplier_accuracy 0.45
+set g_balance_shockwave_blast_multiplier_distance 0.2
+set g_balance_shockwave_blast_multiplier_min 0
+set g_balance_shockwave_blast_refire 0.75
+set g_balance_shockwave_blast_splash_damage 15
+set g_balance_shockwave_blast_splash_edgedamage 0
+set g_balance_shockwave_blast_splash_force 100
+set g_balance_shockwave_blast_splash_force_forwardbias 50
+set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5
+set g_balance_shockwave_blast_splash_multiplier_distance 0.5
+set g_balance_shockwave_blast_splash_multiplier_min 0
+set g_balance_shockwave_blast_splash_radius 70
+set g_balance_shockwave_blast_spread_max 120
+set g_balance_shockwave_blast_spread_min 25
+set g_balance_shockwave_melee_animtime 1.3
+set g_balance_shockwave_melee_damage 80
+set g_balance_shockwave_melee_delay 0.25
+set g_balance_shockwave_melee_force 200
+set g_balance_shockwave_melee_multihit 1
+set g_balance_shockwave_melee_no_doubleslap 1
+set g_balance_shockwave_melee_nonplayerdamage 40
+set g_balance_shockwave_melee_range 120
+set g_balance_shockwave_melee_refire 1.25
+set g_balance_shockwave_melee_swing_side 120
+set g_balance_shockwave_melee_swing_up 30
+set g_balance_shockwave_melee_time 0.15
+set g_balance_shockwave_melee_traces 10
+set g_balance_shockwave_switchdelay_drop 0.2
+set g_balance_shockwave_switchdelay_raise 0.2
+set g_balance_shockwave_weaponreplace ""
+set g_balance_shockwave_weaponstart 0
+set g_balance_shockwave_weaponstartoverride -1
+set g_balance_shockwave_weaponthrowable 0
+// }}}
+// {{{ #20: Arc
+set g_balance_arc_beam_ammo 6
+set g_balance_arc_beam_animtime 0.1
+set g_balance_arc_beam_botaimlifetime 0
+set g_balance_arc_beam_botaimspeed 0
+set g_balance_arc_beam_damage 100
+set g_balance_arc_beam_degreespersegment 1
+set g_balance_arc_beam_distancepersegment 0
+set g_balance_arc_beam_falloff_halflifedist 0
+set g_balance_arc_beam_falloff_maxdist 0
+set g_balance_arc_beam_falloff_mindist 0
+set g_balance_arc_beam_force 600
+set g_balance_arc_beam_healing_amax 0
+set g_balance_arc_beam_healing_aps 50
+set g_balance_arc_beam_healing_hmax 150
+set g_balance_arc_beam_healing_hps 50
+set g_balance_arc_cooldown 2.5
+set g_balance_arc_cooldown_release 0
+set g_balance_arc_overheat_max 5
+set g_balance_arc_overheat_min 3
+set g_balance_arc_beam_heat 0
+set g_balance_arc_burst_heat 5
+set g_balance_arc_beam_maxangle 10
+set g_balance_arc_beam_nonplayerdamage 80
+set g_balance_arc_beam_range 1000
+set g_balance_arc_beam_refire 0.25
+set g_balance_arc_beam_returnspeed 8
+set g_balance_arc_beam_tightness 0.5
+set g_balance_arc_bolt 1
+set g_balance_arc_bolt_ammo 1
+set g_balance_arc_bolt_damage 25
+set g_balance_arc_bolt_damageforcescale 0
+set g_balance_arc_bolt_edgedamage 12.5
+set g_balance_arc_bolt_force 120
+set g_balance_arc_bolt_health 15
+set g_balance_arc_bolt_lifetime 5
+set g_balance_arc_bolt_radius 65
+set g_balance_arc_bolt_refire 0.16667
+set g_balance_arc_bolt_speed 2300
+set g_balance_arc_bolt_spread 0
+set g_balance_arc_burst_ammo 15
+set g_balance_arc_burst_damage 250
+set g_balance_arc_burst_healing_aps 100
+set g_balance_arc_burst_healing_hps 100
+set g_balance_arc_switchdelay_drop 0.2
+set g_balance_arc_switchdelay_raise 0.2
+set g_balance_arc_weaponreplace ""
+set g_balance_arc_weaponstart 0
+set g_balance_arc_weaponstartoverride -1
+set g_balance_arc_weaponthrowable 1
+// }}}
+// {{{ #21: Heavy Machine Gun
+set g_balance_hmg_ammo 1
+set g_balance_hmg_damage 10
+set g_balance_hmg_force 5
+set g_balance_hmg_refire 0.05
+set g_balance_hmg_reload_ammo 120
+set g_balance_hmg_reload_time 1
+set g_balance_hmg_solidpenetration 32
+set g_balance_hmg_spread_add 0.01
+set g_balance_hmg_spread_max 0.05
+set g_balance_hmg_spread_min 0.02
+set g_balance_hmg_switchdelay_drop 0.2
+set g_balance_hmg_switchdelay_raise 0.2
+set g_balance_hmg_weaponreplace ""
+set g_balance_hmg_weaponstart 0
+set g_balance_hmg_weaponstartoverride 0
+set g_balance_hmg_weaponthrowable 0
+// }}}
+// {{{ #22: Rocket Propelled Chainsaw
+set g_balance_rpc_ammo 10
+set g_balance_rpc_animtime 1
+set g_balance_rpc_damage 150
+set g_balance_rpc_damage2 500
+set g_balance_rpc_damageforcescale 2
+set g_balance_rpc_edgedamage 50
+set g_balance_rpc_force 400
+set g_balance_rpc_health 25
+set g_balance_rpc_lifetime 30
+set g_balance_rpc_radius 300
+set g_balance_rpc_refire 1
+set g_balance_rpc_reload_ammo 10
+set g_balance_rpc_reload_time 1
+set g_balance_rpc_speed 1250
+set g_balance_rpc_speedaccel 5000
+set g_balance_rpc_switchdelay_drop 0.2
+set g_balance_rpc_switchdelay_raise 0.2
+set g_balance_rpc_weaponreplace ""
+set g_balance_rpc_weaponstart 0
+set g_balance_rpc_weaponstartoverride 0
+set g_balance_rpc_weaponthrowable 0
+// }}}
index 97707a7255789ab49f96602d9b5a5373a8f673d1..41c9081b92d80a0df3b7252dbf1a65fa7c385e83 100644 (file)
@@ -219,6 +219,7 @@ set g_balance_electro_secondary_speed 900
 set g_balance_electro_secondary_speed_up 200
 set g_balance_electro_secondary_speed_z 0
 set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
 set g_balance_electro_secondary_touchexplode 0
 set g_balance_electro_switchdelay_drop 0.15
 set g_balance_electro_switchdelay_raise 0.15
@@ -398,6 +399,7 @@ set g_balance_devastator_remote_damage 105
 set g_balance_devastator_remote_edgedamage 40
 set g_balance_devastator_remote_force 600
 set g_balance_devastator_remote_jump_damage 70
+set g_balance_devastator_remote_jump_force 0
 set g_balance_devastator_remote_jump_radius 0
 set g_balance_devastator_remote_jump_velocity_z_add 400
 set g_balance_devastator_remote_jump_velocity_z_max 1500
@@ -720,7 +722,7 @@ set g_balance_shockwave_melee_traces 10
 set g_balance_shockwave_switchdelay_drop 0.15
 set g_balance_shockwave_switchdelay_raise 0.15
 set g_balance_shockwave_weaponreplace ""
-set g_balance_shockwave_weaponstart 1
+set g_balance_shockwave_weaponstart 0
 set g_balance_shockwave_weaponstartoverride -1
 set g_balance_shockwave_weaponthrowable 0
 // }}}
index d0cfa361d269c1aae66c89cefc2e3e660f65dd24..82609a76ca5ed4158e0c210b839025792de15e96 100644 (file)
@@ -84,7 +84,7 @@ set g_balance_machinegun_reload_time 1.5
 set g_balance_machinegun_solidpenetration 13.1
 set g_balance_machinegun_spread_add 0.012
 set g_balance_machinegun_spread_max 0.05
-set g_balance_machinegun_spread_min 0.02
+set g_balance_machinegun_spread_min 0
 set g_balance_machinegun_sustained_ammo 1
 set g_balance_machinegun_sustained_damage 25
 set g_balance_machinegun_sustained_force 5
@@ -219,6 +219,7 @@ set g_balance_electro_secondary_speed 1000
 set g_balance_electro_secondary_speed_up 200
 set g_balance_electro_secondary_speed_z 0
 set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
 set g_balance_electro_secondary_touchexplode 0
 set g_balance_electro_switchdelay_drop 0.2
 set g_balance_electro_switchdelay_raise 0.2
@@ -301,7 +302,7 @@ set g_balance_vortex_charge_shot_multiplier 0
 set g_balance_vortex_charge_start 0.5
 set g_balance_vortex_charge_velocity_rate 0
 set g_balance_vortex_primary_ammo 10
-set g_balance_vortex_primary_animtime 0.95
+set g_balance_vortex_primary_animtime 0.65
 set g_balance_vortex_primary_damage 100
 set g_balance_vortex_primary_damagefalloff_forcehalflife 0
 set g_balance_vortex_primary_damagefalloff_halflife 0
@@ -324,8 +325,8 @@ set g_balance_vortex_secondary_damagefalloff_maxdist 0
 set g_balance_vortex_secondary_damagefalloff_mindist 0
 set g_balance_vortex_secondary_force 0
 set g_balance_vortex_secondary_refire 0
-set g_balance_vortex_switchdelay_drop 0.25
-set g_balance_vortex_switchdelay_raise 0.25
+set g_balance_vortex_switchdelay_drop 0.2
+set g_balance_vortex_switchdelay_raise 0.2
 set g_balance_vortex_weaponreplace ""
 set g_balance_vortex_weaponstart 0
 set g_balance_vortex_weaponstartoverride -1
@@ -398,6 +399,7 @@ set g_balance_devastator_remote_damage 70
 set g_balance_devastator_remote_edgedamage 35
 set g_balance_devastator_remote_force 300
 set g_balance_devastator_remote_jump_damage 70
+set g_balance_devastator_remote_jump_force 0
 set g_balance_devastator_remote_jump_radius 0
 set g_balance_devastator_remote_jump_velocity_z_add 400
 set g_balance_devastator_remote_jump_velocity_z_max 1500
@@ -776,15 +778,15 @@ set g_balance_arc_weaponthrowable 1
 // }}}
 // {{{ #21: Heavy Machine Gun
 set g_balance_hmg_ammo 1
-set g_balance_hmg_damage 10
-set g_balance_hmg_force 5
+set g_balance_hmg_damage 30
+set g_balance_hmg_force 10
 set g_balance_hmg_refire 0.05
 set g_balance_hmg_reload_ammo 120
 set g_balance_hmg_reload_time 1
 set g_balance_hmg_solidpenetration 32
-set g_balance_hmg_spread_add 0.01
-set g_balance_hmg_spread_max 0.05
-set g_balance_hmg_spread_min 0.02
+set g_balance_hmg_spread_add 0.005
+set g_balance_hmg_spread_max 0.06
+set g_balance_hmg_spread_min 0.01
 set g_balance_hmg_switchdelay_drop 0.2
 set g_balance_hmg_switchdelay_raise 0.2
 set g_balance_hmg_weaponreplace ""
index af2234f0d6c898c02d110d98273a46019bdb7c9d..99edec78b8da1c543470adffdd3a155e967f6a5a 100644 (file)
@@ -234,6 +234,7 @@ set g_balance_electro_secondary_speed 1000
 set g_balance_electro_secondary_speed_up 200
 set g_balance_electro_secondary_speed_z 0
 set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_stick 0
 set g_balance_electro_secondary_touchexplode 0
 set g_balance_electro_switchdelay_drop 0.2
 set g_balance_electro_switchdelay_raise 0.2
@@ -464,6 +465,7 @@ set g_balance_devastator_remote_damage 70
 set g_balance_devastator_remote_edgedamage 35
 set g_balance_devastator_remote_force 400
 set g_balance_devastator_remote_jump_damage 40
+set g_balance_devastator_remote_jump_force 0
 set g_balance_devastator_remote_jump_radius 200
 set g_balance_devastator_remote_jump_velocity_z_add 500
 set g_balance_devastator_remote_jump_velocity_z_max 1500
index 931a63c6a2264d068110f90817f63be68fc5ac64..95ac919b6a9e5d3c5d74e4a92771d47d1c9a5768 100644 (file)
@@ -219,6 +219,7 @@ set g_balance_electro_secondary_speed 900
 set g_balance_electro_secondary_speed_up 200
 set g_balance_electro_secondary_speed_z 0
 set g_balance_electro_secondary_spread 0.05
+set g_balance_electro_secondary_stick 0
 set g_balance_electro_secondary_touchexplode 0
 set g_balance_electro_switchdelay_drop 0
 set g_balance_electro_switchdelay_raise 0
@@ -398,6 +399,7 @@ set g_balance_devastator_remote_damage 70
 set g_balance_devastator_remote_edgedamage 35
 set g_balance_devastator_remote_force 350
 set g_balance_devastator_remote_jump_damage 70
+set g_balance_devastator_remote_jump_force 0
 set g_balance_devastator_remote_jump_radius 0
 set g_balance_devastator_remote_jump_velocity_z_add 400
 set g_balance_devastator_remote_jump_velocity_z_max 1500
index 8d70876901f7da76e45d8f9f1e05a877e2dcbea5..898c3b3924a7d0b97f80eb9b478268e652576e03 100644 (file)
@@ -193,7 +193,7 @@ set g_balance_electro_primary_force 200
 set g_balance_electro_primary_lifetime 5
 set g_balance_electro_primary_midaircombo_explode 1
 set g_balance_electro_primary_midaircombo_interval 0.1
-set g_balance_electro_primary_midaircombo_radius 150
+set g_balance_electro_primary_midaircombo_radius 0
 set g_balance_electro_primary_radius 100
 set g_balance_electro_primary_refire 0.6
 set g_balance_electro_primary_speed 2500
@@ -218,7 +218,8 @@ set g_balance_electro_secondary_refire2 1.6
 set g_balance_electro_secondary_speed 1000
 set g_balance_electro_secondary_speed_up 200
 set g_balance_electro_secondary_speed_z 0
-set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_spread 0
+set g_balance_electro_secondary_stick 0
 set g_balance_electro_secondary_touchexplode 1
 set g_balance_electro_switchdelay_drop 0.2
 set g_balance_electro_switchdelay_raise 0.2
@@ -324,8 +325,8 @@ set g_balance_vortex_secondary_damagefalloff_maxdist 0
 set g_balance_vortex_secondary_damagefalloff_mindist 0
 set g_balance_vortex_secondary_force 0
 set g_balance_vortex_secondary_refire 0
-set g_balance_vortex_switchdelay_drop 0.25
-set g_balance_vortex_switchdelay_raise 0.25
+set g_balance_vortex_switchdelay_drop 0.2
+set g_balance_vortex_switchdelay_raise 0.2
 set g_balance_vortex_weaponreplace ""
 set g_balance_vortex_weaponstart 0
 set g_balance_vortex_weaponstartoverride -1
@@ -367,7 +368,7 @@ set g_balance_hagar_secondary_load_spread_bias 0.5
 set g_balance_hagar_secondary_radius 80
 set g_balance_hagar_secondary_refire 0.5
 set g_balance_hagar_secondary_speed 2000
-set g_balance_hagar_secondary_spread 0.05
+set g_balance_hagar_secondary_spread 0
 set g_balance_hagar_switchdelay_drop 0.2
 set g_balance_hagar_switchdelay_raise 0.2
 set g_balance_hagar_weaponreplace ""
@@ -398,8 +399,9 @@ set g_balance_devastator_remote_damage 70
 set g_balance_devastator_remote_edgedamage 35
 set g_balance_devastator_remote_force 300
 set g_balance_devastator_remote_jump_damage 70
+set g_balance_devastator_remote_jump_force 450
 set g_balance_devastator_remote_jump_radius 0
-set g_balance_devastator_remote_jump_velocity_z_add 400
+set g_balance_devastator_remote_jump_velocity_z_add 0
 set g_balance_devastator_remote_jump_velocity_z_max 1500
 set g_balance_devastator_remote_jump_velocity_z_min 400
 set g_balance_devastator_remote_radius 110
@@ -454,7 +456,7 @@ set g_balance_vaporizer_switchdelay_raise 0.2
 set g_balance_vaporizer_weaponreplace ""
 set g_balance_vaporizer_weaponstart 0
 set g_balance_vaporizer_weaponstartoverride -1
-set g_balance_vaporizer_weaponthrowable 0
+set g_balance_vaporizer_weaponthrowable 1
 // }}}
 // {{{ #13: Grappling Hook
 set g_balance_hook_primary_ammo 5
@@ -675,23 +677,23 @@ set g_balance_seeker_weaponthrowable 1
 // }}}
 // {{{ #19: Shockwave (MUTATOR WEAPON)
 set g_balance_shockwave_blast_animtime 0.3
-set g_balance_shockwave_blast_damage 20
+set g_balance_shockwave_blast_damage 40
 set g_balance_shockwave_blast_distance 1000
 set g_balance_shockwave_blast_edgedamage 0
-set g_balance_shockwave_blast_force 200
+set g_balance_shockwave_blast_force 15
 set g_balance_shockwave_blast_force_forwardbias 50
-set g_balance_shockwave_blast_force_zscale 2
+set g_balance_shockwave_blast_force_zscale 1
 set g_balance_shockwave_blast_jump_damage 20
 set g_balance_shockwave_blast_jump_edgedamage 0
-set g_balance_shockwave_blast_jump_force 300
-set g_balance_shockwave_blast_jump_force_velocitybias 0
-set g_balance_shockwave_blast_jump_force_zscale 1.25
+set g_balance_shockwave_blast_jump_force 100
+set g_balance_shockwave_blast_jump_force_velocitybias 1
+set g_balance_shockwave_blast_jump_force_zscale 1
 set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5
 set g_balance_shockwave_blast_jump_multiplier_distance 0.5
 set g_balance_shockwave_blast_jump_multiplier_min 0
 set g_balance_shockwave_blast_jump_radius 150
-set g_balance_shockwave_blast_multiplier_accuracy 0.5
-set g_balance_shockwave_blast_multiplier_distance 0.5
+set g_balance_shockwave_blast_multiplier_accuracy 0.45
+set g_balance_shockwave_blast_multiplier_distance 0.2
 set g_balance_shockwave_blast_multiplier_min 0
 set g_balance_shockwave_blast_refire 0.75
 set g_balance_shockwave_blast_splash_damage 15
@@ -726,16 +728,16 @@ set g_balance_shockwave_weaponthrowable 0
 // }}}
 // {{{ #20: Arc
 set g_balance_arc_beam_ammo 6
-set g_balance_arc_beam_animtime 0.2
+set g_balance_arc_beam_animtime 0.1
 set g_balance_arc_beam_botaimlifetime 0
 set g_balance_arc_beam_botaimspeed 0
-set g_balance_arc_beam_damage 115
+set g_balance_arc_beam_damage 100
 set g_balance_arc_beam_degreespersegment 1
 set g_balance_arc_beam_distancepersegment 0
 set g_balance_arc_beam_falloff_halflifedist 0
 set g_balance_arc_beam_falloff_maxdist 0
 set g_balance_arc_beam_falloff_mindist 0
-set g_balance_arc_beam_force 900
+set g_balance_arc_beam_force 600
 set g_balance_arc_beam_healing_amax 0
 set g_balance_arc_beam_healing_aps 50
 set g_balance_arc_beam_healing_hmax 150
@@ -744,8 +746,8 @@ set g_balance_arc_cooldown 2.5
 set g_balance_arc_cooldown_release 0
 set g_balance_arc_overheat_max 5
 set g_balance_arc_overheat_min 3
-set g_balance_arc_beam_heat 0.75
-set g_balance_arc_burst_heat 4
+set g_balance_arc_beam_heat 0
+set g_balance_arc_burst_heat 5
 set g_balance_arc_beam_maxangle 10
 set g_balance_arc_beam_nonplayerdamage 80
 set g_balance_arc_beam_range 1000
@@ -762,7 +764,7 @@ set g_balance_arc_bolt_health 15
 set g_balance_arc_bolt_lifetime 5
 set g_balance_arc_bolt_radius 65
 set g_balance_arc_bolt_refire 0.16667
-set g_balance_arc_bolt_speed 2200
+set g_balance_arc_bolt_speed 2300
 set g_balance_arc_bolt_spread 0
 set g_balance_arc_burst_ammo 15
 set g_balance_arc_burst_damage 250
index 8d70876901f7da76e45d8f9f1e05a877e2dcbea5..898c3b3924a7d0b97f80eb9b478268e652576e03 100644 (file)
@@ -193,7 +193,7 @@ set g_balance_electro_primary_force 200
 set g_balance_electro_primary_lifetime 5
 set g_balance_electro_primary_midaircombo_explode 1
 set g_balance_electro_primary_midaircombo_interval 0.1
-set g_balance_electro_primary_midaircombo_radius 150
+set g_balance_electro_primary_midaircombo_radius 0
 set g_balance_electro_primary_radius 100
 set g_balance_electro_primary_refire 0.6
 set g_balance_electro_primary_speed 2500
@@ -218,7 +218,8 @@ set g_balance_electro_secondary_refire2 1.6
 set g_balance_electro_secondary_speed 1000
 set g_balance_electro_secondary_speed_up 200
 set g_balance_electro_secondary_speed_z 0
-set g_balance_electro_secondary_spread 0.04
+set g_balance_electro_secondary_spread 0
+set g_balance_electro_secondary_stick 0
 set g_balance_electro_secondary_touchexplode 1
 set g_balance_electro_switchdelay_drop 0.2
 set g_balance_electro_switchdelay_raise 0.2
@@ -324,8 +325,8 @@ set g_balance_vortex_secondary_damagefalloff_maxdist 0
 set g_balance_vortex_secondary_damagefalloff_mindist 0
 set g_balance_vortex_secondary_force 0
 set g_balance_vortex_secondary_refire 0
-set g_balance_vortex_switchdelay_drop 0.25
-set g_balance_vortex_switchdelay_raise 0.25
+set g_balance_vortex_switchdelay_drop 0.2
+set g_balance_vortex_switchdelay_raise 0.2
 set g_balance_vortex_weaponreplace ""
 set g_balance_vortex_weaponstart 0
 set g_balance_vortex_weaponstartoverride -1
@@ -367,7 +368,7 @@ set g_balance_hagar_secondary_load_spread_bias 0.5
 set g_balance_hagar_secondary_radius 80
 set g_balance_hagar_secondary_refire 0.5
 set g_balance_hagar_secondary_speed 2000
-set g_balance_hagar_secondary_spread 0.05
+set g_balance_hagar_secondary_spread 0
 set g_balance_hagar_switchdelay_drop 0.2
 set g_balance_hagar_switchdelay_raise 0.2
 set g_balance_hagar_weaponreplace ""
@@ -398,8 +399,9 @@ set g_balance_devastator_remote_damage 70
 set g_balance_devastator_remote_edgedamage 35
 set g_balance_devastator_remote_force 300
 set g_balance_devastator_remote_jump_damage 70
+set g_balance_devastator_remote_jump_force 450
 set g_balance_devastator_remote_jump_radius 0
-set g_balance_devastator_remote_jump_velocity_z_add 400
+set g_balance_devastator_remote_jump_velocity_z_add 0
 set g_balance_devastator_remote_jump_velocity_z_max 1500
 set g_balance_devastator_remote_jump_velocity_z_min 400
 set g_balance_devastator_remote_radius 110
@@ -454,7 +456,7 @@ set g_balance_vaporizer_switchdelay_raise 0.2
 set g_balance_vaporizer_weaponreplace ""
 set g_balance_vaporizer_weaponstart 0
 set g_balance_vaporizer_weaponstartoverride -1
-set g_balance_vaporizer_weaponthrowable 0
+set g_balance_vaporizer_weaponthrowable 1
 // }}}
 // {{{ #13: Grappling Hook
 set g_balance_hook_primary_ammo 5
@@ -675,23 +677,23 @@ set g_balance_seeker_weaponthrowable 1
 // }}}
 // {{{ #19: Shockwave (MUTATOR WEAPON)
 set g_balance_shockwave_blast_animtime 0.3
-set g_balance_shockwave_blast_damage 20
+set g_balance_shockwave_blast_damage 40
 set g_balance_shockwave_blast_distance 1000
 set g_balance_shockwave_blast_edgedamage 0
-set g_balance_shockwave_blast_force 200
+set g_balance_shockwave_blast_force 15
 set g_balance_shockwave_blast_force_forwardbias 50
-set g_balance_shockwave_blast_force_zscale 2
+set g_balance_shockwave_blast_force_zscale 1
 set g_balance_shockwave_blast_jump_damage 20
 set g_balance_shockwave_blast_jump_edgedamage 0
-set g_balance_shockwave_blast_jump_force 300
-set g_balance_shockwave_blast_jump_force_velocitybias 0
-set g_balance_shockwave_blast_jump_force_zscale 1.25
+set g_balance_shockwave_blast_jump_force 100
+set g_balance_shockwave_blast_jump_force_velocitybias 1
+set g_balance_shockwave_blast_jump_force_zscale 1
 set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5
 set g_balance_shockwave_blast_jump_multiplier_distance 0.5
 set g_balance_shockwave_blast_jump_multiplier_min 0
 set g_balance_shockwave_blast_jump_radius 150
-set g_balance_shockwave_blast_multiplier_accuracy 0.5
-set g_balance_shockwave_blast_multiplier_distance 0.5
+set g_balance_shockwave_blast_multiplier_accuracy 0.45
+set g_balance_shockwave_blast_multiplier_distance 0.2
 set g_balance_shockwave_blast_multiplier_min 0
 set g_balance_shockwave_blast_refire 0.75
 set g_balance_shockwave_blast_splash_damage 15
@@ -726,16 +728,16 @@ set g_balance_shockwave_weaponthrowable 0
 // }}}
 // {{{ #20: Arc
 set g_balance_arc_beam_ammo 6
-set g_balance_arc_beam_animtime 0.2
+set g_balance_arc_beam_animtime 0.1
 set g_balance_arc_beam_botaimlifetime 0
 set g_balance_arc_beam_botaimspeed 0
-set g_balance_arc_beam_damage 115
+set g_balance_arc_beam_damage 100
 set g_balance_arc_beam_degreespersegment 1
 set g_balance_arc_beam_distancepersegment 0
 set g_balance_arc_beam_falloff_halflifedist 0
 set g_balance_arc_beam_falloff_maxdist 0
 set g_balance_arc_beam_falloff_mindist 0
-set g_balance_arc_beam_force 900
+set g_balance_arc_beam_force 600
 set g_balance_arc_beam_healing_amax 0
 set g_balance_arc_beam_healing_aps 50
 set g_balance_arc_beam_healing_hmax 150
@@ -744,8 +746,8 @@ set g_balance_arc_cooldown 2.5
 set g_balance_arc_cooldown_release 0
 set g_balance_arc_overheat_max 5
 set g_balance_arc_overheat_min 3
-set g_balance_arc_beam_heat 0.75
-set g_balance_arc_burst_heat 4
+set g_balance_arc_beam_heat 0
+set g_balance_arc_burst_heat 5
 set g_balance_arc_beam_maxangle 10
 set g_balance_arc_beam_nonplayerdamage 80
 set g_balance_arc_beam_range 1000
@@ -762,7 +764,7 @@ set g_balance_arc_bolt_health 15
 set g_balance_arc_bolt_lifetime 5
 set g_balance_arc_bolt_radius 65
 set g_balance_arc_bolt_refire 0.16667
-set g_balance_arc_bolt_speed 2200
+set g_balance_arc_bolt_speed 2300
 set g_balance_arc_bolt_spread 0
 set g_balance_arc_burst_ammo 15
 set g_balance_arc_burst_damage 250
diff --git a/balance-mario.cfg b/balance-mario.cfg
new file mode 100644 (file)
index 0000000..d290be0
--- /dev/null
@@ -0,0 +1,235 @@
+g_mod_balance Mario
+
+// {{{ starting gear
+set g_balance_health_start 100
+set g_balance_armor_start 0
+set g_start_ammo_shells 15
+set g_start_ammo_nails 0
+set g_start_ammo_rockets 0
+set g_start_ammo_cells 0
+set g_start_ammo_plasma 0
+set g_start_ammo_fuel 0
+set g_warmup_start_health 100 "starting values when being in warmup-stage"
+set g_warmup_start_armor 100 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_plasma 90 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
+set g_lms_start_health 200
+set g_lms_start_armor 200
+set g_lms_start_ammo_shells 60
+set g_lms_start_ammo_nails 320
+set g_lms_start_ammo_rockets 160
+set g_lms_start_ammo_cells 180
+set g_lms_start_ammo_plasma 180
+set g_lms_start_ammo_fuel 0
+set g_balance_nix_roundtime 25
+set g_balance_nix_incrtime 1.6
+set g_balance_nix_ammo_shells 60
+set g_balance_nix_ammo_nails 320
+set g_balance_nix_ammo_rockets 160
+set g_balance_nix_ammo_cells 180
+set g_balance_nix_ammo_plasma 180
+set g_balance_nix_ammo_fuel 100
+set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
+set g_balance_nix_ammoincr_nails 6
+set g_balance_nix_ammoincr_rockets 2
+set g_balance_nix_ammoincr_cells 2
+set g_balance_nix_ammoincr_plasma 2
+set g_balance_nix_ammoincr_fuel 2
+// }}}
+
+// {{{ pickup items
+set g_pickup_ammo_anyway 1
+set g_pickup_weapons_anyway 1
+set g_pickup_shells 15
+set g_pickup_shells_weapon 15
+set g_pickup_shells_max 60
+set g_pickup_nails 80
+set g_pickup_nails_weapon 80
+set g_pickup_nails_max 320
+set g_pickup_rockets 40
+set g_pickup_rockets_weapon 40
+set g_pickup_rockets_max 160
+set g_pickup_cells 30
+set g_pickup_cells_weapon 30
+set g_pickup_cells_max 180
+set g_pickup_plasma 30
+set g_pickup_plasma_weapon 30
+set g_pickup_plasma_max 180
+set g_pickup_fuel 50
+set g_pickup_fuel_weapon 50
+set g_pickup_fuel_jetpack 100
+set g_pickup_fuel_max 100
+set g_pickup_armorsmall 5
+set g_pickup_armorsmall_max 200
+set g_pickup_armorsmall_anyway 1
+set g_pickup_armormedium 25
+set g_pickup_armormedium_max 200
+set g_pickup_armormedium_anyway 1
+set g_pickup_armorbig 50
+set g_pickup_armorbig_max 200
+set g_pickup_armorbig_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
+set g_pickup_healthsmall 5
+set g_pickup_healthsmall_max 200
+set g_pickup_healthsmall_anyway 1
+set g_pickup_healthmedium 25
+set g_pickup_healthmedium_max 200
+set g_pickup_healthmedium_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
+set g_pickup_healthmega 100
+set g_pickup_healthmega_max 200
+set g_pickup_healthmega_anyway 1
+set g_pickup_respawntime_short 15
+set g_pickup_respawntime_medium 20
+set g_pickup_respawntime_long 30
+set g_pickup_respawntime_powerup 120
+set g_pickup_respawntime_weapon 10
+set g_pickup_respawntime_superweapon 120
+set g_pickup_respawntime_ammo 10
+set g_pickup_respawntimejitter_short 0
+set g_pickup_respawntimejitter_medium 0
+set g_pickup_respawntimejitter_long 0
+set g_pickup_respawntimejitter_powerup 0
+set g_pickup_respawntimejitter_weapon 0
+set g_pickup_respawntimejitter_superweapon 10
+set g_pickup_respawntimejitter_ammo 0
+// }}}
+
+// {{{ regen/rot
+set g_balance_health_regen 0.08
+set g_balance_health_regenlinear 0.5
+set g_balance_pause_health_regen 5
+set g_balance_pause_health_regen_spawn 0
+set g_balance_health_rot 0.02
+set g_balance_health_rotlinear 1
+set g_balance_pause_health_rot 1
+set g_balance_pause_health_rot_spawn 5
+set g_balance_health_regenstable 100
+set g_balance_health_rotstable 100
+set g_balance_health_limit 999
+set g_balance_armor_regen 0
+set g_balance_armor_regenlinear 0
+set g_balance_armor_rot 0.02
+set g_balance_armor_rotlinear 1
+set g_balance_pause_armor_rot 1
+set g_balance_pause_armor_rot_spawn 5
+set g_balance_armor_regenstable 100
+set g_balance_armor_rotstable 100
+set g_balance_armor_limit 999
+set g_balance_armor_blockpercent 0.7
+set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
+set g_balance_fuel_regenlinear 0
+set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
+set g_balance_fuel_rot 0.05
+set g_balance_fuel_rotlinear 0
+set g_balance_pause_fuel_rot 5
+set g_balance_pause_fuel_rot_spawn 10
+set g_balance_fuel_regenstable 50
+set g_balance_fuel_rotstable 100
+set g_balance_fuel_limit 999
+// }}}
+
+// {{{ misc
+set g_balance_selfdamagepercent 0.65
+set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
+set g_weaponratefactor 1 "weapon fire rate multiplier"
+set g_weapondamagefactor 1 "weapon damage multiplier"
+set g_weaponforcefactor 1 "weapon force multiplier"
+set g_weaponspreadfactor 1 "weapon spread multiplier"
+set g_balance_firetransfer_time 0.9
+set g_balance_firetransfer_damage 0.8
+set g_throughfloor_damage 0.75
+set g_throughfloor_force 0.75
+set g_projectiles_damage -2
+// possible values:
+// -2: absolutely no damage to projectiles (no exceptions)
+// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
+// 0: only damage from contents (lava/slime) or exceptions
+// 1: only self damage or damage from contents or exceptions
+// 2: allow all damage to projectiles normally
+set g_projectiles_keep_owner 1
+set g_projectiles_newton_style 0
+// possible values:
+// 0: absolute velocity projectiles (like Quake)
+// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
+// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
+set g_projectiles_newton_style_2_minfactor 0.8
+set g_projectiles_newton_style_2_maxfactor 1.5
+set g_projectiles_spread_style 7
+// possible values:
+// 0: forward + solid sphere (like Quake) - varies velocity
+// 1: forward + flattened solid sphere
+// 2: forward + solid circle
+// 3: forward + normal distribution 3D - varies velocity
+// 4: forward + normal distribution on a plane
+// 5: forward + circle with 1-r falloff
+// 6: forward + circle with 1-r^2 falloff
+// 7: forward + circle with (1-r)(2-r) falloff
+set g_balance_falldamage_deadminspeed 250
+set g_balance_falldamage_minspeed 900
+set g_balance_falldamage_factor 0.20
+set g_balance_falldamage_maxdamage 40
+set g_balance_damagepush_speedfactor 2.5
+set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
+set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
+set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
+set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
+set g_balance_contents_playerdamage_lava_burn 0 // extra burning damage after leaving lava
+set g_balance_contents_playerdamage_lava_burn_time 2.5 // time across which the damage is applied (note: not dps!)
+set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
+set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
+set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
+// }}}
+
+// {{{ powerups
+set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken
+set g_balance_powerup_invincible_time 30
+set g_balance_powerup_strength_damage 3
+set g_balance_powerup_strength_force 3
+set g_balance_powerup_strength_time 30
+set g_balance_powerup_strength_selfdamage 1.5
+set g_balance_powerup_strength_selfforce 1.5
+set g_balance_superweapons_time 30
+// }}}
+
+// {{{ jetpack/hook
+set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
+set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
+set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
+set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
+set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
+set g_jetpack_fuel 8 "fuel per second for jetpack"
+set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
+
+set g_grappling_hook_tarzan 2 // 2: can also pull players
+set g_balance_grapplehook_speed_fly 1800
+set g_balance_grapplehook_speed_pull 2000
+set g_balance_grapplehook_force_rubber 2000
+set g_balance_grapplehook_force_rubber_overstretch 1000
+set g_balance_grapplehook_length_min 50
+set g_balance_grapplehook_stretch 50
+set g_balance_grapplehook_airfriction 0.2
+set g_balance_grapplehook_health 50
+set g_balance_grapplehook_damagedbycontents 1
+set g_balance_grapplehook_refire 0.2
+set g_balance_grapplehook_nade_time 0.7
+set g_balance_grapplehook_crouchslide 0
+set g_balance_grapplehook_gravity 0
+set g_balance_grapplehook_pull_frozen 0
+// }}}
+
+// {{{ port-o-launch
+set g_balance_portal_health 200 // these get recharged whenever the portal is used
+set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
+// }}}
+
+exec bal-wep-mario.cfg
index 3ac47371ee350ab68ce3fe4c5c2c3d2dfe769696..39405f399881c31eecdc243527b81eec5e3628f5 100644 (file)
@@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 45
 set g_balance_nix_ammo_rockets 15
 set g_balance_nix_ammo_cells 15
 set g_balance_nix_ammo_plasma 15
-set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammo_fuel 25
 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 set g_balance_nix_ammoincr_nails 6
 set g_balance_nix_ammoincr_rockets 2
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 0
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 999
 set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 999
-set g_pickup_armorlarge_anyway 0
+set g_pickup_armormega 100
+set g_pickup_armormega_max 999
+set g_pickup_armormega_anyway 0
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 999
 set g_pickup_healthsmall_anyway 0
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 999
 set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 999
-set g_pickup_healthlarge_anyway 0
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 999
+set g_pickup_healthbig_anyway 0
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 999
 set g_pickup_healthmega_anyway 0
@@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction"
 set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 set g_jetpack_fuel 8 "fuel per second for jetpack"
 set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
 
 set g_grappling_hook_tarzan 2 // 2: can also pull players
 set g_balance_grapplehook_speed_fly 1800
index a691a191838bdae351692863f639a759fb1e5059..da17e0afeb099d8cdd3664890f6cca351d208e43 100644 (file)
@@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320
 set g_balance_nix_ammo_rockets 160
 set g_balance_nix_ammo_cells 180
 set g_balance_nix_ammo_plasma 180
-set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammo_fuel 100
 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 set g_balance_nix_ammoincr_nails 6
 set g_balance_nix_ammoincr_rockets 2
@@ -65,25 +65,25 @@ set g_pickup_fuel_jetpack 100
 set g_pickup_fuel_max 100
 set g_pickup_armorsmall 5
 set g_pickup_armorsmall_max 20
-set g_pickup_armorsmall_anyway 0
+set g_pickup_armorsmall_anyway 1
 set g_pickup_armormedium 25
-set g_pickup_armormedium_max 200
+set g_pickup_armormedium_max 100
 set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
-set g_pickup_armorbig_max 200
+set g_pickup_armorbig_max 100
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 100
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 0
@@ -119,12 +119,12 @@ set g_balance_armor_regen 0
 set g_balance_armor_regenlinear 0
 set g_balance_armor_rot 0
 set g_balance_armor_rotlinear 1
-set g_balance_pause_armor_rot 1
+set g_balance_pause_armor_rot 5
 set g_balance_pause_armor_rot_spawn 5
 set g_balance_armor_regenstable 100
 set g_balance_armor_rotstable 0
 set g_balance_armor_limit 999
-set g_balance_armor_blockpercent 0.7
+set g_balance_armor_blockpercent 0.6
 set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
 set g_balance_fuel_regenlinear 0
 set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
@@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
 set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 set g_jetpack_fuel 8 "fuel per second for jetpack"
 set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
 
 set g_grappling_hook_tarzan 2 // 2: can also pull players
 set g_balance_grapplehook_speed_fly 1800
index 6d7831adac2624bbe88528ddc4388f7059cd18d5..ad7192227eb4abcf842a2b02f57b1025de46ff27 100644 (file)
@@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320
 set g_balance_nix_ammo_rockets 160
 set g_balance_nix_ammo_cells 180
 set g_balance_nix_ammo_plasma 180
-set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammo_fuel 100
 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 set g_balance_nix_ammoincr_nails 6
 set g_balance_nix_ammoincr_rockets 2
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
@@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
 set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 set g_jetpack_fuel 8 "fuel per second for jetpack"
 set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
 
 set g_grappling_hook_tarzan 2 // 2: can also pull players
 set g_balance_grapplehook_speed_fly 1800
index 07b38e830a8f988235a1ba0306db0c488f8c21f3..79344fda96b597a1ac1b034e3a2755c08bc598d0 100644 (file)
@@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320
 set g_balance_nix_ammo_rockets 160
 set g_balance_nix_ammo_cells 180
 set g_balance_nix_ammo_plasma 180
-set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammo_fuel 100
 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 set g_balance_nix_ammoincr_nails 6
 set g_balance_nix_ammoincr_rockets 2
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
@@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
 set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 set g_jetpack_fuel 8 "fuel per second for jetpack"
 set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
 
 set g_grappling_hook_tarzan 2 // 2: can also pull players
 set g_balance_grapplehook_speed_fly 1800
index ddc40e288eda5c3a8aaa18e47b96ac2f9772236b..f317d2e6b422d1225078fe3fa4d58de4ee128ce4 100644 (file)
@@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320
 set g_balance_nix_ammo_rockets 160
 set g_balance_nix_ammo_cells 180
 set g_balance_nix_ammo_plasma 180
-set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammo_fuel 100
 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 set g_balance_nix_ammoincr_nails 6
 set g_balance_nix_ammoincr_rockets 2
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
@@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
 set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 set g_jetpack_fuel 8 "fuel per second for jetpack"
 set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
 
 set g_grappling_hook_tarzan 2 // 2: can also pull players
 set g_balance_grapplehook_speed_fly 1800
index e0aa8b08270ddd73312518916661e2db0fb101cb..f5ec29240e97d8b8646c7ba8aabb535037049acd 100644 (file)
@@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320
 set g_balance_nix_ammo_rockets 160
 set g_balance_nix_ammo_cells 180
 set g_balance_nix_ammo_plasma 180
-set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammo_fuel 100
 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 set g_balance_nix_ammoincr_nails 6
 set g_balance_nix_ammoincr_rockets 2
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 0
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 100
 set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 0
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 0
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 0
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 100
 set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 100
-set g_pickup_healthlarge_anyway 0
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 100
+set g_pickup_healthbig_anyway 0
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 0
@@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
 set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 set g_jetpack_fuel 8 "fuel per second for jetpack"
 set g_jetpack_attenuation 2 "jetpack sound attenuation"
+set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack"
 
 set g_grappling_hook_tarzan 2 // 2: can also pull players
 set g_balance_grapplehook_speed_fly 1800
index 361621c6a83b413a270f1458f56a4ab362ffd338..82ea7815e566bcdce4077e4857d513b3934974d5 100644 (file)
@@ -17,7 +17,6 @@ bind LEFTARROW +moveleft
 bind DOWNARROW +back
 bind RIGHTARROW +moveright
 bind SHIFT +crouch
-bind ENTER +jump
 bind SPACE +jump
 
 // weapons
@@ -51,6 +50,7 @@ bind ` toggleconsole
 bind ~ toggleconsole
 bind TAB +showscores
 bind ESCAPE togglemenu
+bind ENTER messagemode
 bind t messagemode
 bind y messagemode2
 bind z messagemode2
index fc8082f49d114179419fcafa857bc0757d762c74..18df7f835b4226ce8cef0819fa2d3d301e0ca332 100644 (file)
--- a/bots.txt
+++ b/bots.txt
@@ -2,7 +2,7 @@
 //default team values (team-override): 1 = red, 2 = blue, 3 = yellow, 4 = pink                                                                                                                                 
 //use -1 for shirt-color or pants-color to get random colors                                                                                                                                   
 Hellfire       ignis   0       4       0       0       0       0       -0.5    -1      1       1       -0.5    -1      -1      2       0.5     -1
-Toxic  gakarmored      0       14      7       0       -1      -1.5    -0.5    0       1       0       0       0       2       -0.5    -0.5    1
+Toxic  gakmasked       0       14      7       0       -1      -1.5    -0.5    0       1       0       0       0       2       -0.5    -0.5    1
 Discovery      erebus  0       2       6       0       0       -1      -0.5    -0.5    1       -0.5    0.5     1.5     -0.5    -1      1       0.5
 Pegasus        umbra   0       13      11      0       1       1       1       1       -1      0       0.5     0       -2      0       -1      0
 Eureka umbra   0       12      7       0       0       0       -1.5    -0.5    -0.5    0       0       0       0       -0.5    1.5     1.5
@@ -10,7 +10,7 @@ Airhead       ignis   0       11      1       0       -1      -1.5    -1      -0.5    1       1       -1      1       -0.5    1       0.5     0
 Gator  gak     0       3       10      0       0       1       0       0.5     -0.5    0.5     -0.5    -1      0       0       -0.5    0
 Delirium       gakmasked       0       8       12      0       0       -1      -1      -1      0       2       0       1       0       2       -1      -1
 Death  gakmasked       0       4       11      0       -0.5    0       0       1       -0.5    0       1       0       0       0       0       0
-Scorcher       ignishalfmasked 0       13      13      0       0       -1      0       -0.5    0.5     1       0       1       -2      1       0       0
+Scorcher       ignismasked     0       13      13      0       0       -1      0       -0.5    0.5     1       0       1       -2      1       0       0
 Necrotic       nyx     0       12      14      0       0       0       0       1       0       -1      -0.5    -1      1       0       0       0
 Dominator      nyx     0       3       9       0       0       0       0       2       -1      0       0       0       -1      0       0       0
 Thunderstorm   erebus  0       13      6       0       0       0       0       -0.5    -1      1       0.5     0.5     -0.5    0       0.5     0
index dea35012a76a36546bfb922a8247b4ea67c904e4..bbdbdcb74a4873406ac60d3c4ac72fc158aba72e 100644 (file)
@@ -36,6 +36,8 @@ alias +hook +button6
 alias -hook -button6
 alias +jetpack +button10
 alias -jetpack -button10
+alias +dodge +button11
+alias -dodge -button11
 alias use "impulse 21"
 
 // for backwards compatibility
@@ -79,12 +81,14 @@ seta cl_zoomsensitivity 0   "how zoom changes sensitivity (0 = weakest, 1 = strong
 seta cl_unpress_zoom_on_spawn 1 "automatically unpress zoom when you spawn"
 seta cl_unpress_zoom_on_death 1 "automatically unpress zoom when you die (and don't allow zoom again while dead)"
 seta cl_unpress_zoom_on_weapon_switch 1 "automatically unpress zoom when you switch a weapon"
-seta cl_unpress_attack_on_weapon_switch 1 "automatically unpress fire and fire1 attack buttons when you switch a weapon"
+seta cl_unpress_attack_on_weapon_switch 0 "automatically unpress fire and fire1 attack buttons when you switch a weapon"
 
 seta cl_spawn_event_particles 1 "pointparticles effect whenever a player spawns"
 seta cl_spawn_event_sound 1 "sound effect whenever a player spawns"
 //seta cl_spawn_point_model 0 "place a model at all spawn points" // still needs a model
 seta cl_spawn_point_particles 1 "pointparticles effect at all spawn points" // managed by effects-.cfg files
+seta cl_spawn_point_dist_min 1200
+seta cl_spawn_point_dist_max 1600
 
 freelook 1
 sensitivity 6
@@ -214,8 +218,8 @@ set sv_mapchange_delay 5
 set minplayers 0 "number of players playing at the same time (if not enough real players are there the remaining slots are filled with bots)"
 
 // restart server if all players hit "ready"-button
-set sv_ready_restart 0 "if set to 1 allow a map to be restarted once all players pressed the \"ready\" button'"
-set sv_ready_restart_after_countdown 0 "if set to 1 the players and map items are reset after the countdown ended, otherwise they're reset already at the beginning of the countdown"
+set sv_ready_restart 0 "allow a map to be restarted once all players pressed the \"ready\" button"
+set sv_ready_restart_after_countdown 0 "reset players and map items after the countdown ended, instead of at the beginning of the countdown"
 set sv_ready_restart_repeatable 0      "allows the players to restart the game as often as needed"
 
 seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy, 1: same pitch 2: increase pitch with more damage 3: decrease pitch with more damage"
@@ -233,28 +237,33 @@ seta cl_eventchase_maxs "12 12 8" "max size of eventchase camera bbox"
 seta cl_eventchase_mins "-12 -12 -8" "min size of eventchase camera bbox"
 seta cl_eventchase_viewoffset "0 0 20" "viewoffset of eventchase camera"
 seta cl_eventchase_generator_viewoffset "0 0 80" "viewoffset of eventchase camera while viewing generator explosion"
+seta cl_eventchase_vehicle 1 "camera goes into 3rd person mode when inside a vehicle"
+seta cl_eventchase_vehicle_viewoffset "0 0 80"
+seta cl_eventchase_vehicle_distance 250
+
+set _vehicles_shownchasemessage 0
 
 //nifreks lockonrestart feature, used in team-based game modes, if set to 1 and all players readied up no other player can then join the game anymore, useful to block spectators from joining
-set teamplay_lockonrestart 0 "it set to 1 in a team-based game, the teams are locked once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)"
+set teamplay_lockonrestart 0 "lock teams once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)"
 
 set g_maxplayers 0     "maximum number of players allowed to play at the same time, set to 0 to allow all players to join the game"
 set g_maxplayers_spectator_blocktime 5 "if the players voted for the \"nospectators\" command, this setting defines the number of seconds a observer/spectator has time to join the game before he gets kicked"
 
 // tournament mod
-set g_warmup 0 "split the game into a warmup- and match-stage when set to 1"
-set g_warmup_limit 0   "if set to -1 the warmup-stage is not affected by any timelimit, if set to 0 the usual timelimit also affects warmup-stage, otherwise warmup will be limited to this time in SECONDS (useful for public matches)"
-set g_warmup_allow_timeout 0   "if set to 1 timeouts can also be called in the warmup-stage, when sv_timeout is set to 1"
+set g_warmup 0 "split the game into a warmup- and match-stage"
+set g_warmup_limit 0   "limit warmup-stage to this time (in seconds); if set to -1 the warmup-stage is not affected by any timelimit, if set to 0 the usual timelimit also affects warmup-stage"
+set g_warmup_allow_timeout 0   "allow calling timeouts in the warmup-stage (if sv_timeout is set to 1)"
 set g_warmup_allguns 1 "provide more weapons on start while in warmup: 0 = normal start weapons, 1 = all guns available on the map, 2 = all normal weapons"
 set g_warmup_majority_factor 0.8 "minimum percentage of players ready needed for warmup to end"
 
 set g_chat_nospectators 0      "if 0 spec/observer chat is always visible to the player, if 1 it is never visible to players, if 2 it is only visible to players during warmup stage"
-set sv_vote_nospectators 0     "Only players can call a vote (thus spectators and observers can't call a vote): 0 = all people can vote, 1 = spectators can vote in warmup stage, 2 = only players can vote (no exceptions)."
+set sv_vote_nospectators 0     "only players can call a vote (thus spectators and observers can't call a vote): 0 = all people can vote, 1 = spectators can vote in warmup stage, 2 = only players can vote (no exceptions)."
 
 alias g_tourney "g_tourney_$1"
 alias g_tourney_1 "g_warmup 1; g_chat_nospectators 2; sv_vote_nospectators 1"
 alias g_tourney_0 "g_warmup 0; g_chat_nospectators 0; sv_vote_nospectators 0"
 
-set sv_timeout 0       "allows a player to call a timeout, this will pause the game for some time"
+set sv_timeout 0       "allow a player to call a timeout, this will pause the game for some time"
 set sv_timeout_length 120      "how long the game will be paused at max, in seconds"
 set sv_timeout_number 2        "how many timeouts one player is allowed to call (gets reset after a restart)"
 set sv_timeout_leadtime 4      "how long the players will be informed that a timeout was called before it starts, in seconds"
@@ -268,7 +277,7 @@ set g_telefrags_teamplay 1 "never telefrag team mates"
 set g_telefrags_avoid 1 "when teleporters have a random destination, avoid teleporting to locations where a telefrag would happen"
 set g_teleport_maxspeed 0 "maximum speed that a player can keep when going through a teleporter (if a misc_teleporter_dest also has a cap the smallest one of these will be used), 0 = don't limit, -1 = keep no speed"
 
-seta cl_damageeffect 1 "enable weapon damage effects. 1 enables the feature on skeletal models, 2 on any model"
+seta cl_damageeffect 1 "enable weapon damage effects: 1 enables the feature on skeletal models, 2 on any model"
 seta cl_damageeffect_ticrate 0.1 "particle spawn rate"
 seta cl_damageeffect_bones 5 "how many damages to allow on a rigged mesh at once (non-skeletal objects are limited to one)"
 seta cl_damageeffect_distribute 1 "divide particle intensity if multiple damages are present"
@@ -287,6 +296,10 @@ set sv_gibhealth 100 "Minus health a dead body must have in order to get gibbed"
 set sv_friction_on_land 0
 set sv_friction_slick 0.5
 
+set sv_slick_applygravity 0
+
+set sv_aircontrol_backwards 0 "apply forward aircontrol options to backward movement"
+
 set sv_player_viewoffset "0 0 35" "view offset of the player model"
 set sv_player_mins "-16 -16 -24" "playermodel mins"
 set sv_player_maxs "16 16 45" "playermodel maxs"
@@ -360,26 +373,26 @@ set bot_ai_keyboard_threshold 0.57
 set bot_ai_aimskill_offset 0.3 "Amount of error induced to the bots aim"
 set bot_ai_aimskill_think 1 "Aiming velocity. Use values below 1 for slower aiming"
 set bot_ai_custom_weapon_priority_distances "300 850"  "Define close and far distances in any order. Based on the distance to the enemy bots will choose different weapons"
-set bot_ai_custom_weapon_priority_far   "vaporizer vortex rifle electro devastator mortar hagar hlac crylink blaster machinegun fireball seeker shotgun tuba minelayer"        "Desired weapons for far distances ordered by priority"
-set bot_ai_custom_weapon_priority_mid   "vaporizer devastator vortex fireball seeker mortar electro machinegun crylink hlac hagar shotgun blaster rifle tuba minelayer arc shockwave"  "Desired weapons for middle distances ordered by priority"
-set bot_ai_custom_weapon_priority_close "vaporizer shotgun vortex machinegun hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer arc shockwave"  "Desired weapons for close distances ordered by priority"
+set bot_ai_custom_weapon_priority_far   "vaporizer vortex rifle electro devastator mortar hagar hlac crylink blaster machinegun fireball seeker shotgun shockwave tuba minelayer"      "Desired weapons for far distances ordered by priority"
+set bot_ai_custom_weapon_priority_mid   "vaporizer devastator vortex fireball seeker mortar electro machinegun arc crylink hlac hagar shotgun shockwave blaster rifle tuba minelayer"  "Desired weapons for middle distances ordered by priority"
+set bot_ai_custom_weapon_priority_close "vaporizer shotgun shockwave vortex machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer"  "Desired weapons for close distances ordered by priority"
 set bot_ai_weapon_combo 1      "Enable bots to do weapon combos"
 set bot_ai_weapon_combo_threshold 0.4  "Try to make a combo N seconds after the last attack"
 set bot_ai_friends_aware_pickup_radius "500"   "Bots will not pickup items if a team mate is this distance near the item"
 set bot_ai_ignoregoal_timeout 3        "Ignore goals making bots to get stuck in front of a wall for N seconds"
 set bot_ai_bunnyhop_skilloffset 7      "Bots with skill equal or greater than this value will perform the  \"bunnyhop\" technique"
 set bot_ai_bunnyhop_startdistance 200 "Run to goals located further than this distance"
-set bot_ai_bunnyhop_stopdistance 200 "Stop jumping after reaching this distance to the goal"
+set bot_ai_bunnyhop_stopdistance 300 "Stop jumping after reaching this distance to the goal"
 set bot_ai_bunnyhop_firstjumpdelay 0.2 "Start running to the goal only if it was seen for more than N seconds"
 set bot_god 0 "god mode for bots"
 set bot_ai_navigation_jetpack 0 "Enable bots to navigate maps using the jetpack"
 set bot_ai_navigation_jetpack_mindistance 3500 "Bots will try fly to objects located farther than this distance"
 // Better don't touch these, there are hard to tweak!
 set bot_ai_aimskill_order_mix_1st 0.01 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 1st filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 2nd filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 3th filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 4th filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 5th filter output to apply to the aiming angle"
 set bot_ai_aimskill_order_filter_1st 0.4 "Position filter"
 set bot_ai_aimskill_order_filter_2nd 0.4 "Movement filter"
 set bot_ai_aimskill_order_filter_3th 0.2 "Acceleration filter"
@@ -487,15 +500,15 @@ seta timelimit_suddendeath 5 "number of minutes suddendeath mode lasts after all
 set g_tdm 0 "Team Deathmatch: the team who kills their opponents most often wins"
 set g_tdm_on_dm_maps 0 "when this is set, all DM maps automatically support TDM"
 
-seta teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the cvars g_mirrordamage*, g_friendlyfire* and g_teamdamage_threshold*"
-seta g_mirrordamage 0.700000   "for teamplay 4: mirror damage factor"
-seta g_mirrordamage_virtual 1  "for teamplay 4: do not actually apply mirror damage, just show graphics effect for it"
-seta g_mirrordamage_onlyweapons 0 "for teamplay 4: only apply mirror damage if the attack was from a weapon"
-seta g_friendlyfire 0.500000   "for teamplay 4: fiendly fire factor"
-seta g_friendlyfire_virtual 1  "for teamplay 4: do not actually apply friendly fire, just show graphics effect for it"
-seta g_friendlyfire_virtual_force 1    "for teamplay 4: apply force even though damage was made virtual only"
-seta g_teamdamage_threshold 40 "for teamplay 4: threshold over which to apply mirror damage"
-seta g_teamdamage_resetspeed 20        "for teamplay 4: how fast player's teamdamage count decreases"
+seta teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the cvars g_mirrordamage*, g_friendlyfire* and g_teamdamage*"
+seta g_mirrordamage 0.7              "for teamplay_mode 4: mirror damage factor"
+seta g_mirrordamage_virtual 1        "for teamplay_mode 4: do not actually apply mirror damage, just show graphics effect for it"
+seta g_mirrordamage_onlyweapons 0    "for teamplay_mode 4: only apply mirror damage if the attack was from a weapon"
+seta g_friendlyfire 0.5              "for teamplay_mode 4: friendly fire factor"
+seta g_friendlyfire_virtual 1        "for teamplay_mode 4: do not actually apply friendly fire, just show graphics effect for it"
+seta g_friendlyfire_virtual_force 1  "for teamplay_mode 4: apply force even though damage was made virtual only"
+seta g_teamdamage_threshold 40       "for teamplay_mode 4: threshold over which to apply mirror damage"
+seta g_teamdamage_resetspeed 20      "for teamplay_mode 4: how fast player's teamdamage count decreases"
 
 seta g_balance_teams 1 "automatically balance out players entering instead of asking them for their preferred team"
 seta g_balance_teams_prevent_imbalance 1       "prevent players from changing to larger teams"
@@ -682,7 +695,8 @@ alias sv_fbskin_off "sv_defaultcharacter 0; sv_defaultplayerskin 0; sv_defaultpl
 seta sv_servermodelsonly 1
 
 cl_curl_enabled 1
-cl_curl_maxspeed 500
+cl_curl_maxdownloads 3
+cl_curl_maxspeed 0
 cl_curl_useragent 1
 cl_curl_useragent_append "$g_xonoticversion"
 
@@ -694,7 +708,7 @@ set sv_motd ""
 set g_waypoints_for_items 0    "make waypoints out of items, values: 0 = never, 1 = unless the mapper prevents it by worldspawn.spawnflags & 1, 2 = always"
 
 seta g_maplist_votable 6 "number of maps that are shown in the map voting at the end of a match"
-seta g_maplist_votable_keeptwotime 15
+seta g_maplist_votable_keeptwotime 15 "show only 2 options after this amount of time during map vote screen"
 seta g_maplist_votable_timeout 30      "timeout for the map voting; must be below 50 seconds!"
 seta g_maplist_votable_suggestions 2
 seta g_maplist_votable_suggestions_override_mostrecent 0
@@ -703,7 +717,7 @@ seta g_maplist_votable_abstain 0    "when 1, you can abstain from your vote"
 seta g_maplist_votable_screenshot_dir "maps levelshots"        "where to look for map screenshots"
 
 set sv_vote_gametype 0 "show a vote screen for gametypes before map vote screen"
-set sv_vote_gametype_keeptwotime 10 "show only 2 options for this amount of time during gametype vote screen"
+set sv_vote_gametype_keeptwotime 10 "show only 2 options after this amount of time during gametype vote screen"
 set sv_vote_gametype_options "dm ctf ca lms tdm ft"
 set sv_vote_gametype_timeout 20
 set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes"
@@ -774,8 +788,9 @@ seta g_waypointsprite_turrets 1 "disable turret waypoints"
 seta g_waypointsprite_turrets_maxdist 5000 "max distace for turret sprites"
 seta g_waypointsprite_tactical 1 "tactical overlay on turrets when in a vehicle"
 
-seta cl_damagetext "0" "Draw damage dealt where you hit the enemy"
-seta cl_damagetext_format "-{total}" "How to format the damage text. {health}, {armor}, {total}"
+seta cl_damagetext "1" "Draw damage dealt where you hit the enemy"
+seta cl_damagetext_format "-{total}" "How to format the damage text. {health}, {armor}, {total}, {potential}: full damage not capped to target's health, {potential_health}: health damage not capped to target's health"
+seta cl_damagetext_format_verbose 0 "{health} shows {potential_health} too when they differ; {total} shows {potential} too when they differ"
 seta cl_damagetext_color "1 1 0" "Damage text color"
 seta cl_damagetext_color_per_weapon "0" "Damage text uses weapon color"
 seta cl_damagetext_size "8" "Damage text font size"
@@ -784,8 +799,18 @@ seta cl_damagetext_alpha_lifetime "3" "Damage text lifetime in seconds"
 seta cl_damagetext_velocity "0 0 20" "Damage text move direction"
 seta cl_damagetext_offset "0 -40 0" "Damage text offset"
 seta cl_damagetext_accumulate_range "30" "Damage text spawned within this range is accumulated"
+seta cl_damagetext_accumulate_alpha_rel "0.65" "Only update existing damage text when it's above this much percentage (0 to 1) of the starting alpha"
+seta cl_damagetext_friendlyfire "1" "Show damage text for friendlyfire too"
+seta cl_damagetext_friendlyfire_color "1 0 0" "Damage text color for friendlyfire"
 
-set sv_itemstime 1 "enable networking of left time until respawn for items such as mega health and large armor"
+seta cl_vehicles_alarm 1 "Play an alarm sound when the vehicle you are driving is heavily damaged"
+seta cl_vehicles_hud_tactical 1
+seta cl_vehicles_hudscale 0.5
+seta cl_vehicles_notify_time 15
+seta cl_vehicles_crosshair_size 0.5
+seta cl_vehicles_crosshair_colorize 1
+
+set sv_itemstime 1 "enable networking of time left until respawn for items such as mega health/armor and powerups"
 
 // so it can be stuffcmd-ed still
 set cl_gravity 800     "but ignored anyway"
@@ -830,6 +855,10 @@ seta sbar_info_pos 0 "Y-axis distance from lower right corner for engine info pr
 
 // scoreboard
 seta scoreboard_columns default
+
+// keep old scoreboard cvars for compatibility's sake
+// they've been replaced by hud_panel_scoreboard_* cvars
+// TODO remove them after a future release (0.8.2+)
 seta scoreboard_border_thickness 1 "scoreboard border thickness"
 seta scoreboard_accuracy_border_thickness 1 "accuracy stats border thickness"
 seta scoreboard_accuracy_doublerows 0 "use two rows instead of one"
@@ -881,7 +910,7 @@ seta menu_slist_categories_CAT_FAVORITED_override ""
 seta menu_slist_categories_CAT_RECOMMENDED_override ""
 seta menu_slist_categories_CAT_NORMAL_override ""
 seta menu_slist_categories_CAT_SERVERS_override "CAT_NORMAL"
-seta menu_slist_categories_CAT_XPM_override "CAT_NORMAL"
+seta menu_slist_categories_CAT_XPM_override ""
 seta menu_slist_categories_CAT_MODIFIED_override ""
 seta menu_slist_categories_CAT_OVERKILL_override ""
 seta menu_slist_categories_CAT_INSTAGIB_override ""
@@ -936,7 +965,7 @@ set con_completion_playermodel      "models/player/*.iqm"
 
 // helper
 // these non-saved engine cvars shall be saved
-alias makesaved "seta $1 \"${$1 ?}\"
+alias makesaved "seta $1 \"${$1 ?}\""
 makesaved cl_maxfps_alwayssleep
 makesaved cl_port
 makesaved gl_finish
@@ -961,8 +990,6 @@ sv_gameplayfix_delayprojectiles 0
 sv_gameplayfix_q2airaccelerate 1
 sv_gameplayfix_stepmultipletimes 1
 
-cl_gameplayfix_fixedcheckwatertransition 1
-
 // delay for "kill" to prevent abuse
 set g_balance_kill_delay 2
 set g_balance_kill_antispam 5
@@ -978,6 +1005,8 @@ gl_texturecompression_lightcubemaps 1
 gl_texturecompression_q3bsplightmaps 0
 gl_texturecompression_sky 1
 
+cl_maxfps 200
+
 seta menu_mouse_absolute 1 "use the OS mouse pointer motion for menu"
 seta menu_mouse_speed 1 "speed multiplier for the mouse in the menu (does not affect in-game aiming)"
 set menu_use_default_hostname 1
@@ -1005,6 +1034,8 @@ seta menu_cdtrack "rising-of-the-phoenix"
 
 set sv_maxidle 0 "kick players idle for more than this amount of time in seconds"
 set sv_maxidle_spectatorsareidle 0 "when sv_maxidle is not 0, assume spectators are idle too"
+set sv_maxidle_slots 0 "when not 0, only kick idlers when this many or less player slots are available"
+set sv_maxidle_slots_countbots 1 "count bots as player slots"
 
 // these entities are not referenced by anything directly, they just represent
 // teams and are found by find() when needed
@@ -1016,13 +1047,13 @@ sv_allowdownloads 0 // download protocol is evil
 
 set g_jump_grunt 0     "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?"
 
-seta cl_weaponpriority "vaporizer vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun hlac tuba blaster porto seeker hook" "weapon priority list"
+seta cl_weaponpriority "vaporizer hmg rpc vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list"
 seta cl_weaponpriority_useforcycling 0 "when set, weapon cycling by the mouse wheel makes use of the weapon priority list (the special value 2 uses the weapon ID list for cycling)"
-seta cl_weaponpriority0 "devastator mortar hagar seeker fireball"                       "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun.  Default value: explosives"
+seta cl_weaponpriority0 "rpc devastator mortar hagar seeker fireball"                   "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun.  Default value: explosives"
 seta cl_weaponpriority1 "vaporizer vortex crylink hlac arc electro blaster shockwave"   "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun.  Default value: energy"
 seta cl_weaponpriority2 "vaporizer vortex rifle"                                        "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun.  Default value: hitscan exact"
-seta cl_weaponpriority3 "vaporizer vortex rifle machinegun shotgun"                     "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun.  Default value: hitscan all"
-seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun"            "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun.  Default value: spam weapons"
+seta cl_weaponpriority3 "vaporizer hmg vortex rifle machinegun shotgun shockwave"       "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun.  Default value: hitscan all"
+seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun shockwave"  "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun.  Default value: spam weapons"
 seta cl_weaponpriority5 "blaster shockwave hook porto"                                  "use weapon_priority_5_prev for prev gun from this list, weapon_priority_5_best for best gun, weapon_priority_5_next for next gun.  Default value: weapons for moving"
 seta cl_weaponpriority6 ""                                                              "use weapon_priority_6_prev for prev gun from this list, weapon_priority_6_best for best gun, weapon_priority_6_next for next gun"
 seta cl_weaponpriority7 ""                                                              "use weapon_priority_7_prev for prev gun from this list, weapon_priority_7_best for best gun, weapon_priority_7_next for next gun"
@@ -1073,7 +1104,7 @@ set g_ban_sync_trusted_servers "" "request ban lists from these xonotic servers
 set g_ban_sync_timeout 45      "time out in seconds for the ban sync requests"
 set g_ban_sync_trusted_servers_verify 0        "when set to 1, additional bans sent by the servers are ignored, and only bans for the requested IP are used"
 
-set g_showweaponspawns 1       "display sprites for weapon spawns found on the map when a weapon key is pressed and the weapon is not available"
+set g_showweaponspawns 1 "1: display waypoints for weapon spawns found on the map when a weapon key is pressed and the weapon is not owned; 2: for dropped weapons too; 3: for all the weapons sharing the same impulse"
 
 // ballistics use physical units, but qu based
 //   Quake-Newton: 1 qN  = 1 qu * 1 g / 1 s^2
@@ -1083,6 +1114,7 @@ set g_showweaponspawns 1  "display sprites for weapon spawns found on the map whe
 set g_ballistics_mindistance 2 // enable ballistics starting from 2 qu
 set g_ballistics_density_player 0.50 // players are 2x as easy to pass as walls
 set g_ballistics_density_corpse 0.10 // corpses are 10x as easy to pass as walls
+set g_ballistics_penetrate_clips 0 "allow ballistics to pass through weapon clips"
 
 set cl_stripcolorcodes 0       "experimental feature (notes: strips ALL color codes from messages!)"
 
@@ -1193,6 +1225,7 @@ seta cl_forcemyplayermodel "" "set to the model file name you want to show yours
 seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (does not affect how enemies look with cl_forceplayermodels)"
 seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)"
 seta cl_movement_errorcompensation 1 "try to compensate for prediction errors and reduce preceived lag"
+seta cl_movement_intermissionrunning 0 "keep velocity after the match ends, players may appear to continue running while stationary"
 
 // campaign internal, set when loading a campaign map1G
 set _campaign_index ""
@@ -1202,6 +1235,10 @@ set _campaign_testrun 0 "To verify the campaign file, set this to 1, then start
 // debug
 set _independent_players 0 "DO NOT TOUCH"
 set _notarget 0 "NO, REALLY, DON'T"
+set debugdraw 0
+set debugdraw_filter ""
+set debugdraw_filterout ""
+set debugtrace 0
 
 // define some engine cvars that we need even on dedicated server
 set r_showbboxes 0
@@ -1290,6 +1327,8 @@ r_fakelight 1
 r_water_hideplayer 1 // hide your own feet/player model in refraction views, this way you don't see half of your body under water
 r_water_refractdistort 0.019
 
+set cl_rainsnow_maxdrawdist 2048
+
 // strength sound settings
 set sv_strengthsound_antispam_time 0.1 "minimum distance of strength sounds"
 set sv_strengthsound_antispam_refire_threshold 0.04 "apply minimum distance only if refire of the gun is smaller than this"
@@ -1355,8 +1394,12 @@ set g_frozen_revive_falldamage_health 40 "Amount of health player has if they re
 set g_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn"
 set g_frozen_force 0.6 "How much to multiply the force on a frozen player with"
 
-// player statistics server URI
+// player statistics
 set g_playerstats_gamereport_uri "http://stats.xonotic.org/stats/submit" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory."
+set g_playerstats_gamereport_ladder ""
+set g_playerstats_playerbasic_uri "http://stats.xonotic.org"
+set g_playerstats_playerdetail_uri "http://stats.xonotic.org/player/me"
+set g_playerstats_playerdetail_autoupdatetime 1800 // automatically update every 30 minutes anyway
 
 // autoscreenshots
 set g_max_info_autoscreenshot 3 "how many info_autoscreenshot entities are allowed"
@@ -1436,6 +1479,8 @@ alias menu_sync "menu_cmd sync"
 set sv_join_notices ""
 set sv_join_notices_time 15
 
+seta cl_items_nofade 0
+seta cl_animate_items 1
 seta cl_ghost_items 0.45 "enable ghosted items (when between 0 and 1, overrides the alpha value)"
 seta cl_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged"
 set sv_simple_items 1 "allow or forbid client use of simple items"
@@ -1445,6 +1490,9 @@ set cl_fullbright_items 0 "enable fullbright items (if server allows, controled
 set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0"
 set cl_weapon_stay_alpha 0.75 "Alpha of picked up weapons when g_weapon_stay > 0"
 
+set sv_showspectators 0 "Show who's spectating who in the player info panel. Shouldn't be used on competitive servers, also disable when watching a suspected cheater"
+seta cl_showspectators 1
+
 // Facility for config.cfg use ONLY.
 // Interpreted in post-config.cfg.
 seta menu_forced_saved_cvars "" "These cvars will always be saved, despite engine/Xonotic cvar saving status"
index 4cf77d52e39bd0373c320cf4231372f5864f633f..71f0dc1792200bea112c846ba847e47c7526bf29 100644 (file)
@@ -259,9 +259,10 @@ set g_ctf_oneflag_reverse 0 "apply reverse mode to oneflag CTF (take flag to ene
 set g_ctf_flag_return 1 "auto return the flag to base when touched by a teammate"
 set g_ctf_flag_return_carrying 0 "(manual return mode) auto return the flag to base if touched by a flag carrier"
 set g_ctf_flag_return_carried_radius 100 "allow flags to be returned by carrier if base is within this radius"
-set g_ctf_flag_return_time 15
-set g_ctf_flag_return_dropped 100
-set g_ctf_flag_return_damage 0
+set g_ctf_flag_return_time 15 "automatically return the flag to base after this amount of time"
+set g_ctf_flag_return_dropped 100 "automatically return the flag to base if dropped within this distance from base (in qu)"
+set g_ctf_flag_return_damage 0 "allow the flag to be damaged, reducing time needed to automatically return to base"
+set g_ctf_flag_return_damage_delay 0 "how much time the flag takes to automatically return to base if it falls into lava/slime/trigger hurt"
 set g_ctf_flag_return_when_unreachable 1 "automatically return the flag if it falls into lava/slime/trigger hurt"
 set g_ctf_flagcarrier_auto_helpme_damage 100 "automatically place a helpme notification on flag carrier waypointsprite if they get hit and their health dips below this value"
 set g_ctf_flagcarrier_auto_helpme_time 2 "antispam time for the helpme notification"
@@ -391,7 +392,7 @@ set g_freezetag_teams 0
 // ==========
 //  keepaway
 // ==========
-set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details"
+set g_keepaway 0 "game mode which focuses around a ball"
 set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)"
 set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball"
 set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score"
@@ -488,6 +489,7 @@ set g_nexball_sound_bounce   1    "bouncing sound (0: off)"
 set g_nexball_basketball_trail  1  "1 to leave a trail"
 set g_nexball_football_trail    0  "1 to leave a trail"
 set g_nexball_trail_color     254  "1-256 for different colors (Quake palette, 254 is white)"
+set g_nexball_playerclip_collisions 1 "make the ball bounce off clips"
 set g_nexball_radar_showallplayers 1  "1: show every player and the ball on the radar  0: only show teammates and the ball on the radar"
 seta g_nexball_safepass_maxdist 5000 "Max distance to allow save fassping (0 to turn off safe passing)"
 seta g_nexball_safepass_turnrate 0.1 "How fast the safe-pass ball can habge direction"
diff --git a/gfx/hud/default/flag_stalemate.tga b/gfx/hud/default/flag_stalemate.tga
new file mode 100644 (file)
index 0000000..c52ebb8
Binary files /dev/null and b/gfx/hud/default/flag_stalemate.tga differ
index 2c5229aa93667f6556a8bbcee3d0b80cf68330cb..1609b782c95a5b778ca9b9365f3fd65251e5eb4a 100644 (file)
Binary files a/gfx/hud/luma/flag_blue_lost.tga and b/gfx/hud/luma/flag_blue_lost.tga differ
index e6fd5c5bc0f2d20ae3eaae6281fe2f01ac7558bd..85ef904c58ab80a4119bcecce6f494c3649258d1 100644 (file)
Binary files a/gfx/hud/luma/flag_neutral_lost.tga and b/gfx/hud/luma/flag_neutral_lost.tga differ
index 0cd5467199b07afc3fa0d50986e66250963e8d6d..f636620b59650ebc9793e9976c6f842c081df95b 100644 (file)
Binary files a/gfx/hud/luma/flag_pink_lost.tga and b/gfx/hud/luma/flag_pink_lost.tga differ
index 014a4163c1f6111b87b68b5f2125efd732ad605f..7260275eaf385c8aa3a57c196077a84d0d68b764 100644 (file)
Binary files a/gfx/hud/luma/flag_red_lost.tga and b/gfx/hud/luma/flag_red_lost.tga differ
diff --git a/gfx/hud/luma/flag_stalemate.tga b/gfx/hud/luma/flag_stalemate.tga
new file mode 100644 (file)
index 0000000..c52ebb8
Binary files /dev/null and b/gfx/hud/luma/flag_stalemate.tga differ
index bd5f3def21975de5898d52aee631fc4c9cb938b1..ec45f11740e295d9506f6b448688e121ca28131c 100644 (file)
Binary files a/gfx/hud/luma/flag_yellow_lost.tga and b/gfx/hud/luma/flag_yellow_lost.tga differ
index 2c5229aa93667f6556a8bbcee3d0b80cf68330cb..1609b782c95a5b778ca9b9365f3fd65251e5eb4a 100644 (file)
Binary files a/gfx/hud/luma/notify_blue_lost.tga and b/gfx/hud/luma/notify_blue_lost.tga differ
index e6fd5c5bc0f2d20ae3eaae6281fe2f01ac7558bd..85ef904c58ab80a4119bcecce6f494c3649258d1 100644 (file)
Binary files a/gfx/hud/luma/notify_neutral_lost.tga and b/gfx/hud/luma/notify_neutral_lost.tga differ
index 0cd5467199b07afc3fa0d50986e66250963e8d6d..f636620b59650ebc9793e9976c6f842c081df95b 100644 (file)
Binary files a/gfx/hud/luma/notify_pink_lost.tga and b/gfx/hud/luma/notify_pink_lost.tga differ
index 014a4163c1f6111b87b68b5f2125efd732ad605f..7260275eaf385c8aa3a57c196077a84d0d68b764 100644 (file)
Binary files a/gfx/hud/luma/notify_red_lost.tga and b/gfx/hud/luma/notify_red_lost.tga differ
index bd5f3def21975de5898d52aee631fc4c9cb938b1..ec45f11740e295d9506f6b448688e121ca28131c 100644 (file)
Binary files a/gfx/hud/luma/notify_yellow_lost.tga and b/gfx/hud/luma/notify_yellow_lost.tga differ
diff --git a/gfx/menu/luma/icon_mod_jeff.tga b/gfx/menu/luma/icon_mod_jeff.tga
new file mode 100644 (file)
index 0000000..ba3080a
Binary files /dev/null and b/gfx/menu/luma/icon_mod_jeff.tga differ
diff --git a/gfx/menu/luminos/icon_mod_jeff.tga b/gfx/menu/luminos/icon_mod_jeff.tga
new file mode 100644 (file)
index 0000000..4bf904b
Binary files /dev/null and b/gfx/menu/luminos/icon_mod_jeff.tga differ
diff --git a/gfx/menu/wickedx/icon_mod_jeff.tga b/gfx/menu/wickedx/icon_mod_jeff.tga
new file mode 100644 (file)
index 0000000..4bf904b
Binary files /dev/null and b/gfx/menu/wickedx/icon_mod_jeff.tga differ
diff --git a/gfx/menu/xaw/icon_mod_jeff.tga b/gfx/menu/xaw/icon_mod_jeff.tga
new file mode 100644 (file)
index 0000000..4bf904b
Binary files /dev/null and b/gfx/menu/xaw/icon_mod_jeff.tga differ
index 2f4f31e92b7290f7f2852a69ffdc1dc8272915ff..5ebdeaeec999de16665fbadd76f228d2edd9ffd3 100644 (file)
@@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.86 0.35 0"
 seta hud_progressbar_vehicles_ammo1_color "0.77 0.67 0"
 seta hud_progressbar_vehicles_ammo2_color "0.86 0.35 0"
 
-seta _hud_panelorder "17 15 12 9 5 10 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 "
+seta _hud_panelorder "17 15 12 9 5 10 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 23 24 "
 
 seta hud_configure_grid "1"
 seta hud_configure_grid_xsize "0.005000"
@@ -230,7 +230,7 @@ seta hud_panel_engineinfo_bg_padding ""
 seta hud_panel_engineinfo_framecounter_time "0.1"
 seta hud_panel_engineinfo_framecounter_decimals "0"
 
-seta hud_panel_infomessages_pos "0.720000 0.100000"
+seta hud_panel_infomessages_pos "0.680000 0.100000"
 seta hud_panel_infomessages_size "0.280000 0.080000"
 seta hud_panel_infomessages_bg "0"
 seta hud_panel_infomessages_bg_color ""
@@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team ""
 seta hud_panel_mapvote_bg_alpha ""
 seta hud_panel_mapvote_bg_border ""
 seta hud_panel_mapvote_bg_padding ""
+seta hud_panel_mapvote_highlight_border "1"
 
 seta hud_panel_itemstime_pos "0.030000 0.260000"
 seta hud_panel_itemstime_size "0.070000 0.230000"
@@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border ""
 seta hud_panel_quickmenu_bg_padding ""
 seta hud_panel_quickmenu_align "0"
 
+seta hud_panel_scoreboard_pos "0.150000 0.150000"
+seta hud_panel_scoreboard_size "0.700000 0.700000"
+seta hud_panel_scoreboard_bg "border_default"
+seta hud_panel_scoreboard_bg_color "0 0.3 0.5"
+seta hud_panel_scoreboard_bg_color_team ""
+seta hud_panel_scoreboard_bg_alpha "0.7"
+seta hud_panel_scoreboard_bg_border ""
+seta hud_panel_scoreboard_bg_padding ""
+seta hud_panel_scoreboard_fadeinspeed "10"
+seta hud_panel_scoreboard_fadeoutspeed "5"
+seta hud_panel_scoreboard_respawntime_decimals "1"
+seta hud_panel_scoreboard_table_bg_alpha "0"
+seta hud_panel_scoreboard_table_bg_scale "0.25"
+seta hud_panel_scoreboard_table_fg_alpha "0.9"
+seta hud_panel_scoreboard_table_fg_alpha_self "1"
+seta hud_panel_scoreboard_table_highlight "1"
+seta hud_panel_scoreboard_table_highlight_alpha "0.2"
+seta hud_panel_scoreboard_table_highlight_alpha_self "0.4"
+seta hud_panel_scoreboard_bg_teams_color_team "0"
+seta hud_panel_scoreboard_accuracy_doublerows "0"
+seta hud_panel_scoreboard_accuracy_nocolors "0"
+
 menu_sync
index 3d259ebf394d7b104f848c37e8a35058c8b6aec0..8f64ddebb6e943040a0c8458f64ecc02a22f4a2b 100644 (file)
@@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5"
 seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0"
 seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0"
 
-seta _hud_panelorder "17 15 12 9 10 5 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 "
+seta _hud_panelorder "17 15 12 9 10 5 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 23 24 "
 
 seta hud_configure_grid "1"
 seta hud_configure_grid_xsize "0.010000"
@@ -230,7 +230,7 @@ seta hud_panel_engineinfo_bg_padding ""
 seta hud_panel_engineinfo_framecounter_time "0.1"
 seta hud_panel_engineinfo_framecounter_decimals "0"
 
-seta hud_panel_infomessages_pos "0.720000 0.100000"
+seta hud_panel_infomessages_pos "0.680000 0.100000"
 seta hud_panel_infomessages_size "0.280000 0.080000"
 seta hud_panel_infomessages_bg "0"
 seta hud_panel_infomessages_bg_color ""
@@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team ""
 seta hud_panel_mapvote_bg_alpha ""
 seta hud_panel_mapvote_bg_border ""
 seta hud_panel_mapvote_bg_padding ""
+seta hud_panel_mapvote_highlight_border "1"
 
 seta hud_panel_itemstime_pos "0.030000 0.260000"
 seta hud_panel_itemstime_size "0.070000 0.230000"
@@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border ""
 seta hud_panel_quickmenu_bg_padding ""
 seta hud_panel_quickmenu_align "0"
 
+seta hud_panel_scoreboard_pos "0.150000 0.150000"
+seta hud_panel_scoreboard_size "0.700000 0.700000"
+seta hud_panel_scoreboard_bg "border_default"
+seta hud_panel_scoreboard_bg_color ""
+seta hud_panel_scoreboard_bg_color_team "0.7"
+seta hud_panel_scoreboard_bg_alpha "0.7"
+seta hud_panel_scoreboard_bg_border ""
+seta hud_panel_scoreboard_bg_padding ""
+seta hud_panel_scoreboard_fadeinspeed "10"
+seta hud_panel_scoreboard_fadeoutspeed "5"
+seta hud_panel_scoreboard_respawntime_decimals "1"
+seta hud_panel_scoreboard_table_bg_alpha "0.8"
+seta hud_panel_scoreboard_table_bg_scale "0.25"
+seta hud_panel_scoreboard_table_fg_alpha "0.9"
+seta hud_panel_scoreboard_table_fg_alpha_self "1"
+seta hud_panel_scoreboard_table_highlight "1"
+seta hud_panel_scoreboard_table_highlight_alpha "0.08"
+seta hud_panel_scoreboard_table_highlight_alpha_self "0.3"
+seta hud_panel_scoreboard_bg_teams_color_team "0"
+seta hud_panel_scoreboard_accuracy_doublerows "1"
+seta hud_panel_scoreboard_accuracy_nocolors "0"
+
 menu_sync
index c1ac51b7ab854ddf345f7e11023b64713dccc810..31926d2f5ab9d906dfbf79f26d2e5d9e8441ebfe 100644 (file)
@@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5"
 seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0"
 seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0"
 
-seta _hud_panelorder "17 10 3 0 14 6 9 13 4 1 2 11 12 7 5 8 15 16 18 23 19 20 21 22 "
+seta _hud_panelorder "17 10 3 0 14 6 9 13 4 1 2 11 12 7 5 8 15 16 18 23 19 20 21 22 23 24 "
 
 seta hud_configure_grid "1"
 seta hud_configure_grid_xsize "0.010000"
@@ -231,7 +231,7 @@ seta hud_panel_engineinfo_framecounter_time "0.1"
 seta hud_panel_engineinfo_framecounter_decimals "0"
 
 seta hud_panel_infomessages_pos "0.710000 0"
-seta hud_panel_infomessages_size "0.290000 0.100000"
+seta hud_panel_infomessages_size "0.280000 0.090000"
 seta hud_panel_infomessages_bg "0"
 seta hud_panel_infomessages_bg_color ""
 seta hud_panel_infomessages_bg_color_team ""
@@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team ""
 seta hud_panel_mapvote_bg_alpha ""
 seta hud_panel_mapvote_bg_border ""
 seta hud_panel_mapvote_bg_padding ""
+seta hud_panel_mapvote_highlight_border "1"
 
 seta hud_panel_itemstime_pos "0.000000 0.310000"
 seta hud_panel_itemstime_size "0.070000 0.180000"
@@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border ""
 seta hud_panel_quickmenu_bg_padding ""
 seta hud_panel_quickmenu_align "0"
 
+seta hud_panel_scoreboard_pos "0.150000 0.150000"
+seta hud_panel_scoreboard_size "0.700000 0.700000"
+seta hud_panel_scoreboard_bg "0"
+seta hud_panel_scoreboard_bg_color ""
+seta hud_panel_scoreboard_bg_color_team ""
+seta hud_panel_scoreboard_bg_alpha ""
+seta hud_panel_scoreboard_bg_border ""
+seta hud_panel_scoreboard_bg_padding ""
+seta hud_panel_scoreboard_fadeinspeed "10"
+seta hud_panel_scoreboard_fadeoutspeed "5"
+seta hud_panel_scoreboard_respawntime_decimals "1"
+seta hud_panel_scoreboard_table_bg_alpha "0.8"
+seta hud_panel_scoreboard_table_bg_scale "0.25"
+seta hud_panel_scoreboard_table_fg_alpha "0.9"
+seta hud_panel_scoreboard_table_fg_alpha_self "1"
+seta hud_panel_scoreboard_table_highlight "1"
+seta hud_panel_scoreboard_table_highlight_alpha "0.08"
+seta hud_panel_scoreboard_table_highlight_alpha_self "0.3"
+seta hud_panel_scoreboard_bg_teams_color_team "0.7"
+seta hud_panel_scoreboard_accuracy_doublerows "1"
+seta hud_panel_scoreboard_accuracy_nocolors "0"
+
 menu_sync
index d6125985b8a42e180e753e91a4b3ee584bddf932..380b65814864325977cb29475ce31d896ed71dd0 100644 (file)
@@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5"
 seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0"
 seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0"
 
-seta _hud_panelorder "17 15 3 1 2 11 10 0 14 6 9 13 4 12 7 5 8 16 18 23 19 20 21 22 "
+seta _hud_panelorder "17 15 3 1 2 11 10 0 14 6 9 13 4 12 7 5 8 16 18 23 19 20 21 22 23 24 "
 
 seta hud_configure_grid "1"
 seta hud_configure_grid_xsize "0.010000"
@@ -231,7 +231,7 @@ seta hud_panel_engineinfo_framecounter_time "0.1"
 seta hud_panel_engineinfo_framecounter_decimals "0"
 
 seta hud_panel_infomessages_pos "0.710000 0"
-seta hud_panel_infomessages_size "0.290000 0.100000"
+seta hud_panel_infomessages_size "0.280000 0.090000"
 seta hud_panel_infomessages_bg "0"
 seta hud_panel_infomessages_bg_color ""
 seta hud_panel_infomessages_bg_color_team ""
@@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team ""
 seta hud_panel_mapvote_bg_alpha ""
 seta hud_panel_mapvote_bg_border ""
 seta hud_panel_mapvote_bg_padding ""
+seta hud_panel_mapvote_highlight_border "1"
 
 seta hud_panel_itemstime_pos "0.000000 0.310000"
 seta hud_panel_itemstime_size "0.070000 0.180000"
@@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border ""
 seta hud_panel_quickmenu_bg_padding ""
 seta hud_panel_quickmenu_align "0"
 
+seta hud_panel_scoreboard_pos "0.150000 0.150000"
+seta hud_panel_scoreboard_size "0.700000 0.700000"
+seta hud_panel_scoreboard_bg "0"
+seta hud_panel_scoreboard_bg_color ""
+seta hud_panel_scoreboard_bg_color_team ""
+seta hud_panel_scoreboard_bg_alpha ""
+seta hud_panel_scoreboard_bg_border ""
+seta hud_panel_scoreboard_bg_padding ""
+seta hud_panel_scoreboard_fadeinspeed "10"
+seta hud_panel_scoreboard_fadeoutspeed "5"
+seta hud_panel_scoreboard_respawntime_decimals "1"
+seta hud_panel_scoreboard_table_bg_alpha "0.8"
+seta hud_panel_scoreboard_table_bg_scale "0.25"
+seta hud_panel_scoreboard_table_fg_alpha "0.9"
+seta hud_panel_scoreboard_table_fg_alpha_self "1"
+seta hud_panel_scoreboard_table_highlight "1"
+seta hud_panel_scoreboard_table_highlight_alpha "0.08"
+seta hud_panel_scoreboard_table_highlight_alpha_self "0.3"
+seta hud_panel_scoreboard_bg_teams_color_team "0.7"
+seta hud_panel_scoreboard_accuracy_doublerows "1"
+seta hud_panel_scoreboard_accuracy_nocolors "0"
+
 menu_sync
index 4ee8005213de354475b006596b82655b7c5408f9..96b4f508f50c1a193ad5dca91664274a96b6d781 100644 (file)
@@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5"
 seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0"
 seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0"
 
-seta _hud_panelorder "17 15 10 9 6 8 14 5 0 4 13 2 7 1 3 11 12 16 18 23 19 20 21 22 "
+seta _hud_panelorder "17 15 10 9 6 8 14 5 0 4 13 2 7 1 3 11 12 16 18 23 19 20 21 22 23 24 "
 
 seta hud_configure_grid "1"
 seta hud_configure_grid_xsize "0.010000"
@@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team ""
 seta hud_panel_mapvote_bg_alpha ""
 seta hud_panel_mapvote_bg_border ""
 seta hud_panel_mapvote_bg_padding ""
+seta hud_panel_mapvote_highlight_border "1"
 
 seta hud_panel_itemstime_pos "0.020000 0.490000"
 seta hud_panel_itemstime_size "0.090000 0.140000"
@@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border ""
 seta hud_panel_quickmenu_bg_padding ""
 seta hud_panel_quickmenu_align "1"
 
+seta hud_panel_scoreboard_pos "0.150000 0.150000"
+seta hud_panel_scoreboard_size "0.700000 0.700000"
+seta hud_panel_scoreboard_bg ""
+seta hud_panel_scoreboard_bg_color ""
+seta hud_panel_scoreboard_bg_color_team ""
+seta hud_panel_scoreboard_bg_alpha ""
+seta hud_panel_scoreboard_bg_border ""
+seta hud_panel_scoreboard_bg_padding ""
+seta hud_panel_scoreboard_fadeinspeed "10"
+seta hud_panel_scoreboard_fadeoutspeed "5"
+seta hud_panel_scoreboard_respawntime_decimals "1"
+seta hud_panel_scoreboard_table_bg_alpha "0.8"
+seta hud_panel_scoreboard_table_bg_scale "0.25"
+seta hud_panel_scoreboard_table_fg_alpha "0.9"
+seta hud_panel_scoreboard_table_fg_alpha_self "1"
+seta hud_panel_scoreboard_table_highlight "1"
+seta hud_panel_scoreboard_table_highlight_alpha "0.08"
+seta hud_panel_scoreboard_table_highlight_alpha_self "0.3"
+seta hud_panel_scoreboard_bg_teams_color_team "0.7"
+seta hud_panel_scoreboard_accuracy_doublerows "1"
+seta hud_panel_scoreboard_accuracy_nocolors "0"
+
 menu_sync
index df565ef686ebf47a92f2541f74183dfa937c2aa8..ae786574f3bfcbd6a59ed889927f4d333617deb0 100644 (file)
@@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5"
 seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0"
 seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0"
 
-seta _hud_panelorder "17 15 0 11 8 5 6 14 9 13 7 2 3 1 10 12 4 16 18 23 19 20 21 22 "
+seta _hud_panelorder "17 15 0 11 8 5 6 14 9 13 7 2 3 1 10 12 4 16 18 23 19 20 21 22 23 24 "
 
 seta hud_configure_grid "1"
 seta hud_configure_grid_xsize "0.010000"
@@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team ""
 seta hud_panel_mapvote_bg_alpha ""
 seta hud_panel_mapvote_bg_border ""
 seta hud_panel_mapvote_bg_padding ""
+seta hud_panel_mapvote_highlight_border "1"
 
 seta hud_panel_itemstime_pos "0.000000 0.290000"
 seta hud_panel_itemstime_size "0.150000 0.060000"
@@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border ""
 seta hud_panel_quickmenu_bg_padding ""
 seta hud_panel_quickmenu_align "0"
 
+seta hud_panel_scoreboard_pos "0.150000 0.150000"
+seta hud_panel_scoreboard_size "0.700000 0.700000"
+seta hud_panel_scoreboard_bg ""
+seta hud_panel_scoreboard_bg_color ""
+seta hud_panel_scoreboard_bg_color_team ""
+seta hud_panel_scoreboard_bg_alpha "0.7"
+seta hud_panel_scoreboard_bg_border ""
+seta hud_panel_scoreboard_bg_padding ""
+seta hud_panel_scoreboard_fadeinspeed "10"
+seta hud_panel_scoreboard_fadeoutspeed "5"
+seta hud_panel_scoreboard_respawntime_decimals "1"
+seta hud_panel_scoreboard_table_bg_alpha "0.8"
+seta hud_panel_scoreboard_table_bg_scale "0.25"
+seta hud_panel_scoreboard_table_fg_alpha "0.9"
+seta hud_panel_scoreboard_table_fg_alpha_self "1"
+seta hud_panel_scoreboard_table_highlight "1"
+seta hud_panel_scoreboard_table_highlight_alpha "0.1"
+seta hud_panel_scoreboard_table_highlight_alpha_self "0.25"
+seta hud_panel_scoreboard_bg_teams_color_team "0.7"
+seta hud_panel_scoreboard_accuracy_doublerows "1"
+seta hud_panel_scoreboard_accuracy_nocolors "0"
+
 menu_sync
index 6dfec5088f886a3b229b0185c35f2e6d24a4ff17..8e70653bb205cd497447bb84b89a8ff17ba19854 100644 (file)
@@ -19,3 +19,8 @@ set sv_minigames_snake_wrap 0 "Wrap around the edges of the screen instead of dy
 set sv_minigames_snake_delay_initial 0.7 "Initial delay between snake movement"
 set sv_minigames_snake_delay_multiplier 50 "Multiplier of incremental of movement speed (player_score / cvar)"
 set sv_minigames_snake_delay_min 0.1 "Minimum delay between snake movement (at fastest rate)"
+set sv_minigames_snake_lives 3
+
+
+// Bulldozer
+set sv_minigames_bulldozer_startlevel "level1"
index d82be653c503d8c73def6e4380f3f29b13b0bfd0..6d12e37cf183f113d93440926454858bb6592b8d 100644 (file)
 // bulldozer storage "level1" last updated 16-11-2015 03:11:33
 nextlevel = "level2"
-"k11" 1 "0 -1 0"
-"a20" 6 "0 0 0"
-"a19" 6 "0 0 0"
-"a18" 6 "0 0 0"
-"a17" 6 "0 0 0"
-"a15" 6 "0 0 0"
-"a14" 6 "0 0 0"
-"a13" 6 "0 0 0"
-"a12" 6 "0 0 0"
-"a11" 6 "0 0 0"
-"a10" 6 "0 0 0"
-"a9" 6 "0 0 0"
-"a8" 6 "0 0 0"
-"a7" 6 "0 0 0"
-"a6" 6 "0 0 0"
-"a5" 6 "0 0 0"
-"a4" 6 "0 0 0"
-"a3" 6 "0 0 0"
-"a2" 6 "0 0 0"
-"a1" 6 "0 0 0"
-"b1" 6 "0 0 0"
-"c1" 6 "0 0 0"
-"d1" 6 "0 0 0"
-"e1" 6 "0 0 0"
-"f1" 6 "0 0 0"
-"g1" 6 "0 0 0"
-"h1" 6 "0 0 0"
-"i1" 6 "0 0 0"
-"j1" 6 "0 0 0"
-"k1" 6 "0 0 0"
-"l1" 6 "0 0 0"
-"m1" 6 "0 0 0"
-"n1" 6 "0 0 0"
-"o1" 6 "0 0 0"
-"p1" 6 "0 0 0"
-"q1" 6 "0 0 0"
-"r1" 6 "0 0 0"
-"s1" 6 "0 0 0"
-"t1" 6 "0 0 0"
-"t2" 6 "0 0 0"
-"t3" 6 "0 0 0"
-"t4" 6 "0 0 0"
-"t5" 6 "0 0 0"
-"t6" 6 "0 0 0"
-"t7" 6 "0 0 0"
-"t8" 6 "0 0 0"
-"t9" 6 "0 0 0"
-"t10" 6 "0 0 0"
-"t11" 6 "0 0 0"
-"t12" 6 "0 0 0"
-"t13" 6 "0 0 0"
-"t14" 6 "0 0 0"
-"t15" 6 "0 0 0"
-"t16" 6 "0 0 0"
-"t17" 6 "0 0 0"
-"t18" 6 "0 0 0"
-"t19" 6 "0 0 0"
-"t20" 6 "0 0 0"
-"s20" 6 "0 0 0"
-"r20" 6 "0 0 0"
-"q20" 6 "0 0 0"
-"p20" 6 "0 0 0"
-"o20" 6 "0 0 0"
-"n20" 6 "0 0 0"
-"m20" 6 "0 0 0"
-"l20" 6 "0 0 0"
-"k20" 6 "0 0 0"
-"j20" 6 "0 0 0"
-"i20" 6 "0 0 0"
-"h20" 6 "0 0 0"
-"g20" 6 "0 0 0"
-"f20" 6 "0 0 0"
-"e20" 6 "0 0 0"
-"d20" 6 "0 0 0"
-"c20" 6 "0 0 0"
-"b20" 6 "0 0 0"
-"a16" 6 "0 0 0"
-"b2" 4 "0 0 0"
-"c18" 6 "0 0 0"
-"e18" 6 "0 0 0"
-"e17" 6 "0 0 0"
-"e16" 6 "0 0 0"
-"e15" 6 "0 0 0"
-"e14" 6 "0 0 0"
-"e13" 6 "0 0 0"
-"e12" 6 "0 0 0"
-"e11" 6 "0 0 0"
-"f10" 4 "0 0 0"
-"e9" 6 "0 0 0"
-"e8" 6 "0 0 0"
-"e7" 6 "0 0 0"
-"e6" 6 "0 0 0"
-"e5" 6 "0 0 0"
-"e4" 6 "0 0 0"
-"e3" 6 "0 0 0"
-"e2" 6 "0 0 0"
-"e19" 6 "0 0 0"
-"c17" 6 "0 0 0"
-"c16" 6 "0 0 0"
-"c15" 6 "0 0 0"
-"c14" 6 "0 0 0"
-"c13" 6 "0 0 0"
-"c12" 6 "0 0 0"
-"c11" 6 "0 0 0"
-"c10" 6 "0 0 0"
-"c9" 6 "0 0 0"
-"c8" 6 "0 0 0"
-"c7" 6 "0 0 0"
-"c6" 6 "0 0 0"
-"c5" 6 "0 0 0"
-"c4" 6 "0 0 0"
-"c3" 6 "0 0 0"
-"c2" 6 "0 0 0"
-"c19" 6 "0 0 0"
-"i14" 5 "0 0 0"
-"f12" 4 "0 0 0"
-"f11" 4 "0 0 0"
-"e10" 6 "0 0 0"
-"i10" 5 "0 0 0"
-"h10" 5 "0 0 0"
-"j10" 5 "0 0 0"
-"j9" 5 "0 0 0"
-"g11" 5 "0 0 0"
-"j8" 5 "0 0 0"
-"k8" 5 "0 0 0"
-"l8" 5 "0 0 0"
-"l9" 5 "0 0 0"
-"i8" 4 "0 0 0"
-"i9" 4 "0 0 0"
-"m11" 5 "0 0 0"
-"l10" 5 "0 0 0"
-"n11" 5 "0 0 0"
-"n13" 5 "0 0 0"
-"n12" 5 "0 0 0"
-"m13" 5 "0 0 0"
-"k13" 5 "0 0 0"
-"k14" 5 "0 0 0"
-"k15" 5 "0 0 0"
-"j15" 5 "0 0 0"
-"i13" 5 "0 0 0"
-"h15" 4 "0 0 0"
-"h14" 4 "0 0 0"
-"h13" 4 "0 0 0"
-"g15" 4 "0 0 0"
-"p17" 6 "0 0 0"
-"p16" 6 "0 0 0"
-"l13" 5 "0 0 0"
-"h12" 5 "0 0 0"
-"i15" 5 "0 0 0"
-"l15" 4 "0 0 0"
-"m15" 4 "0 0 0"
-"l14" 4 "0 0 0"
-"m14" 4 "0 0 0"
-"n14" 4 "0 0 0"
-"p15" 6 "0 0 0"
-"p14" 6 "0 0 0"
-"l11" 5 "0 0 0"
-"h11" 2 "0 0 0"
-"k9" 2 "0 0 0"
-"j14" 2 "0 0 0"
-"m12" 2 "0 0 0"
-"l12" 3 "0 0 0"
-"j11" 3 "0 0 0"
-"j12" 3 "0 0 0"
-"k10" 3 "0 0 0"
-"i12" 5 "0 0 0"
-"g12" 5 "0 0 0"
-"g10" 5 "0 0 0"
-"p13" 6 "0 0 0"
-"p12" 6 "0 0 0"
-"p11" 6 "0 0 0"
-"p10" 6 "0 0 0"
-"p9" 6 "0 0 0"
-"p8" 6 "0 0 0"
-"p7" 6 "0 0 0"
-"p6" 6 "0 0 0"
-"p5" 6 "0 0 0"
-"p4" 6 "0 0 0"
-"p3" 6 "0 0 0"
-"p2" 6 "0 0 0"
-"p18" 6 "0 0 0"
-"p19" 6 "0 0 0"
-"r16" 6 "0 0 0"
-"r15" 6 "0 0 0"
-"r14" 6 "0 0 0"
-"r13" 6 "0 0 0"
-"r12" 6 "0 0 0"
-"r11" 6 "0 0 0"
-"r10" 6 "0 0 0"
-"r9" 6 "0 0 0"
-"r8" 6 "0 0 0"
-"r7" 6 "0 0 0"
-"r6" 6 "0 0 0"
-"r5" 6 "0 0 0"
-"r4" 6 "0 0 0"
-"r3" 6 "0 0 0"
-"r2" 6 "0 0 0"
-"r17" 6 "0 0 0"
-"r18" 6 "0 0 0"
-"r19" 6 "0 0 0"
-"d12" 4 "0 0 0"
-"d6" 4 "0 0 0"
-"f6" 4 "0 0 0"
-"f7" 4 "0 0 0"
-"f8" 4 "0 0 0"
-"f9" 4 "0 0 0"
-"h9" 4 "0 0 0"
-"g9" 4 "0 0 0"
-"g8" 4 "0 0 0"
-"g7" 4 "0 0 0"
-"g6" 4 "0 0 0"
-"g5" 4 "0 0 0"
-"g4" 4 "0 0 0"
-"h6" 4 "0 0 0"
-"h7" 4 "0 0 0"
-"h8" 4 "0 0 0"
-"i7" 4 "0 0 0"
-"i6" 4 "0 0 0"
-"j6" 4 "0 0 0"
-"k6" 4 "0 0 0"
-"j7" 4 "0 0 0"
-"k7" 4 "0 0 0"
-"l6" 4 "0 0 0"
-"l7" 4 "0 0 0"
-"m10" 4 "0 0 0"
-"m8" 4 "0 0 0"
-"m7" 4 "0 0 0"
-"m6" 4 "0 0 0"
-"m5" 4 "0 0 0"
-"m4" 4 "0 0 0"
-"m3" 4 "0 0 0"
-"m2" 4 "0 0 0"
-"f19" 4 "0 0 0"
-"f18" 4 "0 0 0"
-"f17" 4 "0 0 0"
-"f16" 4 "0 0 0"
-"f15" 4 "0 0 0"
-"f13" 4 "0 0 0"
-"l3" 4 "0 0 0"
-"l4" 4 "0 0 0"
-"l5" 4 "0 0 0"
-"j3" 4 "0 0 0"
-"j2" 4 "0 0 0"
-"k3" 4 "0 0 0"
-"i3" 4 "0 0 0"
-"g3" 4 "0 0 0"
-"h3" 4 "0 0 0"
-"f3" 4 "0 0 0"
-"d3" 4 "0 0 0"
-"d19" 4 "0 0 0"
-"d17" 4 "0 0 0"
-"d5" 4 "0 0 0"
-"d4" 4 "0 0 0"
-"d7" 4 "0 0 0"
-"h5" 4 "0 0 0"
-"h4" 4 "0 0 0"
-"i4" 4 "0 0 0"
-"j4" 4 "0 0 0"
-"k5" 4 "0 0 0"
-"j5" 4 "0 0 0"
-"i5" 4 "0 0 0"
-"d18" 4 "0 0 0"
-"d10" 4 "0 0 0"
-"g13" 4 "0 0 0"
-"g14" 4 "0 0 0"
-"k4" 4 "0 0 0"
-"d8" 4 "0 0 0"
-"d9" 4 "0 0 0"
-"g16" 4 "0 0 0"
-"g17" 4 "0 0 0"
-"g18" 4 "0 0 0"
-"d11" 4 "0 0 0"
-"g19" 4 "0 0 0"
-"h19" 4 "0 0 0"
-"h18" 4 "0 0 0"
-"h16" 4 "0 0 0"
-"h17" 4 "0 0 0"
-"i16" 4 "0 0 0"
-"i17" 4 "0 0 0"
-"i18" 4 "0 0 0"
-"j19" 4 "0 0 0"
-"j18" 4 "0 0 0"
-"j17" 4 "0 0 0"
-"i19" 4 "0 0 0"
-"j16" 4 "0 0 0"
-"k16" 4 "0 0 0"
-"k17" 4 "0 0 0"
-"k18" 4 "0 0 0"
-"k19" 4 "0 0 0"
-"l19" 4 "0 0 0"
-"f5" 4 "0 0 0"
-"f4" 4 "0 0 0"
-"l18" 4 "0 0 0"
-"l17" 4 "0 0 0"
-"l16" 4 "0 0 0"
-"m16" 4 "0 0 0"
-"m17" 4 "0 0 0"
-"m18" 4 "0 0 0"
-"n19" 4 "0 0 0"
-"m19" 4 "0 0 0"
-"n18" 4 "0 0 0"
-"n17" 4 "0 0 0"
-"n16" 4 "0 0 0"
-"n15" 4 "0 0 0"
-"n10" 4 "0 0 0"
-"n9" 4 "0 0 0"
-"n8" 4 "0 0 0"
-"n7" 4 "0 0 0"
-"n6" 4 "0 0 0"
-"n5" 4 "0 0 0"
-"n4" 4 "0 0 0"
-"n3" 4 "0 0 0"
-"m9" 4 "0 0 0"
-"s2" 4 "0 0 0"
-"s3" 4 "0 0 0"
-"s4" 4 "0 0 0"
-"s5" 4 "0 0 0"
-"s6" 4 "0 0 0"
-"s7" 4 "0 0 0"
-"s8" 4 "0 0 0"
-"s9" 4 "0 0 0"
-"s10" 4 "0 0 0"
-"s11" 4 "0 0 0"
-"s12" 4 "0 0 0"
-"s13" 4 "0 0 0"
-"s14" 4 "0 0 0"
-"s15" 4 "0 0 0"
-"s16" 4 "0 0 0"
-"s17" 4 "0 0 0"
-"s18" 4 "0 0 0"
-"s19" 4 "0 0 0"
-"q19" 4 "0 0 0"
-"o19" 4 "0 0 0"
-"o18" 4 "0 0 0"
-"o17" 4 "0 0 0"
-"o16" 4 "0 0 0"
-"o15" 4 "0 0 0"
-"o14" 4 "0 0 0"
-"o13" 4 "0 0 0"
-"o12" 4 "0 0 0"
-"o11" 4 "0 0 0"
-"o10" 4 "0 0 0"
-"o9" 4 "0 0 0"
-"o8" 4 "0 0 0"
-"o7" 4 "0 0 0"
-"o6" 4 "0 0 0"
-"o5" 4 "0 0 0"
-"o4" 4 "0 0 0"
-"o3" 4 "0 0 0"
-"q8" 4 "0 0 0"
-"q15" 4 "0 0 0"
-"q16" 4 "0 0 0"
-"q17" 4 "0 0 0"
-"q18" 4 "0 0 0"
-"q3" 4 "0 0 0"
-"q4" 4 "0 0 0"
-"q6" 4 "0 0 0"
-"q7" 4 "0 0 0"
-"q5" 4 "0 0 0"
-"q9" 4 "0 0 0"
-"q10" 4 "0 0 0"
-"q11" 4 "0 0 0"
-"q12" 4 "0 0 0"
-"q13" 4 "0 0 0"
-"q14" 4 "0 0 0"
-"b14" 4 "0 0 0"
-"b13" 4 "0 0 0"
-"d13" 4 "0 0 0"
-"d14" 4 "0 0 0"
-"d15" 4 "0 0 0"
-"b15" 4 "0 0 0"
-"b16" 4 "0 0 0"
-"d16" 4 "0 0 0"
-"b17" 4 "0 0 0"
-"b18" 4 "0 0 0"
-"b19" 4 "0 0 0"
-"f2" 4 "0 0 0"
-"g2" 4 "0 0 0"
-"h2" 4 "0 0 0"
-"i2" 4 "0 0 0"
-"k2" 4 "0 0 0"
-"l2" 4 "0 0 0"
-"n2" 4 "0 0 0"
-"o2" 4 "0 0 0"
-"q2" 4 "0 0 0"
-"f14" 4 "0 0 0"
-"b12" 4 "0 0 0"
-"b11" 4 "0 0 0"
-"b10" 4 "0 0 0"
-"b9" 4 "0 0 0"
-"b8" 4 "0 0 0"
-"b7" 4 "0 0 0"
-"b6" 4 "0 0 0"
-"b5" 4 "0 0 0"
-"b4" 4 "0 0 0"
-"b3" 4 "0 0 0"
-"d2" 4 "0 0 0"
+"k11" 1 1
+"a20" 6 0
+"a19" 6 0
+"a18" 6 0
+"a17" 6 0
+"a15" 6 0
+"a14" 6 0
+"a13" 6 0
+"a12" 6 0
+"a11" 6 0
+"a10" 6 0
+"a9" 6 0
+"a8" 6 0
+"a7" 6 0
+"a6" 6 0
+"a5" 6 0
+"a4" 6 0
+"a3" 6 0
+"a2" 6 0
+"a1" 6 0
+"b1" 6 0
+"c1" 6 0
+"d1" 6 0
+"e1" 6 0
+"f1" 6 0
+"g1" 6 0
+"h1" 6 0
+"i1" 6 0
+"j1" 6 0
+"k1" 6 0
+"l1" 6 0
+"m1" 6 0
+"n1" 6 0
+"o1" 6 0
+"p1" 6 0
+"q1" 6 0
+"r1" 6 0
+"s1" 6 0
+"t1" 6 0
+"t2" 6 0
+"t3" 6 0
+"t4" 6 0
+"t5" 6 0
+"t6" 6 0
+"t7" 6 0
+"t8" 6 0
+"t9" 6 0
+"t10" 6 0
+"t11" 6 0
+"t12" 6 0
+"t13" 6 0
+"t14" 6 0
+"t15" 6 0
+"t16" 6 0
+"t17" 6 0
+"t18" 6 0
+"t19" 6 0
+"t20" 6 0
+"s20" 6 0
+"r20" 6 0
+"q20" 6 0
+"p20" 6 0
+"o20" 6 0
+"n20" 6 0
+"m20" 6 0
+"l20" 6 0
+"k20" 6 0
+"j20" 6 0
+"i20" 6 0
+"h20" 6 0
+"g20" 6 0
+"f20" 6 0
+"e20" 6 0
+"d20" 6 0
+"c20" 6 0
+"b20" 6 0
+"a16" 6 0
+"b2" 4 0
+"c18" 6 0
+"e18" 6 0
+"e17" 6 0
+"e16" 6 0
+"e15" 6 0
+"e14" 6 0
+"e13" 6 0
+"e12" 6 0
+"e11" 6 0
+"f10" 4 0
+"e9" 6 0
+"e8" 6 0
+"e7" 6 0
+"e6" 6 0
+"e5" 6 0
+"e4" 6 0
+"e3" 6 0
+"e2" 6 0
+"e19" 6 0
+"c17" 6 0
+"c16" 6 0
+"c15" 6 0
+"c14" 6 0
+"c13" 6 0
+"c12" 6 0
+"c11" 6 0
+"c10" 6 0
+"c9" 6 0
+"c8" 6 0
+"c7" 6 0
+"c6" 6 0
+"c5" 6 0
+"c4" 6 0
+"c3" 6 0
+"c2" 6 0
+"c19" 6 0
+"i14" 5 0
+"f12" 4 0
+"f11" 4 0
+"e10" 6 0
+"i10" 5 0
+"h10" 5 0
+"j10" 5 0
+"j9" 5 0
+"g11" 5 0
+"j8" 5 0
+"k8" 5 0
+"l8" 5 0
+"l9" 5 0
+"i8" 4 0
+"i9" 4 0
+"m11" 5 0
+"l10" 5 0
+"n11" 5 0
+"n13" 5 0
+"n12" 5 0
+"m13" 5 0
+"k13" 5 0
+"k14" 5 0
+"k15" 5 0
+"j15" 5 0
+"i13" 5 0
+"h15" 4 0
+"h14" 4 0
+"h13" 4 0
+"g15" 4 0
+"p17" 6 0
+"p16" 6 0
+"l13" 5 0
+"h12" 5 0
+"i15" 5 0
+"l15" 4 0
+"m15" 4 0
+"l14" 4 0
+"m14" 4 0
+"n14" 4 0
+"p15" 6 0
+"p14" 6 0
+"l11" 5 0
+"h11" 2 0
+"k9" 2 0
+"j14" 2 0
+"m12" 2 0
+"l12" 3 0
+"j11" 3 0
+"j12" 3 0
+"k10" 3 0
+"i12" 5 0
+"g12" 5 0
+"g10" 5 0
+"p13" 6 0
+"p12" 6 0
+"p11" 6 0
+"p10" 6 0
+"p9" 6 0
+"p8" 6 0
+"p7" 6 0
+"p6" 6 0
+"p5" 6 0
+"p4" 6 0
+"p3" 6 0
+"p2" 6 0
+"p18" 6 0
+"p19" 6 0
+"r16" 6 0
+"r15" 6 0
+"r14" 6 0
+"r13" 6 0
+"r12" 6 0
+"r11" 6 0
+"r10" 6 0
+"r9" 6 0
+"r8" 6 0
+"r7" 6 0
+"r6" 6 0
+"r5" 6 0
+"r4" 6 0
+"r3" 6 0
+"r2" 6 0
+"r17" 6 0
+"r18" 6 0
+"r19" 6 0
+"d12" 4 0
+"d6" 4 0
+"f6" 4 0
+"f7" 4 0
+"f8" 4 0
+"f9" 4 0
+"h9" 4 0
+"g9" 4 0
+"g8" 4 0
+"g7" 4 0
+"g6" 4 0
+"g5" 4 0
+"g4" 4 0
+"h6" 4 0
+"h7" 4 0
+"h8" 4 0
+"i7" 4 0
+"i6" 4 0
+"j6" 4 0
+"k6" 4 0
+"j7" 4 0
+"k7" 4 0
+"l6" 4 0
+"l7" 4 0
+"m10" 4 0
+"m8" 4 0
+"m7" 4 0
+"m6" 4 0
+"m5" 4 0
+"m4" 4 0
+"m3" 4 0
+"m2" 4 0
+"f19" 4 0
+"f18" 4 0
+"f17" 4 0
+"f16" 4 0
+"f15" 4 0
+"f13" 4 0
+"l3" 4 0
+"l4" 4 0
+"l5" 4 0
+"j3" 4 0
+"j2" 4 0
+"k3" 4 0
+"i3" 4 0
+"g3" 4 0
+"h3" 4 0
+"f3" 4 0
+"d3" 4 0
+"d19" 4 0
+"d17" 4 0
+"d5" 4 0
+"d4" 4 0
+"d7" 4 0
+"h5" 4 0
+"h4" 4 0
+"i4" 4 0
+"j4" 4 0
+"k5" 4 0
+"j5" 4 0
+"i5" 4 0
+"d18" 4 0
+"d10" 4 0
+"g13" 4 0
+"g14" 4 0
+"k4" 4 0
+"d8" 4 0
+"d9" 4 0
+"g16" 4 0
+"g17" 4 0
+"g18" 4 0
+"d11" 4 0
+"g19" 4 0
+"h19" 4 0
+"h18" 4 0
+"h16" 4 0
+"h17" 4 0
+"i16" 4 0
+"i17" 4 0
+"i18" 4 0
+"j19" 4 0
+"j18" 4 0
+"j17" 4 0
+"i19" 4 0
+"j16" 4 0
+"k16" 4 0
+"k17" 4 0
+"k18" 4 0
+"k19" 4 0
+"l19" 4 0
+"f5" 4 0
+"f4" 4 0
+"l18" 4 0
+"l17" 4 0
+"l16" 4 0
+"m16" 4 0
+"m17" 4 0
+"m18" 4 0
+"n19" 4 0
+"m19" 4 0
+"n18" 4 0
+"n17" 4 0
+"n16" 4 0
+"n15" 4 0
+"n10" 4 0
+"n9" 4 0
+"n8" 4 0
+"n7" 4 0
+"n6" 4 0
+"n5" 4 0
+"n4" 4 0
+"n3" 4 0
+"m9" 4 0
+"s2" 4 0
+"s3" 4 0
+"s4" 4 0
+"s5" 4 0
+"s6" 4 0
+"s7" 4 0
+"s8" 4 0
+"s9" 4 0
+"s10" 4 0
+"s11" 4 0
+"s12" 4 0
+"s13" 4 0
+"s14" 4 0
+"s15" 4 0
+"s16" 4 0
+"s17" 4 0
+"s18" 4 0
+"s19" 4 0
+"q19" 4 0
+"o19" 4 0
+"o18" 4 0
+"o17" 4 0
+"o16" 4 0
+"o15" 4 0
+"o14" 4 0
+"o13" 4 0
+"o12" 4 0
+"o11" 4 0
+"o10" 4 0
+"o9" 4 0
+"o8" 4 0
+"o7" 4 0
+"o6" 4 0
+"o5" 4 0
+"o4" 4 0
+"o3" 4 0
+"q8" 4 0
+"q15" 4 0
+"q16" 4 0
+"q17" 4 0
+"q18" 4 0
+"q3" 4 0
+"q4" 4 0
+"q6" 4 0
+"q7" 4 0
+"q5" 4 0
+"q9" 4 0
+"q10" 4 0
+"q11" 4 0
+"q12" 4 0
+"q13" 4 0
+"q14" 4 0
+"b14" 4 0
+"b13" 4 0
+"d13" 4 0
+"d14" 4 0
+"d15" 4 0
+"b15" 4 0
+"b16" 4 0
+"d16" 4 0
+"b17" 4 0
+"b18" 4 0
+"b19" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"k2" 4 0
+"l2" 4 0
+"n2" 4 0
+"o2" 4 0
+"q2" 4 0
+"f14" 4 0
+"b12" 4 0
+"b11" 4 0
+"b10" 4 0
+"b9" 4 0
+"b8" 4 0
+"b7" 4 0
+"b6" 4 0
+"b5" 4 0
+"b4" 4 0
+"b3" 4 0
+"d2" 4 0
index eb47650b3ffaae5b7eb8d6bc1d49510728dfeebe..38f0560db0b515ef40c6452a5977599757d8ae3c 100644 (file)
 // bulldozer storage "level10" last updated 11-06-2016 14:57:48
 nextlevel = "level11"
-"e13" 4 "0 -1 0"
-"d13" 4 "0 -1 0"
-"c13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"a13" 4 "0 -1 0"
-"a12" 4 "0 -1 0"
-"c10" 10 "0 -1 0"
-"c12" 4 "0 -1 0"
-"e12" 4 "0 -1 0"
-"e14" 10 "0 -1 0"
-"g12" 4 "0 -1 0"
-"j14" 5 "0 -1 0"
-"k13" 2 "0 -1 0"
-"k12" 2 "0 -1 0"
-"l12" 2 "0 -1 0"
-"l13" 2 "0 -1 0"
-"k11" 5 "0 -1 0"
-"k14" 5 "0 -1 0"
-"o12" 4 "0 -1 0"
-"r8" 10 "0 -1 0"
-"q12" 4 "0 -1 0"
-"r12" 10 "0 -1 0"
-"s12" 4 "0 -1 0"
-"s10" 10 "0 -1 0"
-"t11" 4 "0 -1 0"
-"s11" 4 "0 -1 0"
-"r11" 4 "0 -1 0"
-"q11" 4 "0 -1 0"
-"p11" 4 "0 -1 0"
-"o11" 4 "0 -1 0"
-"k15" 5 "0 -1 0"
-"m11" 1 "0 -1 0"
-"j12" 3 "0 -1 0"
-"j11" 3 "0 -1 0"
-"j10" 3 "0 -1 0"
-"l14" 3 "0 -1 0"
-"k16" 5 "0 -1 0"
-"g11" 4 "0 -1 0"
-"f11" 4 "0 -1 0"
-"e11" 4 "0 -1 0"
-"d11" 4 "0 -1 0"
-"c11" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"a11" 4 "0 -1 0"
-"a10" 4 "0 -1 0"
-"b10" 4 "0 -1 0"
-"e10" 10 "0 -1 0"
-"d10" 4 "0 -1 0"
-"d12" 10 "0 -1 0"
-"f10" 4 "0 -1 0"
-"g10" 4 "0 -1 0"
-"l16" 5 "0 -1 0"
-"t12" 10 "0 -1 0"
-"s14" 10 "0 -1 0"
-"r16" 10 "0 -1 0"
-"q14" 10 "0 -1 0"
-"p12" 10 "0 -1 0"
-"m16" 5 "0 -1 0"
-"o10" 4 "0 -1 0"
-"q10" 10 "0 -1 0"
-"p10" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"f12" 10 "0 -1 0"
-"t10" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"q9" 4 "0 -1 0"
-"p9" 4 "0 -1 0"
-"o9" 4 "0 -1 0"
-"n16" 5 "0 -1 0"
-"n15" 5 "0 -1 0"
-"n14" 5 "0 -1 0"
-"d16" 10 "0 -1 0"
-"c14" 10 "0 -1 0"
-"b12" 10 "0 -1 0"
-"n13" 5 "0 -1 0"
-"g9" 4 "0 -1 0"
-"f9" 4 "0 -1 0"
-"e9" 4 "0 -1 0"
-"d9" 4 "0 -1 0"
-"c9" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"a9" 4 "0 -1 0"
-"a8" 4 "0 -1 0"
-"b8" 4 "0 -1 0"
-"c8" 4 "0 -1 0"
-"e8" 4 "0 -1 0"
-"f8" 4 "0 -1 0"
-"g8" 4 "0 -1 0"
-"n12" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"m9" 5 "0 -1 0"
-"m8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"q8" 4 "0 -1 0"
-"d8" 10 "0 -1 0"
-"s8" 4 "0 -1 0"
-"t8" 4 "0 -1 0"
-"t7" 4 "0 -1 0"
-"s7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"p7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"h7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"a7" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"k6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"a4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"t3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"t2" 4 "0 -1 0"
-"t1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"a1" 4 "0 -1 0"
-"t13" 4 "0 -1 0"
-"s13" 4 "0 -1 0"
-"r13" 4 "0 -1 0"
-"q13" 4 "0 -1 0"
-"p13" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"l9" 5 "0 -1 0"
-"l8" 5 "0 -1 0"
-"k8" 5 "0 -1 0"
-"j8" 5 "0 -1 0"
-"g13" 4 "0 -1 0"
-"f13" 4 "0 -1 0"
-"f14" 4 "0 -1 0"
-"d14" 4 "0 -1 0"
-"b14" 4 "0 -1 0"
-"a14" 4 "0 -1 0"
-"a15" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"d15" 4 "0 -1 0"
-"e15" 4 "0 -1 0"
-"f15" 4 "0 -1 0"
-"g15" 4 "0 -1 0"
-"h15" 4 "0 -1 0"
-"i15" 4 "0 -1 0"
-"j15" 4 "0 -1 0"
-"i8" 5 "0 -1 0"
-"h8" 5 "0 -1 0"
-"o15" 4 "0 -1 0"
-"p15" 4 "0 -1 0"
-"q15" 4 "0 -1 0"
-"r15" 4 "0 -1 0"
-"s15" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"t14" 4 "0 -1 0"
-"r14" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
-"o14" 4 "0 -1 0"
-"h9" 5 "0 -1 0"
-"h10" 5 "0 -1 0"
-"h11" 5 "0 -1 0"
-"i14" 4 "0 -1 0"
-"h14" 4 "0 -1 0"
-"g14" 4 "0 -1 0"
-"t16" 4 "0 -1 0"
-"s16" 4 "0 -1 0"
-"q16" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"o16" 4 "0 -1 0"
-"h12" 5 "0 -1 0"
-"h13" 5 "0 -1 0"
-"i13" 5 "0 -1 0"
-"j13" 5 "0 -1 0"
-"j16" 4 "0 -1 0"
-"i16" 4 "0 -1 0"
-"h16" 4 "0 -1 0"
-"g16" 4 "0 -1 0"
-"f16" 4 "0 -1 0"
-"e16" 4 "0 -1 0"
-"c16" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"a16" 4 "0 -1 0"
-"a17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"d17" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"q17" 4 "0 -1 0"
-"r17" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"t17" 4 "0 -1 0"
-"t18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"a18" 4 "0 -1 0"
-"a19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"t19" 4 "0 -1 0"
-"t20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"a20" 4 "0 -1 0"
+"e13" 4 0
+"d13" 4 0
+"c13" 4 0
+"b13" 4 0
+"a13" 4 0
+"a12" 4 0
+"c10" 10 0
+"c12" 4 0
+"e12" 4 0
+"e14" 10 0
+"g12" 4 0
+"j14" 5 0
+"k13" 2 0
+"k12" 2 0
+"l12" 2 0
+"l13" 2 0
+"k11" 5 0
+"k14" 5 0
+"o12" 4 0
+"r8" 10 0
+"q12" 4 0
+"r12" 10 0
+"s12" 4 0
+"s10" 10 0
+"t11" 4 0
+"s11" 4 0
+"r11" 4 0
+"q11" 4 0
+"p11" 4 0
+"o11" 4 0
+"k15" 5 0
+"m11" 1 1
+"j12" 3 0
+"j11" 3 0
+"j10" 3 0
+"l14" 3 0
+"k16" 5 0
+"g11" 4 0
+"f11" 4 0
+"e11" 4 0
+"d11" 4 0
+"c11" 4 0
+"b11" 4 0
+"a11" 4 0
+"a10" 4 0
+"b10" 4 0
+"e10" 10 0
+"d10" 4 0
+"d12" 10 0
+"f10" 4 0
+"g10" 4 0
+"l16" 5 0
+"t12" 10 0
+"s14" 10 0
+"r16" 10 0
+"q14" 10 0
+"p12" 10 0
+"m16" 5 0
+"o10" 4 0
+"q10" 10 0
+"p10" 4 0
+"r10" 4 0
+"f12" 10 0
+"t10" 4 0
+"t9" 4 0
+"s9" 4 0
+"r9" 4 0
+"q9" 4 0
+"p9" 4 0
+"o9" 4 0
+"n16" 5 0
+"n15" 5 0
+"n14" 5 0
+"d16" 10 0
+"c14" 10 0
+"b12" 10 0
+"n13" 5 0
+"g9" 4 0
+"f9" 4 0
+"e9" 4 0
+"d9" 4 0
+"c9" 4 0
+"b9" 4 0
+"a9" 4 0
+"a8" 4 0
+"b8" 4 0
+"c8" 4 0
+"e8" 4 0
+"f8" 4 0
+"g8" 4 0
+"n12" 5 0
+"n11" 5 0
+"n10" 5 0
+"n9" 5 0
+"m9" 5 0
+"m8" 4 0
+"n8" 4 0
+"o8" 4 0
+"p8" 4 0
+"q8" 4 0
+"d8" 10 0
+"s8" 4 0
+"t8" 4 0
+"t7" 4 0
+"s7" 4 0
+"r7" 4 0
+"q7" 4 0
+"p7" 4 0
+"o7" 4 0
+"n7" 4 0
+"m7" 4 0
+"l7" 4 0
+"k7" 4 0
+"j7" 4 0
+"i7" 4 0
+"h7" 4 0
+"g7" 4 0
+"f7" 4 0
+"e7" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"a7" 4 0
+"a6" 4 0
+"b6" 4 0
+"c6" 4 0
+"d6" 4 0
+"e6" 4 0
+"f6" 4 0
+"g6" 4 0
+"h6" 4 0
+"i6" 4 0
+"j6" 4 0
+"k6" 4 0
+"l6" 4 0
+"m6" 4 0
+"n6" 4 0
+"o6" 4 0
+"p6" 4 0
+"q6" 4 0
+"r6" 4 0
+"s6" 4 0
+"t6" 4 0
+"t5" 4 0
+"s5" 4 0
+"r5" 4 0
+"q5" 4 0
+"p5" 4 0
+"o5" 4 0
+"n5" 4 0
+"m5" 4 0
+"l5" 4 0
+"k5" 4 0
+"j5" 4 0
+"i5" 4 0
+"h5" 4 0
+"g5" 4 0
+"f5" 4 0
+"e5" 4 0
+"d5" 4 0
+"c5" 4 0
+"b5" 4 0
+"a5" 4 0
+"a4" 4 0
+"b4" 4 0
+"c4" 4 0
+"d4" 4 0
+"e4" 4 0
+"f4" 4 0
+"g4" 4 0
+"h4" 4 0
+"i4" 4 0
+"j4" 4 0
+"k4" 4 0
+"l4" 4 0
+"m4" 4 0
+"n4" 4 0
+"o4" 4 0
+"p4" 4 0
+"q4" 4 0
+"r4" 4 0
+"s4" 4 0
+"t4" 4 0
+"t3" 4 0
+"s3" 4 0
+"r3" 4 0
+"q3" 4 0
+"p3" 4 0
+"o3" 4 0
+"n3" 4 0
+"m3" 4 0
+"l3" 4 0
+"k3" 4 0
+"j3" 4 0
+"i3" 4 0
+"h3" 4 0
+"g3" 4 0
+"f3" 4 0
+"e3" 4 0
+"d3" 4 0
+"c3" 4 0
+"b3" 4 0
+"a3" 4 0
+"a2" 4 0
+"b2" 4 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"k2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"s2" 4 0
+"t2" 4 0
+"t1" 4 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"a1" 4 0
+"t13" 4 0
+"s13" 4 0
+"r13" 4 0
+"q13" 4 0
+"p13" 4 0
+"o13" 4 0
+"l9" 5 0
+"l8" 5 0
+"k8" 5 0
+"j8" 5 0
+"g13" 4 0
+"f13" 4 0
+"f14" 4 0
+"d14" 4 0
+"b14" 4 0
+"a14" 4 0
+"a15" 4 0
+"b15" 4 0
+"c15" 4 0
+"d15" 4 0
+"e15" 4 0
+"f15" 4 0
+"g15" 4 0
+"h15" 4 0
+"i15" 4 0
+"j15" 4 0
+"i8" 5 0
+"h8" 5 0
+"o15" 4 0
+"p15" 4 0
+"q15" 4 0
+"r15" 4 0
+"s15" 4 0
+"t15" 4 0
+"t14" 4 0
+"r14" 4 0
+"p14" 4 0
+"o14" 4 0
+"h9" 5 0
+"h10" 5 0
+"h11" 5 0
+"i14" 4 0
+"h14" 4 0
+"g14" 4 0
+"t16" 4 0
+"s16" 4 0
+"q16" 4 0
+"p16" 4 0
+"o16" 4 0
+"h12" 5 0
+"h13" 5 0
+"i13" 5 0
+"j13" 5 0
+"j16" 4 0
+"i16" 4 0
+"h16" 4 0
+"g16" 4 0
+"f16" 4 0
+"e16" 4 0
+"c16" 4 0
+"b16" 4 0
+"a16" 4 0
+"a17" 4 0
+"b17" 4 0
+"c17" 4 0
+"d17" 4 0
+"e17" 4 0
+"f17" 4 0
+"g17" 4 0
+"h17" 4 0
+"i17" 4 0
+"j17" 4 0
+"k17" 4 0
+"l17" 4 0
+"m17" 4 0
+"n17" 4 0
+"o17" 4 0
+"p17" 4 0
+"q17" 4 0
+"r17" 4 0
+"s17" 4 0
+"t17" 4 0
+"t18" 4 0
+"s18" 4 0
+"r18" 4 0
+"q18" 4 0
+"p18" 4 0
+"o18" 4 0
+"n18" 4 0
+"m18" 4 0
+"l18" 4 0
+"k18" 4 0
+"j18" 4 0
+"i18" 4 0
+"h18" 4 0
+"g18" 4 0
+"f18" 4 0
+"e18" 4 0
+"d18" 4 0
+"c18" 4 0
+"b18" 4 0
+"a18" 4 0
+"a19" 4 0
+"b19" 4 0
+"c19" 4 0
+"d19" 4 0
+"e19" 4 0
+"f19" 4 0
+"g19" 4 0
+"h19" 4 0
+"i19" 4 0
+"j19" 4 0
+"k19" 4 0
+"l19" 4 0
+"m19" 4 0
+"n19" 4 0
+"o19" 4 0
+"p19" 4 0
+"q19" 4 0
+"r19" 4 0
+"s19" 4 0
+"t19" 4 0
+"t20" 4 0
+"s20" 4 0
+"r20" 4 0
+"q20" 4 0
+"p20" 4 0
+"o20" 4 0
+"n20" 4 0
+"m20" 4 0
+"l20" 4 0
+"k20" 4 0
+"j20" 4 0
+"i20" 4 0
+"h20" 4 0
+"g20" 4 0
+"f20" 4 0
+"e20" 4 0
+"d20" 4 0
+"c20" 4 0
+"b20" 4 0
+"a20" 4 0
index 5cb54623551f4c4c13848b9ae960bd05296301d2..e8ecfcaf5064a3125c95f72b42b7946ea713235a 100644 (file)
 // bulldozer storage "level11" last updated 11-06-2016 15:03:24
 nextlevel = "level12"
-"e6" 5 "0 -1 0"
-"f6" 5 "0 -1 0"
-"g6" 5 "0 -1 0"
-"h6" 5 "0 -1 0"
-"i6" 5 "0 -1 0"
-"j6" 5 "0 -1 0"
-"k6" 5 "0 -1 0"
-"l6" 5 "0 -1 0"
-"m6" 5 "0 -1 0"
-"n6" 5 "0 -1 0"
-"o6" 5 "0 -1 0"
-"p6" 5 "0 -1 0"
-"e7" 5 "0 -1 0"
-"e8" 5 "0 -1 0"
-"e9" 5 "0 -1 0"
-"d9" 5 "0 -1 0"
-"d10" 5 "0 -1 0"
-"d11" 5 "0 -1 0"
-"d12" 5 "0 -1 0"
-"d13" 5 "0 -1 0"
-"d14" 5 "0 -1 0"
-"d15" 5 "0 -1 0"
-"e15" 5 "0 -1 0"
-"f15" 5 "0 -1 0"
-"g15" 5 "0 -1 0"
-"h16" 5 "0 -1 0"
-"g16" 5 "0 -1 0"
-"i16" 5 "0 -1 0"
-"j16" 5 "0 -1 0"
-"k16" 5 "0 -1 0"
-"l16" 5 "0 -1 0"
-"m16" 5 "0 -1 0"
-"m15" 5 "0 -1 0"
-"m14" 5 "0 -1 0"
-"m13" 5 "0 -1 0"
-"n13" 5 "0 -1 0"
-"n12" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"o9" 5 "0 -1 0"
-"p9" 5 "0 -1 0"
-"p8" 5 "0 -1 0"
-"p7" 5 "0 -1 0"
-"a20" 10 "0 -1 0"
-"a19" 10 "0 -1 0"
-"a18" 10 "0 -1 0"
-"a17" 10 "0 -1 0"
-"a16" 10 "0 -1 0"
-"a15" 10 "0 -1 0"
-"a14" 10 "0 -1 0"
-"a13" 10 "0 -1 0"
-"a12" 10 "0 -1 0"
-"a11" 10 "0 -1 0"
-"a10" 10 "0 -1 0"
-"a9" 10 "0 -1 0"
-"a8" 10 "0 -1 0"
-"a7" 10 "0 -1 0"
-"a6" 10 "0 -1 0"
-"a5" 10 "0 -1 0"
-"a4" 10 "0 -1 0"
-"a3" 10 "0 -1 0"
-"a2" 10 "0 -1 0"
-"a1" 10 "0 -1 0"
-"t20" 10 "0 -1 0"
-"t19" 10 "0 -1 0"
-"t18" 10 "0 -1 0"
-"t17" 10 "0 -1 0"
-"t16" 10 "0 -1 0"
-"t15" 10 "0 -1 0"
-"t14" 10 "0 -1 0"
-"t12" 10 "0 -1 0"
-"t11" 10 "0 -1 0"
-"t10" 10 "0 -1 0"
-"t9" 10 "0 -1 0"
-"t8" 10 "0 -1 0"
-"t7" 10 "0 -1 0"
-"t6" 10 "0 -1 0"
-"t5" 10 "0 -1 0"
-"t4" 10 "0 -1 0"
-"t3" 10 "0 -1 0"
-"t2" 10 "0 -1 0"
-"t1" 10 "0 -1 0"
-"t13" 10 "0 -1 0"
-"e3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"s7" 4 "0 -1 0"
-"s8" 4 "0 -1 0"
-"r8" 4 "0 -1 0"
-"q8" 4 "0 -1 0"
-"q9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"s10" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"q10" 4 "0 -1 0"
-"p10" 4 "0 -1 0"
-"o10" 4 "0 -1 0"
-"o11" 4 "0 -1 0"
-"p11" 4 "0 -1 0"
-"q11" 4 "0 -1 0"
-"r11" 4 "0 -1 0"
-"s11" 4 "0 -1 0"
-"s12" 4 "0 -1 0"
-"r12" 4 "0 -1 0"
-"q12" 4 "0 -1 0"
-"p12" 4 "0 -1 0"
-"o12" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"p13" 4 "0 -1 0"
-"q13" 4 "0 -1 0"
-"r13" 4 "0 -1 0"
-"s13" 4 "0 -1 0"
-"s14" 4 "0 -1 0"
-"r14" 4 "0 -1 0"
-"q14" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
-"o14" 4 "0 -1 0"
-"n14" 4 "0 -1 0"
-"n15" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"p15" 4 "0 -1 0"
-"q15" 4 "0 -1 0"
-"r15" 4 "0 -1 0"
-"s15" 4 "0 -1 0"
-"s16" 4 "0 -1 0"
-"r16" 4 "0 -1 0"
-"q16" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"o16" 4 "0 -1 0"
-"n16" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"d17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"c16" 4 "0 -1 0"
-"d16" 4 "0 -1 0"
-"e16" 4 "0 -1 0"
-"f16" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"b14" 4 "0 -1 0"
-"c14" 4 "0 -1 0"
-"c13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"b12" 4 "0 -1 0"
-"c12" 4 "0 -1 0"
-"c11" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"b10" 4 "0 -1 0"
-"c10" 4 "0 -1 0"
-"c9" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"b8" 4 "0 -1 0"
-"c8" 4 "0 -1 0"
-"d8" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"r17" 4 "0 -1 0"
-"q17" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"f13" 5 "0 -1 0"
-"f12" 5 "0 -1 0"
-"f11" 5 "0 -1 0"
-"g10" 5 "0 -1 0"
-"h13" 5 "0 -1 0"
-"i14" 5 "0 -1 0"
-"j14" 5 "0 -1 0"
-"k14" 5 "0 -1 0"
-"k12" 5 "0 -1 0"
-"l11" 5 "0 -1 0"
-"l10" 5 "0 -1 0"
-"l9" 5 "0 -1 0"
-"l7" 5 "0 -1 0"
-"k7" 5 "0 -1 0"
-"g8" 5 "0 -1 0"
-"i8" 5 "0 -1 0"
-"h8" 5 "0 -1 0"
-"j9" 5 "0 -1 0"
-"l8" 1 "0 -1 0"
-"j8" 2 "0 -1 0"
-"i11" 2 "0 -1 0"
-"f10" 2 "0 -1 0"
-"h14" 2 "0 -1 0"
-"l12" 2 "0 -1 0"
-"h10" 3 "0 -1 0"
-"i11" 3 "0 -1 0"
-"h12" 3 "0 -1 0"
-"j12" 3 "0 -1 0"
-"j10" 3 "0 -1 0"
+"e6" 5 0
+"f6" 5 0
+"g6" 5 0
+"h6" 5 0
+"i6" 5 0
+"j6" 5 0
+"k6" 5 0
+"l6" 5 0
+"m6" 5 0
+"n6" 5 0
+"o6" 5 0
+"p6" 5 0
+"e7" 5 0
+"e8" 5 0
+"e9" 5 0
+"d9" 5 0
+"d10" 5 0
+"d11" 5 0
+"d12" 5 0
+"d13" 5 0
+"d14" 5 0
+"d15" 5 0
+"e15" 5 0
+"f15" 5 0
+"g15" 5 0
+"h16" 5 0
+"g16" 5 0
+"i16" 5 0
+"j16" 5 0
+"k16" 5 0
+"l16" 5 0
+"m16" 5 0
+"m15" 5 0
+"m14" 5 0
+"m13" 5 0
+"n13" 5 0
+"n12" 5 0
+"n11" 5 0
+"n10" 5 0
+"n9" 5 0
+"o9" 5 0
+"p9" 5 0
+"p8" 5 0
+"p7" 5 0
+"a20" 10 0
+"a19" 10 0
+"a18" 10 0
+"a17" 10 0
+"a16" 10 0
+"a15" 10 0
+"a14" 10 0
+"a13" 10 0
+"a12" 10 0
+"a11" 10 0
+"a10" 10 0
+"a9" 10 0
+"a8" 10 0
+"a7" 10 0
+"a6" 10 0
+"a5" 10 0
+"a4" 10 0
+"a3" 10 0
+"a2" 10 0
+"a1" 10 0
+"t20" 10 0
+"t19" 10 0
+"t18" 10 0
+"t17" 10 0
+"t16" 10 0
+"t15" 10 0
+"t14" 10 0
+"t12" 10 0
+"t11" 10 0
+"t10" 10 0
+"t9" 10 0
+"t8" 10 0
+"t7" 10 0
+"t6" 10 0
+"t5" 10 0
+"t4" 10 0
+"t3" 10 0
+"t2" 10 0
+"t1" 10 0
+"t13" 10 0
+"e3" 4 0
+"d3" 4 0
+"c3" 4 0
+"b3" 4 0
+"b2" 4 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"k2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"s2" 4 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"s3" 4 0
+"r3" 4 0
+"q3" 4 0
+"p3" 4 0
+"o3" 4 0
+"n3" 4 0
+"m3" 4 0
+"l3" 4 0
+"k3" 4 0
+"j3" 4 0
+"i3" 4 0
+"h3" 4 0
+"g3" 4 0
+"f3" 4 0
+"f4" 4 0
+"e4" 4 0
+"d4" 4 0
+"c4" 4 0
+"b4" 4 0
+"b5" 4 0
+"c5" 4 0
+"d5" 4 0
+"e5" 4 0
+"f5" 4 0
+"g5" 4 0
+"h5" 4 0
+"i5" 4 0
+"j5" 4 0
+"k5" 4 0
+"l5" 4 0
+"m5" 4 0
+"n5" 4 0
+"o5" 4 0
+"p5" 4 0
+"q5" 4 0
+"r5" 4 0
+"s5" 4 0
+"s4" 4 0
+"r4" 4 0
+"q4" 4 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"h4" 4 0
+"g4" 4 0
+"s6" 4 0
+"r6" 4 0
+"q6" 4 0
+"q7" 4 0
+"r7" 4 0
+"s7" 4 0
+"s8" 4 0
+"r8" 4 0
+"q8" 4 0
+"q9" 4 0
+"r9" 4 0
+"s9" 4 0
+"s10" 4 0
+"r10" 4 0
+"q10" 4 0
+"p10" 4 0
+"o10" 4 0
+"o11" 4 0
+"p11" 4 0
+"q11" 4 0
+"r11" 4 0
+"s11" 4 0
+"s12" 4 0
+"r12" 4 0
+"q12" 4 0
+"p12" 4 0
+"o12" 4 0
+"o13" 4 0
+"p13" 4 0
+"q13" 4 0
+"r13" 4 0
+"s13" 4 0
+"s14" 4 0
+"r14" 4 0
+"q14" 4 0
+"p14" 4 0
+"o14" 4 0
+"n14" 4 0
+"n15" 4 0
+"o15" 4 0
+"p15" 4 0
+"q15" 4 0
+"r15" 4 0
+"s15" 4 0
+"s16" 4 0
+"r16" 4 0
+"q16" 4 0
+"p16" 4 0
+"o16" 4 0
+"n16" 4 0
+"n17" 4 0
+"m17" 4 0
+"l17" 4 0
+"k17" 4 0
+"j17" 4 0
+"i17" 4 0
+"h17" 4 0
+"g17" 4 0
+"f17" 4 0
+"e17" 4 0
+"d17" 4 0
+"c17" 4 0
+"b17" 4 0
+"b16" 4 0
+"c16" 4 0
+"d16" 4 0
+"e16" 4 0
+"f16" 4 0
+"c15" 4 0
+"b15" 4 0
+"b14" 4 0
+"c14" 4 0
+"c13" 4 0
+"b13" 4 0
+"b12" 4 0
+"c12" 4 0
+"c11" 4 0
+"b11" 4 0
+"b10" 4 0
+"c10" 4 0
+"c9" 4 0
+"b9" 4 0
+"b8" 4 0
+"c8" 4 0
+"d8" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"b6" 4 0
+"c6" 4 0
+"d6" 4 0
+"b18" 4 0
+"c18" 4 0
+"d18" 4 0
+"e18" 4 0
+"f18" 4 0
+"g18" 4 0
+"h18" 4 0
+"i18" 4 0
+"j18" 4 0
+"k18" 4 0
+"l18" 4 0
+"m18" 4 0
+"n18" 4 0
+"o18" 4 0
+"p18" 4 0
+"q18" 4 0
+"r18" 4 0
+"s18" 4 0
+"s17" 4 0
+"r17" 4 0
+"q17" 4 0
+"p17" 4 0
+"o17" 4 0
+"s19" 4 0
+"r19" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"e19" 4 0
+"d19" 4 0
+"c19" 4 0
+"b19" 4 0
+"b20" 4 0
+"c20" 4 0
+"d20" 4 0
+"e20" 4 0
+"f20" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
+"f13" 5 0
+"f12" 5 0
+"f11" 5 0
+"g10" 5 0
+"h13" 5 0
+"i14" 5 0
+"j14" 5 0
+"k14" 5 0
+"k12" 5 0
+"l11" 5 0
+"l10" 5 0
+"l9" 5 0
+"l7" 5 0
+"k7" 5 0
+"g8" 5 0
+"i8" 5 0
+"h8" 5 0
+"j9" 5 0
+"l8" 1 1
+"j8" 2 0
+"i11" 2 0
+"f10" 2 0
+"h14" 2 0
+"l12" 2 0
+"h10" 3 0
+"i11" 3 0
+"h12" 3 0
+"j12" 3 0
+"j10" 3 0
index f7162fb0460d92c8d2b5e610fdf7f8d3e1e09fd8..ccd163583bc72a8a43153667d1f22fa9b4da4cc2 100644 (file)
 // bulldozer storage "level12" last updated 11-06-2016 15:11:43
 nextlevel = "level13"
-"b18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"t7" 11 "0 -1 0"
-"t17" 4 "0 -1 0"
-"t16" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"t14" 4 "0 -1 0"
-"t13" 4 "0 -1 0"
-"t12" 4 "0 -1 0"
-"t11" 4 "0 -1 0"
-"t10" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"a18" 11 "0 -1 0"
-"a17" 4 "0 -1 0"
-"a16" 4 "0 -1 0"
-"a15" 4 "0 -1 0"
-"a14" 4 "0 -1 0"
-"a13" 4 "0 -1 0"
-"a12" 4 "0 -1 0"
-"a11" 4 "0 -1 0"
-"a10" 4 "0 -1 0"
-"a9" 4 "0 -1 0"
-"a8" 4 "0 -1 0"
-"d17" 5 "0 -1 0"
-"t8" 4 "0 -1 0"
-"a7" 11 "0 -1 0"
-"s7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"p7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"h7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"a4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"t3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"t2" 4 "0 -1 0"
-"t1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"a1" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"k6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"a19" 4 "0 -1 0"
-"a20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"t20" 4 "0 -1 0"
-"t19" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"t18" 11 "0 -1 0"
-"d16" 5 "0 -1 0"
-"e13" 5 "0 -1 0"
-"d15" 5 "0 -1 0"
-"d14" 5 "0 -1 0"
-"d13" 5 "0 -1 0"
-"e12" 5 "0 -1 0"
-"g11" 5 "0 -1 0"
-"e11" 5 "0 -1 0"
-"g12" 5 "0 -1 0"
-"g13" 5 "0 -1 0"
-"g14" 5 "0 -1 0"
-"h14" 5 "0 -1 0"
-"i14" 5 "0 -1 0"
-"i13" 5 "0 -1 0"
-"i12" 5 "0 -1 0"
-"i11" 5 "0 -1 0"
-"g10" 5 "0 -1 0"
-"i10" 5 "0 -1 0"
-"h10" 5 "0 -1 0"
-"h11" 10 "0 -1 0"
-"h12" 10 "0 -1 0"
-"h13" 10 "0 -1 0"
-"d8" 5 "0 -1 0"
-"d9" 5 "0 -1 0"
-"e17" 5 "0 -1 0"
-"f17" 5 "0 -1 0"
-"g17" 5 "0 -1 0"
-"h17" 5 "0 -1 0"
-"i17" 5 "0 -1 0"
-"i16" 5 "0 -1 0"
-"m17" 5 "0 -1 0"
-"n17" 5 "0 -1 0"
-"o17" 5 "0 -1 0"
-"l15" 5 "0 -1 0"
-"m15" 5 "0 -1 0"
-"o15" 5 "0 -1 0"
-"n15" 5 "0 -1 0"
-"p15" 5 "0 -1 0"
-"p14" 5 "0 -1 0"
-"q14" 5 "0 -1 0"
-"r14" 5 "0 -1 0"
-"s14" 8 "0 -1 0"
-"r13" 8 "0 -1 0"
-"q12" 8 "0 -1 0"
-"p11" 8 "0 -1 0"
-"o10" 8 "0 -1 0"
-"n9" 8 "0 -1 0"
-"m8" 8 "0 -1 0"
-"m9" 5 "0 -1 0"
-"m11" 5 "0 -1 0"
-"m14" 5 "0 -1 0"
-"m13" 5 "0 -1 0"
-"q9" 4 "0 -1 0"
-"p9" 4 "0 -1 0"
-"o9" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"q8" 4 "0 -1 0"
-"r8" 4 "0 -1 0"
-"s8" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"q10" 4 "0 -1 0"
-"p10" 4 "0 -1 0"
-"q11" 4 "0 -1 0"
-"r11" 4 "0 -1 0"
-"s11" 4 "0 -1 0"
-"s10" 4 "0 -1 0"
-"s12" 4 "0 -1 0"
-"r12" 4 "0 -1 0"
-"s13" 4 "0 -1 0"
-"f15" 1 "0 -1 0"
-"q16" 2 "0 -1 0"
-"q15" 2 "0 -1 0"
-"r15" 2 "0 -1 0"
-"r16" 2 "0 -1 0"
-"s16" 2 "0 -1 0"
-"s15" 2 "0 -1 0"
-"s17" 2 "0 -1 0"
-"b9" 2 "0 -1 0"
-"c8" 2 "0 -1 0"
-"n10" 2 "0 -1 0"
-"o11" 2 "0 -1 0"
-"p12" 2 "0 -1 0"
-"q13" 2 "0 -1 0"
-"c15" 3 "0 -1 0"
-"b11" 3 "0 -1 0"
-"c13" 3 "0 -1 0"
-"d11" 3 "0 -1 0"
-"c9" 3 "0 -1 0"
-"j16" 3 "0 -1 0"
-"j14" 3 "0 -1 0"
-"k13" 3 "0 -1 0"
-"j12" 3 "0 -1 0"
-"k11" 3 "0 -1 0"
-"j10" 3 "0 -1 0"
-"n16" 3 "0 -1 0"
-"o13" 3 "0 -1 0"
+"b18" 4 0
+"c18" 4 0
+"d18" 4 0
+"e18" 4 0
+"f18" 4 0
+"g18" 4 0
+"h18" 4 0
+"i18" 4 0
+"j18" 4 0
+"k18" 4 0
+"l18" 4 0
+"m18" 4 0
+"n18" 4 0
+"o18" 4 0
+"p18" 4 0
+"q18" 4 0
+"r18" 4 0
+"s18" 4 0
+"t7" 11 0
+"t17" 4 0
+"t16" 4 0
+"t15" 4 0
+"t14" 4 0
+"t13" 4 0
+"t12" 4 0
+"t11" 4 0
+"t10" 4 0
+"t9" 4 0
+"b7" 4 0
+"c7" 4 0
+"a18" 11 0
+"a17" 4 0
+"a16" 4 0
+"a15" 4 0
+"a14" 4 0
+"a13" 4 0
+"a12" 4 0
+"a11" 4 0
+"a10" 4 0
+"a9" 4 0
+"a8" 4 0
+"d17" 5 0
+"t8" 4 0
+"a7" 11 0
+"s7" 4 0
+"r7" 4 0
+"q7" 4 0
+"p7" 4 0
+"o7" 4 0
+"n7" 4 0
+"l7" 4 0
+"k7" 4 0
+"j7" 4 0
+"i7" 4 0
+"h7" 4 0
+"g7" 4 0
+"f7" 4 0
+"e7" 4 0
+"d7" 4 0
+"m7" 4 0
+"c5" 4 0
+"b5" 4 0
+"a5" 4 0
+"a4" 4 0
+"b4" 4 0
+"c4" 4 0
+"d4" 4 0
+"e4" 4 0
+"f4" 4 0
+"g4" 4 0
+"h4" 4 0
+"i4" 4 0
+"j4" 4 0
+"k4" 4 0
+"l4" 4 0
+"m4" 4 0
+"n4" 4 0
+"o4" 4 0
+"p4" 4 0
+"q4" 4 0
+"r4" 4 0
+"s4" 4 0
+"t4" 4 0
+"t3" 4 0
+"s3" 4 0
+"r3" 4 0
+"q3" 4 0
+"p3" 4 0
+"o3" 4 0
+"n3" 4 0
+"m3" 4 0
+"l3" 4 0
+"k3" 4 0
+"j3" 4 0
+"i3" 4 0
+"h3" 4 0
+"g3" 4 0
+"f3" 4 0
+"e3" 4 0
+"d3" 4 0
+"c3" 4 0
+"b3" 4 0
+"a3" 4 0
+"a2" 4 0
+"b2" 4 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"k2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"s2" 4 0
+"t2" 4 0
+"t1" 4 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"a1" 4 0
+"t5" 4 0
+"s5" 4 0
+"r5" 4 0
+"q5" 4 0
+"p5" 4 0
+"o5" 4 0
+"n5" 4 0
+"m5" 4 0
+"l5" 4 0
+"k5" 4 0
+"j5" 4 0
+"i5" 4 0
+"h5" 4 0
+"g5" 4 0
+"f5" 4 0
+"e5" 4 0
+"d5" 4 0
+"d6" 4 0
+"c6" 4 0
+"b6" 4 0
+"a6" 4 0
+"e6" 4 0
+"f6" 4 0
+"g6" 4 0
+"h6" 4 0
+"i6" 4 0
+"j6" 4 0
+"k6" 4 0
+"l6" 4 0
+"m6" 4 0
+"n6" 4 0
+"o6" 4 0
+"p6" 4 0
+"q6" 4 0
+"r6" 4 0
+"s6" 4 0
+"t6" 4 0
+"b19" 4 0
+"a19" 4 0
+"a20" 4 0
+"b20" 4 0
+"c20" 4 0
+"d20" 4 0
+"e20" 4 0
+"f20" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
+"t20" 4 0
+"t19" 4 0
+"s19" 4 0
+"r19" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"e19" 4 0
+"d19" 4 0
+"c19" 4 0
+"t18" 11 0
+"d16" 5 0
+"e13" 5 0
+"d15" 5 0
+"d14" 5 0
+"d13" 5 0
+"e12" 5 0
+"g11" 5 0
+"e11" 5 0
+"g12" 5 0
+"g13" 5 0
+"g14" 5 0
+"h14" 5 0
+"i14" 5 0
+"i13" 5 0
+"i12" 5 0
+"i11" 5 0
+"g10" 5 0
+"i10" 5 0
+"h10" 5 0
+"h11" 10 0
+"h12" 10 0
+"h13" 10 0
+"d8" 5 0
+"d9" 5 0
+"e17" 5 0
+"f17" 5 0
+"g17" 5 0
+"h17" 5 0
+"i17" 5 0
+"i16" 5 0
+"m17" 5 0
+"n17" 5 0
+"o17" 5 0
+"l15" 5 0
+"m15" 5 0
+"o15" 5 0
+"n15" 5 0
+"p15" 5 0
+"p14" 5 0
+"q14" 5 0
+"r14" 5 0
+"s14" 8 0
+"r13" 8 0
+"q12" 8 0
+"p11" 8 0
+"o10" 8 0
+"n9" 8 0
+"m8" 8 0
+"m9" 5 0
+"m11" 5 0
+"m14" 5 0
+"m13" 5 0
+"q9" 4 0
+"p9" 4 0
+"o9" 4 0
+"o8" 4 0
+"n8" 4 0
+"p8" 4 0
+"q8" 4 0
+"r8" 4 0
+"s8" 4 0
+"s9" 4 0
+"r9" 4 0
+"r10" 4 0
+"q10" 4 0
+"p10" 4 0
+"q11" 4 0
+"r11" 4 0
+"s11" 4 0
+"s10" 4 0
+"s12" 4 0
+"r12" 4 0
+"s13" 4 0
+"f15" 1 1
+"q16" 2 0
+"q15" 2 0
+"r15" 2 0
+"r16" 2 0
+"s16" 2 0
+"s15" 2 0
+"s17" 2 0
+"b9" 2 0
+"c8" 2 0
+"n10" 2 0
+"o11" 2 0
+"p12" 2 0
+"q13" 2 0
+"c15" 3 0
+"b11" 3 0
+"c13" 3 0
+"d11" 3 0
+"c9" 3 0
+"j16" 3 0
+"j14" 3 0
+"k13" 3 0
+"j12" 3 0
+"k11" 3 0
+"j10" 3 0
+"n16" 3 0
+"o13" 3 0
index 54f74994960021de466fd227dda29fd787178aff..c5e7f38c470e5a72d87df1d5bfe8dd0dbb8f2c30 100644 (file)
 // bulldozer storage "level13" last updated 11-06-2016 15:20:23
 nextlevel = "level14"
-"a10" 5 "0 -1 0"
-"a8" 5 "0 -1 0"
-"a9" 5 "0 -1 0"
-"a11" 5 "0 -1 0"
-"b11" 5 "0 -1 0"
-"d13" 5 "0 -1 0"
-"c11" 5 "0 -1 0"
-"c12" 5 "0 -1 0"
-"c13" 5 "0 -1 0"
-"e13" 5 "0 -1 0"
-"e14" 5 "0 -1 0"
-"e15" 5 "0 -1 0"
-"e16" 5 "0 -1 0"
-"f16" 5 "0 -1 0"
-"g16" 5 "0 -1 0"
-"h16" 5 "0 -1 0"
-"i16" 5 "0 -1 0"
-"i15" 5 "0 -1 0"
-"i14" 5 "0 -1 0"
-"i13" 5 "0 -1 0"
-"j13" 5 "0 -1 0"
-"j12" 5 "0 -1 0"
-"j11" 5 "0 -1 0"
-"j10" 5 "0 -1 0"
-"k10" 5 "0 -1 0"
-"l10" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"m10" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"o11" 5 "0 -1 0"
-"p11" 5 "0 -1 0"
-"q11" 5 "0 -1 0"
-"r11" 5 "0 -1 0"
-"s11" 5 "0 -1 0"
-"s10" 5 "0 -1 0"
-"s8" 5 "0 -1 0"
-"s9" 5 "0 -1 0"
-"s7" 5 "0 -1 0"
-"r7" 5 "0 -1 0"
-"q7" 5 "0 -1 0"
-"p7" 5 "0 -1 0"
-"o7" 5 "0 -1 0"
-"n7" 5 "0 -1 0"
-"m7" 5 "0 -1 0"
-"l7" 5 "0 -1 0"
-"k7" 5 "0 -1 0"
-"k8" 5 "0 -1 0"
-"m8" 5 "0 -1 0"
-"n8" 5 "0 -1 0"
-"k6" 5 "0 -1 0"
-"j6" 5 "0 -1 0"
-"i6" 5 "0 -1 0"
-"b8" 5 "0 -1 0"
-"c8" 5 "0 -1 0"
-"d8" 5 "0 -1 0"
-"e8" 5 "0 -1 0"
-"e7" 5 "0 -1 0"
-"e6" 5 "0 -1 0"
-"f6" 5 "0 -1 0"
-"g6" 5 "0 -1 0"
-"h6" 5 "0 -1 0"
-"e11" 8 "0 -1 0"
-"e10" 8 "0 -1 0"
-"g11" 8 "0 -1 0"
-"g10" 8 "0 -1 0"
-"h10" 8 "0 -1 0"
-"h11" 8 "0 -1 0"
-"g8" 8 "0 -1 0"
-"h8" 8 "0 -1 0"
-"i8" 8 "0 -1 0"
-"b15" 8 "0 -1 0"
-"o12" 11 "0 -1 0"
-"n13" 11 "0 -1 0"
-"m14" 11 "0 -1 0"
-"l15" 11 "0 -1 0"
-"k16" 11 "0 -1 0"
-"m16" 11 "0 -1 0"
-"n15" 11 "0 -1 0"
-"o16" 11 "0 -1 0"
-"p15" 11 "0 -1 0"
-"q16" 11 "0 -1 0"
-"r15" 11 "0 -1 0"
-"s16" 11 "0 -1 0"
-"q14" 11 "0 -1 0"
-"p13" 11 "0 -1 0"
-"o14" 11 "0 -1 0"
-"j19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"a19" 4 "0 -1 0"
-"a18" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"t18" 4 "0 -1 0"
-"t17" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"r17" 4 "0 -1 0"
-"q17" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"d17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"a17" 4 "0 -1 0"
-"a16" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"c16" 4 "0 -1 0"
-"d16" 4 "0 -1 0"
-"d15" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"c14" 4 "0 -1 0"
-"b14" 4 "0 -1 0"
-"a14" 4 "0 -1 0"
-"a13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"b12" 4 "0 -1 0"
-"a12" 4 "0 -1 0"
-"a15" 4 "0 -1 0"
-"d14" 4 "0 -1 0"
-"j16" 4 "0 -1 0"
-"j15" 4 "0 -1 0"
-"k15" 4 "0 -1 0"
-"k14" 4 "0 -1 0"
-"j14" 4 "0 -1 0"
-"l14" 4 "0 -1 0"
-"l13" 4 "0 -1 0"
-"k13" 4 "0 -1 0"
-"k12" 4 "0 -1 0"
-"l12" 4 "0 -1 0"
-"m12" 4 "0 -1 0"
-"n12" 4 "0 -1 0"
-"m11" 4 "0 -1 0"
-"l11" 4 "0 -1 0"
-"k11" 4 "0 -1 0"
-"m13" 4 "0 -1 0"
-"l16" 4 "0 -1 0"
-"n16" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"r16" 4 "0 -1 0"
-"t16" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"s15" 4 "0 -1 0"
-"s14" 4 "0 -1 0"
-"r14" 4 "0 -1 0"
-"r13" 4 "0 -1 0"
-"q13" 4 "0 -1 0"
-"q12" 4 "0 -1 0"
-"p12" 4 "0 -1 0"
-"r12" 4 "0 -1 0"
-"s12" 4 "0 -1 0"
-"t12" 4 "0 -1 0"
-"t11" 4 "0 -1 0"
-"t10" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"t8" 4 "0 -1 0"
-"t7" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"a4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"t3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"t2" 4 "0 -1 0"
-"t1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"a1" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"a7" 4 "0 -1 0"
-"t13" 4 "0 -1 0"
-"s13" 4 "0 -1 0"
-"t14" 4 "0 -1 0"
-"t19" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"a20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"t20" 4 "0 -1 0"
-"m15" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"q15" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"n14" 4 "0 -1 0"
-"l8" 1 "0 -1 0"
-"r10" 2 "0 -1 0"
-"r9" 2 "0 -1 0"
-"r8" 2 "0 -1 0"
-"q8" 2 "0 -1 0"
-"q9" 2 "0 -1 0"
-"q10" 2 "0 -1 0"
-"c9" 3 "0 -1 0"
-"f9" 3 "0 -1 0"
-"f12" 3 "0 -1 0"
-"f14" 3 "0 -1 0"
-"h13" 3 "0 -1 0"
-"h12" 3 "0 -1 0"
+"a10" 5 0
+"a8" 5 0
+"a9" 5 0
+"a11" 5 0
+"b11" 5 0
+"d13" 5 0
+"c11" 5 0
+"c12" 5 0
+"c13" 5 0
+"e13" 5 0
+"e14" 5 0
+"e15" 5 0
+"e16" 5 0
+"f16" 5 0
+"g16" 5 0
+"h16" 5 0
+"i16" 5 0
+"i15" 5 0
+"i14" 5 0
+"i13" 5 0
+"j13" 5 0
+"j12" 5 0
+"j11" 5 0
+"j10" 5 0
+"k10" 5 0
+"l10" 5 0
+"n10" 5 0
+"m10" 5 0
+"n11" 5 0
+"o11" 5 0
+"p11" 5 0
+"q11" 5 0
+"r11" 5 0
+"s11" 5 0
+"s10" 5 0
+"s8" 5 0
+"s9" 5 0
+"s7" 5 0
+"r7" 5 0
+"q7" 5 0
+"p7" 5 0
+"o7" 5 0
+"n7" 5 0
+"m7" 5 0
+"l7" 5 0
+"k7" 5 0
+"k8" 5 0
+"m8" 5 0
+"n8" 5 0
+"k6" 5 0
+"j6" 5 0
+"i6" 5 0
+"b8" 5 0
+"c8" 5 0
+"d8" 5 0
+"e8" 5 0
+"e7" 5 0
+"e6" 5 0
+"f6" 5 0
+"g6" 5 0
+"h6" 5 0
+"e11" 8 0
+"e10" 8 0
+"g11" 8 0
+"g10" 8 0
+"h10" 8 0
+"h11" 8 0
+"g8" 8 0
+"h8" 8 0
+"i8" 8 0
+"b15" 8 0
+"o12" 11 0
+"n13" 11 0
+"m14" 11 0
+"l15" 11 0
+"k16" 11 0
+"m16" 11 0
+"n15" 11 0
+"o16" 11 0
+"p15" 11 0
+"q16" 11 0
+"r15" 11 0
+"s16" 11 0
+"q14" 11 0
+"p13" 11 0
+"o14" 11 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"e19" 4 0
+"d19" 4 0
+"c19" 4 0
+"b19" 4 0
+"a19" 4 0
+"a18" 4 0
+"b18" 4 0
+"c18" 4 0
+"d18" 4 0
+"e18" 4 0
+"f18" 4 0
+"g18" 4 0
+"h18" 4 0
+"i18" 4 0
+"j18" 4 0
+"k18" 4 0
+"l18" 4 0
+"m18" 4 0
+"n18" 4 0
+"o18" 4 0
+"p18" 4 0
+"q18" 4 0
+"r18" 4 0
+"s18" 4 0
+"t18" 4 0
+"t17" 4 0
+"s17" 4 0
+"r17" 4 0
+"q17" 4 0
+"p17" 4 0
+"o17" 4 0
+"n17" 4 0
+"m17" 4 0
+"l17" 4 0
+"k17" 4 0
+"j17" 4 0
+"i17" 4 0
+"h17" 4 0
+"g17" 4 0
+"f17" 4 0
+"e17" 4 0
+"d17" 4 0
+"c17" 4 0
+"b17" 4 0
+"a17" 4 0
+"a16" 4 0
+"b16" 4 0
+"c16" 4 0
+"d16" 4 0
+"d15" 4 0
+"c15" 4 0
+"c14" 4 0
+"b14" 4 0
+"a14" 4 0
+"a13" 4 0
+"b13" 4 0
+"b12" 4 0
+"a12" 4 0
+"a15" 4 0
+"d14" 4 0
+"j16" 4 0
+"j15" 4 0
+"k15" 4 0
+"k14" 4 0
+"j14" 4 0
+"l14" 4 0
+"l13" 4 0
+"k13" 4 0
+"k12" 4 0
+"l12" 4 0
+"m12" 4 0
+"n12" 4 0
+"m11" 4 0
+"l11" 4 0
+"k11" 4 0
+"m13" 4 0
+"l16" 4 0
+"n16" 4 0
+"p16" 4 0
+"r16" 4 0
+"t16" 4 0
+"t15" 4 0
+"s15" 4 0
+"s14" 4 0
+"r14" 4 0
+"r13" 4 0
+"q13" 4 0
+"q12" 4 0
+"p12" 4 0
+"r12" 4 0
+"s12" 4 0
+"t12" 4 0
+"t11" 4 0
+"t10" 4 0
+"t9" 4 0
+"t8" 4 0
+"t7" 4 0
+"t6" 4 0
+"s6" 4 0
+"r6" 4 0
+"q6" 4 0
+"p6" 4 0
+"o6" 4 0
+"n6" 4 0
+"m6" 4 0
+"l6" 4 0
+"l5" 4 0
+"k5" 4 0
+"j5" 4 0
+"i5" 4 0
+"h5" 4 0
+"g5" 4 0
+"f5" 4 0
+"e5" 4 0
+"d5" 4 0
+"c5" 4 0
+"b5" 4 0
+"a5" 4 0
+"a4" 4 0
+"b4" 4 0
+"c4" 4 0
+"d4" 4 0
+"e4" 4 0
+"f4" 4 0
+"g4" 4 0
+"h4" 4 0
+"i4" 4 0
+"j4" 4 0
+"k4" 4 0
+"l4" 4 0
+"m4" 4 0
+"n4" 4 0
+"o4" 4 0
+"p4" 4 0
+"q4" 4 0
+"r4" 4 0
+"s4" 4 0
+"t4" 4 0
+"t3" 4 0
+"s3" 4 0
+"r3" 4 0
+"q3" 4 0
+"p3" 4 0
+"o3" 4 0
+"n3" 4 0
+"m3" 4 0
+"l3" 4 0
+"k3" 4 0
+"j3" 4 0
+"i3" 4 0
+"h3" 4 0
+"g3" 4 0
+"f3" 4 0
+"e3" 4 0
+"d3" 4 0
+"c3" 4 0
+"b3" 4 0
+"a3" 4 0
+"a2" 4 0
+"b2" 4 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"k2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"s2" 4 0
+"t2" 4 0
+"t1" 4 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"a1" 4 0
+"t5" 4 0
+"s5" 4 0
+"r5" 4 0
+"q5" 4 0
+"p5" 4 0
+"o5" 4 0
+"n5" 4 0
+"m5" 4 0
+"a6" 4 0
+"b6" 4 0
+"c6" 4 0
+"d6" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"a7" 4 0
+"t13" 4 0
+"s13" 4 0
+"t14" 4 0
+"t19" 4 0
+"s19" 4 0
+"r19" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"k20" 4 0
+"j20" 4 0
+"i20" 4 0
+"h20" 4 0
+"g20" 4 0
+"f20" 4 0
+"e20" 4 0
+"d20" 4 0
+"c20" 4 0
+"b20" 4 0
+"a20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
+"t20" 4 0
+"m15" 4 0
+"o15" 4 0
+"q15" 4 0
+"p14" 4 0
+"o13" 4 0
+"n14" 4 0
+"l8" 1 1
+"r10" 2 0
+"r9" 2 0
+"r8" 2 0
+"q8" 2 0
+"q9" 2 0
+"q10" 2 0
+"c9" 3 0
+"f9" 3 0
+"f12" 3 0
+"f14" 3 0
+"h13" 3 0
+"h12" 3 0
index 540c31fe3d856d62bdadba5520fba19ae7931eef..c7ab1e0c2de0ee3cc49f0050e86369553867bc1e 100644 (file)
 // bulldozer storage "level14" last updated 11-06-2016 15:24:43
 nextlevel = "level15"
-"a20" 10 "0 -1 0"
-"a19" 10 "0 -1 0"
-"a18" 10 "0 -1 0"
-"a17" 10 "0 -1 0"
-"a16" 10 "0 -1 0"
-"a15" 10 "0 -1 0"
-"a14" 10 "0 -1 0"
-"a13" 10 "0 -1 0"
-"a12" 10 "0 -1 0"
-"a11" 10 "0 -1 0"
-"a10" 10 "0 -1 0"
-"a9" 10 "0 -1 0"
-"a8" 10 "0 -1 0"
-"a7" 10 "0 -1 0"
-"a6" 10 "0 -1 0"
-"a5" 10 "0 -1 0"
-"a4" 10 "0 -1 0"
-"a3" 10 "0 -1 0"
-"a2" 10 "0 -1 0"
-"a1" 10 "0 -1 0"
-"t20" 10 "0 -1 0"
-"t19" 10 "0 -1 0"
-"t18" 10 "0 -1 0"
-"t17" 10 "0 -1 0"
-"t16" 10 "0 -1 0"
-"t15" 10 "0 -1 0"
-"t13" 10 "0 -1 0"
-"t12" 10 "0 -1 0"
-"t11" 10 "0 -1 0"
-"t10" 10 "0 -1 0"
-"t9" 10 "0 -1 0"
-"t8" 10 "0 -1 0"
-"t7" 10 "0 -1 0"
-"t6" 10 "0 -1 0"
-"t5" 10 "0 -1 0"
-"t4" 10 "0 -1 0"
-"t2" 10 "0 -1 0"
-"t1" 10 "0 -1 0"
-"t3" 10 "0 -1 0"
-"t14" 10 "0 -1 0"
-"c17" 5 "0 -1 0"
-"d17" 5 "0 -1 0"
-"d16" 5 "0 -1 0"
-"c16" 5 "0 -1 0"
-"c15" 5 "0 -1 0"
-"c14" 5 "0 -1 0"
-"c13" 5 "0 -1 0"
-"c12" 5 "0 -1 0"
-"c11" 5 "0 -1 0"
-"c10" 5 "0 -1 0"
-"c9" 5 "0 -1 0"
-"c8" 5 "0 -1 0"
-"d15" 5 "0 -1 0"
-"d14" 5 "0 -1 0"
-"d13" 5 "0 -1 0"
-"d12" 5 "0 -1 0"
-"d11" 5 "0 -1 0"
-"d10" 5 "0 -1 0"
-"d8" 5 "0 -1 0"
-"f9" 5 "0 -1 0"
-"d9" 5 "0 -1 0"
-"f10" 5 "0 -1 0"
-"f11" 5 "0 -1 0"
-"g11" 5 "0 -1 0"
-"h11" 5 "0 -1 0"
-"h12" 5 "0 -1 0"
-"h13" 5 "0 -1 0"
-"h14" 5 "0 -1 0"
-"h15" 5 "0 -1 0"
-"i15" 5 "0 -1 0"
-"j16" 5 "0 -1 0"
-"i16" 5 "0 -1 0"
-"k16" 5 "0 -1 0"
-"l16" 5 "0 -1 0"
-"m16" 5 "0 -1 0"
-"n16" 5 "0 -1 0"
-"o16" 5 "0 -1 0"
-"o15" 5 "0 -1 0"
-"o14" 5 "0 -1 0"
-"o13" 5 "0 -1 0"
-"o12" 5 "0 -1 0"
-"o11" 5 "0 -1 0"
-"q17" 5 "0 -1 0"
-"q16" 5 "0 -1 0"
-"q15" 5 "0 -1 0"
-"q14" 5 "0 -1 0"
-"q13" 5 "0 -1 0"
-"q11" 5 "0 -1 0"
-"q10" 5 "0 -1 0"
-"q9" 5 "0 -1 0"
-"q8" 5 "0 -1 0"
-"r8" 5 "0 -1 0"
-"q12" 5 "0 -1 0"
-"r9" 5 "0 -1 0"
-"r10" 5 "0 -1 0"
-"r11" 5 "0 -1 0"
-"r13" 5 "0 -1 0"
-"r14" 5 "0 -1 0"
-"r15" 5 "0 -1 0"
-"r16" 5 "0 -1 0"
-"r17" 5 "0 -1 0"
-"r12" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"m9" 5 "0 -1 0"
-"k9" 5 "0 -1 0"
-"i9" 5 "0 -1 0"
-"h9" 5 "0 -1 0"
-"g9" 5 "0 -1 0"
-"j9" 5 "0 -1 0"
-"l9" 5 "0 -1 0"
-"l11" 5 "0 -1 0"
-"l12" 5 "0 -1 0"
-"k12" 5 "0 -1 0"
-"l15" 5 "0 -1 0"
-"l14" 5 "0 -1 0"
-"n15" 1 "0 -1 0"
-"g10" 2 "0 -1 0"
-"h10" 2 "0 -1 0"
-"i10" 2 "0 -1 0"
-"j10" 2 "0 -1 0"
-"k10" 2 "0 -1 0"
-"j11" 3 "0 -1 0"
-"j12" 3 "0 -1 0"
-"k13" 3 "0 -1 0"
-"i13" 3 "0 -1 0"
-"m13" 3 "0 -1 0"
-"k6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"h7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"b8" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"b10" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"b12" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"b14" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"s16" 4 "0 -1 0"
-"s15" 4 "0 -1 0"
-"s14" 4 "0 -1 0"
-"s13" 4 "0 -1 0"
-"s12" 4 "0 -1 0"
-"s11" 4 "0 -1 0"
-"s10" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"s8" 4 "0 -1 0"
-"s7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"p7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"m8" 4 "0 -1 0"
-"l8" 4 "0 -1 0"
-"k8" 4 "0 -1 0"
-"j8" 4 "0 -1 0"
-"i8" 4 "0 -1 0"
-"h8" 4 "0 -1 0"
-"g8" 4 "0 -1 0"
-"f8" 4 "0 -1 0"
-"e8" 4 "0 -1 0"
-"e9" 4 "0 -1 0"
-"e10" 4 "0 -1 0"
-"e11" 4 "0 -1 0"
-"e12" 4 "0 -1 0"
-"f12" 4 "0 -1 0"
-"g12" 4 "0 -1 0"
-"g13" 4 "0 -1 0"
-"f13" 4 "0 -1 0"
-"e13" 4 "0 -1 0"
-"e14" 4 "0 -1 0"
-"f14" 4 "0 -1 0"
-"g14" 4 "0 -1 0"
-"g15" 4 "0 -1 0"
-"f15" 4 "0 -1 0"
-"e15" 4 "0 -1 0"
-"e16" 4 "0 -1 0"
-"f16" 4 "0 -1 0"
-"g16" 4 "0 -1 0"
-"h16" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"p15" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
-"p13" 4 "0 -1 0"
-"p12" 4 "0 -1 0"
-"p11" 4 "0 -1 0"
-"p10" 4 "0 -1 0"
-"o10" 4 "0 -1 0"
-"o9" 4 "0 -1 0"
-"p9" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
+"a20" 10 0
+"a19" 10 0
+"a18" 10 0
+"a17" 10 0
+"a16" 10 0
+"a15" 10 0
+"a14" 10 0
+"a13" 10 0
+"a12" 10 0
+"a11" 10 0
+"a10" 10 0
+"a9" 10 0
+"a8" 10 0
+"a7" 10 0
+"a6" 10 0
+"a5" 10 0
+"a4" 10 0
+"a3" 10 0
+"a2" 10 0
+"a1" 10 0
+"t20" 10 0
+"t19" 10 0
+"t18" 10 0
+"t17" 10 0
+"t16" 10 0
+"t15" 10 0
+"t13" 10 0
+"t12" 10 0
+"t11" 10 0
+"t10" 10 0
+"t9" 10 0
+"t8" 10 0
+"t7" 10 0
+"t6" 10 0
+"t5" 10 0
+"t4" 10 0
+"t2" 10 0
+"t1" 10 0
+"t3" 10 0
+"t14" 10 0
+"c17" 5 0
+"d17" 5 0
+"d16" 5 0
+"c16" 5 0
+"c15" 5 0
+"c14" 5 0
+"c13" 5 0
+"c12" 5 0
+"c11" 5 0
+"c10" 5 0
+"c9" 5 0
+"c8" 5 0
+"d15" 5 0
+"d14" 5 0
+"d13" 5 0
+"d12" 5 0
+"d11" 5 0
+"d10" 5 0
+"d8" 5 0
+"f9" 5 0
+"d9" 5 0
+"f10" 5 0
+"f11" 5 0
+"g11" 5 0
+"h11" 5 0
+"h12" 5 0
+"h13" 5 0
+"h14" 5 0
+"h15" 5 0
+"i15" 5 0
+"j16" 5 0
+"i16" 5 0
+"k16" 5 0
+"l16" 5 0
+"m16" 5 0
+"n16" 5 0
+"o16" 5 0
+"o15" 5 0
+"o14" 5 0
+"o13" 5 0
+"o12" 5 0
+"o11" 5 0
+"q17" 5 0
+"q16" 5 0
+"q15" 5 0
+"q14" 5 0
+"q13" 5 0
+"q11" 5 0
+"q10" 5 0
+"q9" 5 0
+"q8" 5 0
+"r8" 5 0
+"q12" 5 0
+"r9" 5 0
+"r10" 5 0
+"r11" 5 0
+"r13" 5 0
+"r14" 5 0
+"r15" 5 0
+"r16" 5 0
+"r17" 5 0
+"r12" 5 0
+"n11" 5 0
+"n10" 5 0
+"n9" 5 0
+"m9" 5 0
+"k9" 5 0
+"i9" 5 0
+"h9" 5 0
+"g9" 5 0
+"j9" 5 0
+"l9" 5 0
+"l11" 5 0
+"l12" 5 0
+"k12" 5 0
+"l15" 5 0
+"l14" 5 0
+"n15" 1 1
+"g10" 2 0
+"h10" 2 0
+"i10" 2 0
+"j10" 2 0
+"k10" 2 0
+"j11" 3 0
+"j12" 3 0
+"k13" 3 0
+"i13" 3 0
+"m13" 3 0
+"k6" 4 0
+"j6" 4 0
+"i6" 4 0
+"h6" 4 0
+"g6" 4 0
+"f6" 4 0
+"e6" 4 0
+"d6" 4 0
+"c6" 4 0
+"b6" 4 0
+"b5" 4 0
+"c5" 4 0
+"d5" 4 0
+"e5" 4 0
+"f5" 4 0
+"g5" 4 0
+"h5" 4 0
+"i5" 4 0
+"j5" 4 0
+"k5" 4 0
+"l5" 4 0
+"m5" 4 0
+"n5" 4 0
+"o5" 4 0
+"p5" 4 0
+"q5" 4 0
+"r5" 4 0
+"s5" 4 0
+"s4" 4 0
+"r4" 4 0
+"q4" 4 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"e4" 4 0
+"d4" 4 0
+"c4" 4 0
+"b4" 4 0
+"b3" 4 0
+"c3" 4 0
+"d3" 4 0
+"e3" 4 0
+"f3" 4 0
+"g3" 4 0
+"h3" 4 0
+"i3" 4 0
+"j3" 4 0
+"k3" 4 0
+"l3" 4 0
+"m3" 4 0
+"n3" 4 0
+"o3" 4 0
+"p3" 4 0
+"q3" 4 0
+"r3" 4 0
+"s3" 4 0
+"s2" 4 0
+"r2" 4 0
+"q2" 4 0
+"p2" 4 0
+"o2" 4 0
+"n2" 4 0
+"m2" 4 0
+"l2" 4 0
+"k2" 4 0
+"j2" 4 0
+"i2" 4 0
+"h2" 4 0
+"g2" 4 0
+"f2" 4 0
+"e2" 4 0
+"d2" 4 0
+"c2" 4 0
+"b2" 4 0
+"b1" 4 0
+"c1" 4 0
+"d1" 4 0
+"e1" 4 0
+"f1" 4 0
+"g1" 4 0
+"h1" 4 0
+"i1" 4 0
+"j1" 4 0
+"k1" 4 0
+"l1" 4 0
+"m1" 4 0
+"n1" 4 0
+"o1" 4 0
+"p1" 4 0
+"q1" 4 0
+"r1" 4 0
+"s1" 4 0
+"s6" 4 0
+"r6" 4 0
+"q6" 4 0
+"p6" 4 0
+"o6" 4 0
+"n6" 4 0
+"m6" 4 0
+"l6" 4 0
+"l7" 4 0
+"k7" 4 0
+"j7" 4 0
+"i7" 4 0
+"h7" 4 0
+"g7" 4 0
+"f7" 4 0
+"e7" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"b8" 4 0
+"b9" 4 0
+"b10" 4 0
+"b11" 4 0
+"b12" 4 0
+"b13" 4 0
+"b14" 4 0
+"b15" 4 0
+"b16" 4 0
+"b17" 4 0
+"b18" 4 0
+"c18" 4 0
+"d18" 4 0
+"e18" 4 0
+"f18" 4 0
+"g18" 4 0
+"h18" 4 0
+"i18" 4 0
+"j18" 4 0
+"k18" 4 0
+"l18" 4 0
+"m18" 4 0
+"n18" 4 0
+"o18" 4 0
+"p18" 4 0
+"q18" 4 0
+"r18" 4 0
+"s18" 4 0
+"s17" 4 0
+"s16" 4 0
+"s15" 4 0
+"s14" 4 0
+"s13" 4 0
+"s12" 4 0
+"s11" 4 0
+"s10" 4 0
+"s9" 4 0
+"s8" 4 0
+"s7" 4 0
+"r7" 4 0
+"q7" 4 0
+"p7" 4 0
+"o7" 4 0
+"n7" 4 0
+"m7" 4 0
+"m8" 4 0
+"l8" 4 0
+"k8" 4 0
+"j8" 4 0
+"i8" 4 0
+"h8" 4 0
+"g8" 4 0
+"f8" 4 0
+"e8" 4 0
+"e9" 4 0
+"e10" 4 0
+"e11" 4 0
+"e12" 4 0
+"f12" 4 0
+"g12" 4 0
+"g13" 4 0
+"f13" 4 0
+"e13" 4 0
+"e14" 4 0
+"f14" 4 0
+"g14" 4 0
+"g15" 4 0
+"f15" 4 0
+"e15" 4 0
+"e16" 4 0
+"f16" 4 0
+"g16" 4 0
+"h16" 4 0
+"h17" 4 0
+"g17" 4 0
+"f17" 4 0
+"e17" 4 0
+"i17" 4 0
+"j17" 4 0
+"k17" 4 0
+"l17" 4 0
+"m17" 4 0
+"n17" 4 0
+"o17" 4 0
+"p17" 4 0
+"p16" 4 0
+"p15" 4 0
+"p14" 4 0
+"p13" 4 0
+"p12" 4 0
+"p11" 4 0
+"p10" 4 0
+"o10" 4 0
+"o9" 4 0
+"p9" 4 0
+"p8" 4 0
+"o8" 4 0
+"n8" 4 0
+"s19" 4 0
+"r19" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"e19" 4 0
+"d19" 4 0
+"c19" 4 0
+"b19" 4 0
+"b20" 4 0
+"c20" 4 0
+"d20" 4 0
+"e20" 4 0
+"f20" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
index 8a2d85668d1cd0059adebb7aecb24f27650b5c28..fa22ecc60650b3d0ce95c18d84ce8afed368b1e0 100644 (file)
 // bulldozer storage "ending" last updated 16-11-2015 04:16:07
 nextlevel = "level1"
-"e15" 3 "0 0 0"
-"f14" 3 "0 0 0"
-"g13" 3 "0 0 0"
-"k13" 3 "0 0 0"
-"b14" 4 "0 0 0"
-"a14" 4 "0 0 0"
-"a13" 4 "0 0 0"
-"b13" 4 "0 0 0"
-"i13" 3 "0 0 0"
-"m13" 3 "0 0 0"
-"n14" 3 "0 0 0"
-"o15" 3 "0 0 0"
-"h17" 2 "0 0 0"
-"l17" 2 "0 0 0"
-"c12" 2 "0 0 0"
-"c13" 2 "0 0 0"
-"c14" 2 "0 0 0"
-"r14" 2 "0 0 0"
-"r13" 2 "0 0 0"
-"r12" 2 "0 0 0"
-"j15" 1 "0 -1 0"
-"s13" 4 "0 0 0"
-"t13" 4 "0 0 0"
-"t12" 4 "0 0 0"
-"s12" 4 "0 0 0"
-"b12" 4 "0 0 0"
-"a12" 4 "0 0 0"
-"a11" 4 "0 0 0"
-"b11" 4 "0 0 0"
-"c11" 4 "0 0 0"
-"d11" 4 "0 0 0"
-"e11" 4 "0 0 0"
-"f11" 4 "0 0 0"
-"g11" 4 "0 0 0"
-"h11" 4 "0 0 0"
-"i11" 4 "0 0 0"
-"j11" 4 "0 0 0"
-"k11" 4 "0 0 0"
-"l11" 4 "0 0 0"
-"m11" 4 "0 0 0"
-"n11" 4 "0 0 0"
-"o11" 4 "0 0 0"
-"p11" 4 "0 0 0"
-"q11" 4 "0 0 0"
-"r11" 4 "0 0 0"
-"s11" 4 "0 0 0"
-"t11" 4 "0 0 0"
-"t6" 6 "0 0 0"
-"s10" 4 "0 0 0"
-"r10" 4 "0 0 0"
-"q10" 4 "0 0 0"
-"b10" 6 "0 0 0"
-"o10" 4 "0 0 0"
-"c10" 6 "0 0 0"
-"d10" 6 "0 0 0"
-"e6" 4 "0 0 0"
-"b9" 6 "0 0 0"
-"b8" 6 "0 0 0"
-"c7" 4 "0 0 0"
-"b7" 6 "0 0 0"
-"b6" 6 "0 0 0"
-"c6" 6 "0 0 0"
-"d6" 6 "0 0 0"
-"f10" 6 "0 0 0"
-"g10" 6 "0 0 0"
-"h10" 6 "0 0 0"
-"a10" 4 "0 0 0"
-"a9" 4 "0 0 0"
-"h9" 4 "0 0 0"
-"c9" 4 "0 0 0"
-"d9" 4 "0 0 0"
-"h8" 4 "0 0 0"
-"h7" 4 "0 0 0"
-"i10" 6 "0 0 0"
-"i9" 6 "0 0 0"
-"j7" 4 "0 0 0"
-"j6" 4 "0 0 0"
-"j10" 4 "0 0 0"
-"i8" 6 "0 0 0"
-"m9" 4 "0 0 0"
-"i7" 6 "0 0 0"
-"o9" 4 "0 0 0"
-"i6" 6 "0 0 0"
-"q9" 4 "0 0 0"
-"r9" 4 "0 0 0"
-"s9" 4 "0 0 0"
-"h6" 6 "0 0 0"
-"g6" 6 "0 0 0"
-"s8" 4 "0 0 0"
-"r8" 4 "0 0 0"
-"q8" 4 "0 0 0"
-"f6" 6 "0 0 0"
-"o8" 4 "0 0 0"
-"f7" 6 "0 0 0"
-"m8" 4 "0 0 0"
-"f8" 6 "0 0 0"
-"f9" 6 "0 0 0"
-"k10" 6 "0 0 0"
-"e10" 4 "0 0 0"
-"d7" 4 "0 0 0"
-"e9" 4 "0 0 0"
-"e8" 4 "0 0 0"
-"e7" 4 "0 0 0"
-"l10" 6 "0 0 0"
-"c8" 4 "0 0 0"
-"m10" 6 "0 0 0"
-"a8" 4 "0 0 0"
-"a7" 4 "0 0 0"
-"g7" 4 "0 0 0"
-"g8" 4 "0 0 0"
-"g9" 4 "0 0 0"
-"n10" 6 "0 0 0"
-"n9" 6 "0 0 0"
-"n8" 6 "0 0 0"
-"d8" 4 "0 0 0"
-"n7" 6 "0 0 0"
-"n6" 6 "0 0 0"
-"m6" 6 "0 0 0"
-"l6" 6 "0 0 0"
-"m7" 4 "0 0 0"
-"k6" 6 "0 0 0"
-"o7" 4 "0 0 0"
-"k7" 6 "0 0 0"
-"q7" 4 "0 0 0"
-"r7" 4 "0 0 0"
-"s7" 4 "0 0 0"
-"k8" 6 "0 0 0"
-"k9" 6 "0 0 0"
-"s6" 4 "0 0 0"
-"p9" 6 "0 0 0"
-"p8" 6 "0 0 0"
-"p7" 6 "0 0 0"
-"o6" 4 "0 0 0"
-"p6" 6 "0 0 0"
-"q6" 6 "0 0 0"
-"r6" 6 "0 0 0"
-"p10" 6 "0 0 0"
-"j9" 4 "0 0 0"
-"j8" 4 "0 0 0"
-"t10" 6 "0 0 0"
-"t9" 6 "0 0 0"
-"t8" 6 "0 0 0"
-"l7" 4 "0 0 0"
-"l8" 4 "0 0 0"
-"l9" 4 "0 0 0"
-"t7" 4 "0 0 0"
-"a6" 4 "0 0 0"
-"a5" 4 "0 0 0"
-"b5" 4 "0 0 0"
-"c5" 4 "0 0 0"
-"d5" 4 "0 0 0"
-"e5" 4 "0 0 0"
-"f5" 4 "0 0 0"
-"g5" 4 "0 0 0"
-"h5" 4 "0 0 0"
-"i5" 4 "0 0 0"
-"j5" 4 "0 0 0"
-"k5" 4 "0 0 0"
-"l5" 4 "0 0 0"
-"m5" 4 "0 0 0"
-"n5" 4 "0 0 0"
-"o5" 4 "0 0 0"
-"p5" 4 "0 0 0"
-"q5" 4 "0 0 0"
-"r5" 4 "0 0 0"
-"s5" 4 "0 0 0"
-"t5" 4 "0 0 0"
-"t4" 4 "0 0 0"
-"s4" 4 "0 0 0"
-"r4" 4 "0 0 0"
-"q4" 4 "0 0 0"
-"p4" 4 "0 0 0"
-"o4" 4 "0 0 0"
-"n4" 4 "0 0 0"
-"m4" 4 "0 0 0"
-"l4" 4 "0 0 0"
-"k4" 4 "0 0 0"
-"j4" 4 "0 0 0"
-"i4" 4 "0 0 0"
-"h4" 4 "0 0 0"
-"g4" 4 "0 0 0"
-"f4" 4 "0 0 0"
-"e4" 4 "0 0 0"
-"d4" 4 "0 0 0"
-"c4" 4 "0 0 0"
-"b4" 4 "0 0 0"
-"a4" 4 "0 0 0"
-"a3" 4 "0 0 0"
-"b3" 4 "0 0 0"
-"c3" 4 "0 0 0"
-"d3" 4 "0 0 0"
-"e3" 4 "0 0 0"
-"f3" 4 "0 0 0"
-"g3" 4 "0 0 0"
-"h3" 4 "0 0 0"
-"i3" 4 "0 0 0"
-"j3" 4 "0 0 0"
-"k3" 4 "0 0 0"
-"l3" 4 "0 0 0"
-"m3" 4 "0 0 0"
-"n3" 4 "0 0 0"
-"o3" 4 "0 0 0"
-"p3" 4 "0 0 0"
-"q3" 4 "0 0 0"
-"r3" 4 "0 0 0"
-"s3" 4 "0 0 0"
-"t3" 4 "0 0 0"
-"t2" 4 "0 0 0"
-"s2" 4 "0 0 0"
-"r2" 4 "0 0 0"
-"q2" 4 "0 0 0"
-"p2" 4 "0 0 0"
-"o2" 4 "0 0 0"
-"n2" 4 "0 0 0"
-"m2" 4 "0 0 0"
-"l2" 4 "0 0 0"
-"k2" 4 "0 0 0"
-"j2" 4 "0 0 0"
-"i2" 4 "0 0 0"
-"h2" 4 "0 0 0"
-"g2" 4 "0 0 0"
-"f2" 4 "0 0 0"
-"e2" 4 "0 0 0"
-"d2" 4 "0 0 0"
-"c2" 4 "0 0 0"
-"b2" 4 "0 0 0"
-"a2" 4 "0 0 0"
-"a1" 4 "0 0 0"
-"b1" 4 "0 0 0"
-"c1" 4 "0 0 0"
-"d1" 4 "0 0 0"
-"e1" 4 "0 0 0"
-"f1" 4 "0 0 0"
-"g1" 4 "0 0 0"
-"h1" 4 "0 0 0"
-"i1" 4 "0 0 0"
-"j1" 4 "0 0 0"
-"k1" 4 "0 0 0"
-"l1" 4 "0 0 0"
-"m1" 4 "0 0 0"
-"n1" 4 "0 0 0"
-"o1" 4 "0 0 0"
-"p1" 4 "0 0 0"
-"q1" 4 "0 0 0"
-"r1" 4 "0 0 0"
-"s1" 4 "0 0 0"
-"t1" 4 "0 0 0"
-"t14" 4 "0 0 0"
-"s14" 4 "0 0 0"
-"b15" 4 "0 0 0"
-"a15" 4 "0 0 0"
-"a16" 4 "0 0 0"
-"b16" 4 "0 0 0"
-"s16" 4 "0 0 0"
-"t16" 4 "0 0 0"
-"t15" 4 "0 0 0"
-"s15" 4 "0 0 0"
-"t17" 4 "0 0 0"
-"s17" 4 "0 0 0"
-"b17" 4 "0 0 0"
-"a17" 4 "0 0 0"
-"a18" 4 "0 0 0"
-"b18" 4 "0 0 0"
-"s18" 4 "0 0 0"
-"t18" 4 "0 0 0"
-"t19" 4 "0 0 0"
-"s19" 4 "0 0 0"
-"r19" 4 "0 0 0"
-"q19" 4 "0 0 0"
-"p19" 4 "0 0 0"
-"o19" 4 "0 0 0"
-"n19" 4 "0 0 0"
-"m19" 4 "0 0 0"
-"l19" 4 "0 0 0"
-"k19" 4 "0 0 0"
-"j19" 4 "0 0 0"
-"i19" 4 "0 0 0"
-"h19" 4 "0 0 0"
-"g19" 4 "0 0 0"
-"f19" 4 "0 0 0"
-"e19" 4 "0 0 0"
-"d19" 4 "0 0 0"
-"c19" 4 "0 0 0"
-"b19" 4 "0 0 0"
-"a19" 4 "0 0 0"
-"a20" 4 "0 0 0"
-"b20" 4 "0 0 0"
-"c20" 4 "0 0 0"
-"d20" 4 "0 0 0"
-"e20" 4 "0 0 0"
-"f20" 4 "0 0 0"
-"g20" 4 "0 0 0"
-"h20" 4 "0 0 0"
-"i20" 4 "0 0 0"
-"j20" 4 "0 0 0"
-"k20" 4 "0 0 0"
-"l20" 4 "0 0 0"
-"m20" 4 "0 0 0"
-"n20" 4 "0 0 0"
-"o20" 4 "0 0 0"
-"p20" 4 "0 0 0"
-"q20" 4 "0 0 0"
-"r20" 4 "0 0 0"
-"s20" 4 "0 0 0"
-"t20" 4 "0 0 0"
+"e15" 3 0
+"f14" 3 0
+"g13" 3 0
+"k13" 3 0
+"b14" 4 0
+"a14" 4 0
+"a13" 4 0
+"b13" 4 0
+"i13" 3 0
+"m13" 3 0
+"n14" 3 0
+"o15" 3 0
+"h17" 2 0
+"l17" 2 0
+"c12" 2 0
+"c13" 2 0
+"c14" 2 0
+"r14" 2 0
+"r13" 2 0
+"r12" 2 0
+"j15" 1 1
+"s13" 4 0
+"t13" 4 0
+"t12" 4 0
+"s12" 4 0
+"b12" 4 0
+"a12" 4 0
+"a11" 4 0
+"b11" 4 0
+"c11" 4 0
+"d11" 4 0
+"e11" 4 0
+"f11" 4 0
+"g11" 4 0
+"h11" 4 0
+"i11" 4 0
+"j11" 4 0
+"k11" 4 0
+"l11" 4 0
+"m11" 4 0
+"n11" 4 0
+"o11" 4 0
+"p11" 4 0
+"q11" 4 0
+"r11" 4 0
+"s11" 4 0
+"t11" 4 0
+"t6" 6 0
+"s10" 4 0
+"r10" 4 0
+"q10" 4 0
+"b10" 6 0
+"o10" 4 0
+"c10" 6 0
+"d10" 6 0
+"e6" 4 0
+"b9" 6 0
+"b8" 6 0
+"c7" 4 0
+"b7" 6 0
+"b6" 6 0
+"c6" 6 0
+"d6" 6 0
+"f10" 6 0
+"g10" 6 0
+"h10" 6 0
+"a10" 4 0
+"a9" 4 0
+"h9" 4 0
+"c9" 4 0
+"d9" 4 0
+"h8" 4 0
+"h7" 4 0
+"i10" 6 0
+"i9" 6 0
+"j7" 4 0
+"j6" 4 0
+"j10" 4 0
+"i8" 6 0
+"m9" 4 0
+"i7" 6 0
+"o9" 4 0
+"i6" 6 0
+"q9" 4 0
+"r9" 4 0
+"s9" 4 0
+"h6" 6 0
+"g6" 6 0
+"s8" 4 0
+"r8" 4 0
+"q8" 4 0
+"f6" 6 0
+"o8" 4 0
+"f7" 6 0
+"m8" 4 0
+"f8" 6 0
+"f9" 6 0
+"k10" 6 0
+"e10" 4 0
+"d7" 4 0
+"e9" 4 0
+"e8" 4 0
+"e7" 4 0
+"l10" 6 0
+"c8" 4 0
+"m10" 6 0
+"a8" 4 0
+"a7" 4 0
+"g7" 4 0
+"g8" 4 0
+"g9" 4 0
+"n10" 6 0
+"n9" 6 0
+"n8" 6 0
+"d8" 4 0
+"n7" 6 0
+"n6" 6 0
+"m6" 6 0
+"l6" 6 0
+"m7" 4 0
+"k6" 6 0
+"o7" 4 0
+"k7" 6 0
+"q7" 4 0
+"r7" 4 0
+"s7" 4 0
+"k8" 6 0
+"k9" 6 0
+"s6" 4 0
+"p9" 6 0
+"p8" 6 0
+"p7" 6 0
+"o6" 4 0
+"p6" 6 0
+"q6" 6 0
+"r6" 6 0
+"p10" 6 0
+"j9" 4 0
+"j8" 4 0
+"t10" 6 0
+"t9" 6 0
+"t8" 6 0
+"l7" 4 0
+"l8" 4 0
+"l9" 4 0
+"t7" 4 0
+"a6" 4 0
+"a5" 4 0
+"b5" 4 0
+"c5" 4 0
+"d5" 4 0
+"e5" 4 0
+"f5" 4 0
+"g5" 4 0
+"h5" 4 0
+"i5" 4 0
+"j5" 4 0
+"k5" 4 0
+"l5" 4 0
+"m5" 4 0
+"n5" 4 0
+"o5" 4 0
+"p5" 4 0
+"q5" 4 0
+"r5" 4 0
+"s5" 4 0
+"t5" 4 0
+"t4" 4 0
+"s4" 4 0
+"r4" 4 0
+"q4" 4 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"e4" 4 0
+"d4" 4 0
+"c4" 4 0
+"b4" 4 0
+"a4" 4 0
+"a3" 4 0
+"b3" 4 0
+"c3" 4 0
+"d3" 4 0
+"e3" 4 0
+"f3" 4 0
+"g3" 4 0
+"h3" 4 0
+"i3" 4 0
+"j3" 4 0
+"k3" 4 0
+"l3" 4 0
+"m3" 4 0
+"n3" 4 0
+"o3" 4 0
+"p3" 4 0
+"q3" 4 0
+"r3" 4 0
+"s3" 4 0
+"t3" 4 0
+"t2" 4 0
+"s2" 4 0
+"r2" 4 0
+"q2" 4 0
+"p2" 4 0
+"o2" 4 0
+"n2" 4 0
+"m2" 4 0
+"l2" 4 0
+"k2" 4 0
+"j2" 4 0
+"i2" 4 0
+"h2" 4 0
+"g2" 4 0
+"f2" 4 0
+"e2" 4 0
+"d2" 4 0
+"c2" 4 0
+"b2" 4 0
+"a2" 4 0
+"a1" 4 0
+"b1" 4 0
+"c1" 4 0
+"d1" 4 0
+"e1" 4 0
+"f1" 4 0
+"g1" 4 0
+"h1" 4 0
+"i1" 4 0
+"j1" 4 0
+"k1" 4 0
+"l1" 4 0
+"m1" 4 0
+"n1" 4 0
+"o1" 4 0
+"p1" 4 0
+"q1" 4 0
+"r1" 4 0
+"s1" 4 0
+"t1" 4 0
+"t14" 4 0
+"s14" 4 0
+"b15" 4 0
+"a15" 4 0
+"a16" 4 0
+"b16" 4 0
+"s16" 4 0
+"t16" 4 0
+"t15" 4 0
+"s15" 4 0
+"t17" 4 0
+"s17" 4 0
+"b17" 4 0
+"a17" 4 0
+"a18" 4 0
+"b18" 4 0
+"s18" 4 0
+"t18" 4 0
+"t19" 4 0
+"s19" 4 0
+"r19" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"e19" 4 0
+"d19" 4 0
+"c19" 4 0
+"b19" 4 0
+"a19" 4 0
+"a20" 4 0
+"b20" 4 0
+"c20" 4 0
+"d20" 4 0
+"e20" 4 0
+"f20" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
+"t20" 4 0
index 1be5fb60989dfb56c69440b0eafed0190f1503ff..3da61ac5233150d6ba08c65aa47980d368378168 100644 (file)
 // bulldozer storage "level2" last updated 16-11-2015 03:34:13
 nextlevel = "level3"
-"a20" 4 "0 0 0"
-"b20" 4 "0 0 0"
-"c20" 4 "0 0 0"
-"d20" 4 "0 0 0"
-"e20" 4 "0 0 0"
-"r13" 5 "0 0 0"
-"f20" 4 "0 0 0"
-"g20" 4 "0 0 0"
-"c12" 5 "0 0 0"
-"h20" 4 "0 0 0"
-"c13" 5 "0 0 0"
-"i20" 4 "0 0 0"
-"c14" 5 "0 0 0"
-"j20" 4 "0 0 0"
-"c15" 5 "0 0 0"
-"k20" 4 "0 0 0"
-"c16" 5 "0 0 0"
-"l20" 4 "0 0 0"
-"c17" 5 "0 0 0"
-"m20" 4 "0 0 0"
-"c18" 5 "0 0 0"
-"n20" 4 "0 0 0"
-"c19" 5 "0 0 0"
-"o20" 4 "0 0 0"
-"r18" 5 "0 0 0"
-"p20" 4 "0 0 0"
-"r17" 5 "0 0 0"
-"q20" 4 "0 0 0"
-"r12" 5 "0 0 0"
-"r20" 4 "0 0 0"
-"r15" 5 "0 0 0"
-"r14" 5 "0 0 0"
-"s20" 4 "0 0 0"
-"t20" 4 "0 0 0"
-"t19" 4 "0 0 0"
-"t18" 4 "0 0 0"
-"t17" 4 "0 0 0"
-"t16" 4 "0 0 0"
-"t15" 4 "0 0 0"
-"t14" 4 "0 0 0"
-"t13" 4 "0 0 0"
-"t9" 4 "0 0 0"
-"s9" 4 "0 0 0"
-"r9" 4 "0 0 0"
-"q9" 4 "0 0 0"
-"p9" 4 "0 0 0"
-"o9" 4 "0 0 0"
-"n9" 4 "0 0 0"
-"m9" 4 "0 0 0"
-"l9" 4 "0 0 0"
-"k9" 4 "0 0 0"
-"j9" 4 "0 0 0"
-"i9" 4 "0 0 0"
-"h9" 4 "0 0 0"
-"g9" 4 "0 0 0"
-"f9" 4 "0 0 0"
-"e9" 4 "0 0 0"
-"d9" 4 "0 0 0"
-"c9" 4 "0 0 0"
-"b9" 4 "0 0 0"
-"a9" 4 "0 0 0"
-"a10" 4 "0 0 0"
-"a11" 4 "0 0 0"
-"a12" 4 "0 0 0"
-"a13" 4 "0 0 0"
-"a14" 4 "0 0 0"
-"a15" 4 "0 0 0"
-"a16" 4 "0 0 0"
-"a17" 4 "0 0 0"
-"a18" 4 "0 0 0"
-"a19" 4 "0 0 0"
-"t12" 4 "0 0 0"
-"t11" 4 "0 0 0"
-"t10" 4 "0 0 0"
-"k4" 4 "0 0 0"
-"t3" 4 "0 0 0"
-"s4" 4 "0 0 0"
-"t2" 4 "0 0 0"
-"l5" 4 "0 0 0"
-"j6" 4 "0 0 0"
-"r11" 5 "0 0 0"
-"r6" 4 "0 0 0"
-"r10" 5 "0 0 0"
-"b7" 5 "0 0 0"
-"i6" 4 "0 0 0"
-"a7" 5 "0 0 0"
-"a2" 4 "0 0 0"
-"c4" 5 "0 0 0"
-"b8" 4 "0 0 0"
-"a6" 5 "0 0 0"
-"o6" 4 "0 0 0"
-"p6" 4 "0 0 0"
-"a5" 5 "0 0 0"
-"l7" 4 "0 0 0"
-"l6" 4 "0 0 0"
-"t6" 4 "0 0 0"
-"t5" 4 "0 0 0"
-"b19" 2 "0 0 0"
-"k5" 4 "0 0 0"
-"b18" 2 "0 0 0"
-"p5" 4 "0 0 0"
-"j4" 4 "0 0 0"
-"k15" 1 "0 -1 0"
-"g16" 3 "0 0 0"
-"g12" 3 "0 0 0"
-"l3" 4 "0 0 0"
-"b3" 5 "0 0 0"
-"i5" 4 "0 0 0"
-"c8" 4 "0 0 0"
-"c5" 5 "0 0 0"
-"c6" 5 "0 0 0"
-"a4" 5 "0 0 0"
-"a3" 5 "0 0 0"
-"a8" 4 "0 0 0"
-"c3" 4 "0 0 0"
-"c7" 4 "0 0 0"
-"k6" 5 "0 0 0"
-"d6" 4 "0 0 0"
-"e7" 5 "0 0 0"
-"e6" 5 "0 0 0"
-"s10" 2 "0 0 0"
-"d5" 4 "0 0 0"
-"s11" 2 "0 0 0"
-"d4" 4 "0 0 0"
-"o17" 3 "0 0 0"
-"o13" 3 "0 0 0"
-"l4" 4 "0 0 0"
-"h7" 4 "0 0 0"
-"o4" 4 "0 0 0"
-"p4" 4 "0 0 0"
-"t4" 4 "0 0 0"
-"h6" 4 "0 0 0"
-"h5" 4 "0 0 0"
-"h4" 4 "0 0 0"
-"h3" 4 "0 0 0"
-"f4" 4 "0 0 0"
-"f5" 4 "0 0 0"
-"f6" 4 "0 0 0"
-"n6" 4 "0 0 0"
-"d3" 4 "0 0 0"
-"o5" 4 "0 0 0"
-"n4" 4 "0 0 0"
-"e5" 5 "0 0 0"
-"b2" 4 "0 0 0"
-"c2" 4 "0 0 0"
-"d2" 4 "0 0 0"
-"e2" 4 "0 0 0"
-"f2" 4 "0 0 0"
-"g2" 4 "0 0 0"
-"h2" 4 "0 0 0"
-"i2" 4 "0 0 0"
-"j2" 4 "0 0 0"
-"k2" 4 "0 0 0"
-"l2" 4 "0 0 0"
-"m2" 4 "0 0 0"
-"n2" 4 "0 0 0"
-"o2" 4 "0 0 0"
-"p2" 4 "0 0 0"
-"q2" 4 "0 0 0"
-"r2" 4 "0 0 0"
-"s2" 4 "0 0 0"
-"t1" 4 "0 0 0"
-"s1" 4 "0 0 0"
-"r1" 4 "0 0 0"
-"q1" 4 "0 0 0"
-"p1" 4 "0 0 0"
-"o1" 4 "0 0 0"
-"n1" 4 "0 0 0"
-"m1" 4 "0 0 0"
-"l1" 4 "0 0 0"
-"k1" 4 "0 0 0"
-"j1" 4 "0 0 0"
-"i1" 4 "0 0 0"
-"h1" 4 "0 0 0"
-"g1" 4 "0 0 0"
-"f1" 4 "0 0 0"
-"e1" 4 "0 0 0"
-"d1" 4 "0 0 0"
-"c1" 4 "0 0 0"
-"b1" 4 "0 0 0"
-"a1" 4 "0 0 0"
-"t7" 4 "0 0 0"
-"p3" 4 "0 0 0"
-"p7" 4 "0 0 0"
-"d7" 4 "0 0 0"
-"r3" 4 "0 0 0"
-"e4" 5 "0 0 0"
-"e3" 5 "0 0 0"
-"f3" 5 "0 0 0"
-"d8" 4 "0 0 0"
-"e8" 4 "0 0 0"
-"f8" 4 "0 0 0"
-"g8" 4 "0 0 0"
-"h8" 4 "0 0 0"
-"i8" 4 "0 0 0"
-"j8" 4 "0 0 0"
-"k8" 4 "0 0 0"
-"l8" 4 "0 0 0"
-"m8" 4 "0 0 0"
-"n8" 4 "0 0 0"
-"o8" 4 "0 0 0"
-"p8" 4 "0 0 0"
-"q8" 4 "0 0 0"
-"r8" 4 "0 0 0"
-"s8" 4 "0 0 0"
-"t8" 4 "0 0 0"
-"g3" 5 "0 0 0"
-"g7" 5 "0 0 0"
-"g4" 5 "0 0 0"
-"g6" 5 "0 0 0"
-"g5" 5 "0 0 0"
-"f7" 5 "0 0 0"
-"j5" 5 "0 0 0"
-"i7" 5 "0 0 0"
-"j7" 5 "0 0 0"
-"k7" 5 "0 0 0"
-"i4" 5 "0 0 0"
-"i3" 5 "0 0 0"
-"j3" 5 "0 0 0"
-"k3" 5 "0 0 0"
-"m7" 5 "0 0 0"
-"m6" 5 "0 0 0"
-"m4" 5 "0 0 0"
-"m5" 5 "0 0 0"
-"m3" 5 "0 0 0"
-"n3" 5 "0 0 0"
-"o3" 5 "0 0 0"
-"n5" 5 "0 0 0"
-"n7" 5 "0 0 0"
-"o7" 5 "0 0 0"
-"q7" 5 "0 0 0"
-"q6" 5 "0 0 0"
-"q4" 5 "0 0 0"
-"q5" 5 "0 0 0"
-"q3" 5 "0 0 0"
-"r5" 5 "0 0 0"
-"r4" 5 "0 0 0"
-"s3" 5 "0 0 0"
-"s5" 5 "0 0 0"
-"s6" 5 "0 0 0"
-"s7" 5 "0 0 0"
-"r7" 5 "0 0 0"
-"b6" 4 "0 0 0"
-"b5" 4 "0 0 0"
-"b4" 4 "0 0 0"
+"a20" 4 0
+"b20" 4 0
+"c20" 4 0
+"d20" 4 0
+"e20" 4 0
+"r13" 5 0
+"f20" 4 0
+"g20" 4 0
+"c12" 5 0
+"h20" 4 0
+"c13" 5 0
+"i20" 4 0
+"c14" 5 0
+"j20" 4 0
+"c15" 5 0
+"k20" 4 0
+"c16" 5 0
+"l20" 4 0
+"c17" 5 0
+"m20" 4 0
+"c18" 5 0
+"n20" 4 0
+"c19" 5 0
+"o20" 4 0
+"r18" 5 0
+"p20" 4 0
+"r17" 5 0
+"q20" 4 0
+"r12" 5 0
+"r20" 4 0
+"r15" 5 0
+"r14" 5 0
+"s20" 4 0
+"t20" 4 0
+"t19" 4 0
+"t18" 4 0
+"t17" 4 0
+"t16" 4 0
+"t15" 4 0
+"t14" 4 0
+"t13" 4 0
+"t9" 4 0
+"s9" 4 0
+"r9" 4 0
+"q9" 4 0
+"p9" 4 0
+"o9" 4 0
+"n9" 4 0
+"m9" 4 0
+"l9" 4 0
+"k9" 4 0
+"j9" 4 0
+"i9" 4 0
+"h9" 4 0
+"g9" 4 0
+"f9" 4 0
+"e9" 4 0
+"d9" 4 0
+"c9" 4 0
+"b9" 4 0
+"a9" 4 0
+"a10" 4 0
+"a11" 4 0
+"a12" 4 0
+"a13" 4 0
+"a14" 4 0
+"a15" 4 0
+"a16" 4 0
+"a17" 4 0
+"a18" 4 0
+"a19" 4 0
+"t12" 4 0
+"t11" 4 0
+"t10" 4 0
+"k4" 4 0
+"t3" 4 0
+"s4" 4 0
+"t2" 4 0
+"l5" 4 0
+"j6" 4 0
+"r11" 5 0
+"r6" 4 0
+"r10" 5 0
+"b7" 5 0
+"i6" 4 0
+"a7" 5 0
+"a2" 4 0
+"c4" 5 0
+"b8" 4 0
+"a6" 5 0
+"o6" 4 0
+"p6" 4 0
+"a5" 5 0
+"l7" 4 0
+"l6" 4 0
+"t6" 4 0
+"t5" 4 0
+"b19" 2 0
+"k5" 4 0
+"b18" 2 0
+"p5" 4 0
+"j4" 4 0
+"k15" 1 1
+"g16" 3 0
+"g12" 3 0
+"l3" 4 0
+"b3" 5 0
+"i5" 4 0
+"c8" 4 0
+"c5" 5 0
+"c6" 5 0
+"a4" 5 0
+"a3" 5 0
+"a8" 4 0
+"c3" 4 0
+"c7" 4 0
+"k6" 5 0
+"d6" 4 0
+"e7" 5 0
+"e6" 5 0
+"s10" 2 0
+"d5" 4 0
+"s11" 2 0
+"d4" 4 0
+"o17" 3 0
+"o13" 3 0
+"l4" 4 0
+"h7" 4 0
+"o4" 4 0
+"p4" 4 0
+"t4" 4 0
+"h6" 4 0
+"h5" 4 0
+"h4" 4 0
+"h3" 4 0
+"f4" 4 0
+"f5" 4 0
+"f6" 4 0
+"n6" 4 0
+"d3" 4 0
+"o5" 4 0
+"n4" 4 0
+"e5" 5 0
+"b2" 4 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"k2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"s2" 4 0
+"t1" 4 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"a1" 4 0
+"t7" 4 0
+"p3" 4 0
+"p7" 4 0
+"d7" 4 0
+"r3" 4 0
+"e4" 5 0
+"e3" 5 0
+"f3" 5 0
+"d8" 4 0
+"e8" 4 0
+"f8" 4 0
+"g8" 4 0
+"h8" 4 0
+"i8" 4 0
+"j8" 4 0
+"k8" 4 0
+"l8" 4 0
+"m8" 4 0
+"n8" 4 0
+"o8" 4 0
+"p8" 4 0
+"q8" 4 0
+"r8" 4 0
+"s8" 4 0
+"t8" 4 0
+"g3" 5 0
+"g7" 5 0
+"g4" 5 0
+"g6" 5 0
+"g5" 5 0
+"f7" 5 0
+"j5" 5 0
+"i7" 5 0
+"j7" 5 0
+"k7" 5 0
+"i4" 5 0
+"i3" 5 0
+"j3" 5 0
+"k3" 5 0
+"m7" 5 0
+"m6" 5 0
+"m4" 5 0
+"m5" 5 0
+"m3" 5 0
+"n3" 5 0
+"o3" 5 0
+"n5" 5 0
+"n7" 5 0
+"o7" 5 0
+"q7" 5 0
+"q6" 5 0
+"q4" 5 0
+"q5" 5 0
+"q3" 5 0
+"r5" 5 0
+"r4" 5 0
+"s3" 5 0
+"s5" 5 0
+"s6" 5 0
+"s7" 5 0
+"r7" 5 0
+"b6" 4 0
+"b5" 4 0
+"b4" 4 0
index 7433ae2095d75de30c35252c0b0b6f5736b0765d..2b12c6757ab46b66226732913ff0f2e68c730101 100644 (file)
 // bulldozer storage "level3" last updated 16-11-2015 04:06:54
 nextlevel = "level4"
-"a20" 4 "0 0 0"
-"a19" 4 "0 0 0"
-"f14" 2 "0 0 0"
-"a18" 4 "0 0 0"
-"a17" 4 "0 0 0"
-"a16" 4 "0 0 0"
-"a15" 4 "0 0 0"
-"f14" 3 "0 0 0"
-"b7" 4 "0 0 0"
-"b5" 4 "0 0 0"
-"b3" 4 "0 0 0"
-"s7" 4 "0 0 0"
-"s5" 4 "0 0 0"
-"s3" 4 "0 0 0"
-"b19" 4 "0 0 0"
-"s19" 4 "0 0 0"
-"a14" 4 "0 0 0"
-"a13" 4 "0 0 0"
-"a12" 4 "0 0 0"
-"a11" 4 "0 0 0"
-"a10" 4 "0 0 0"
-"a9" 4 "0 0 0"
-"a8" 4 "0 0 0"
-"a7" 4 "0 0 0"
-"a6" 4 "0 0 0"
-"a5" 4 "0 0 0"
-"a4" 4 "0 0 0"
-"a3" 4 "0 0 0"
-"a2" 4 "0 0 0"
-"a1" 4 "0 0 0"
-"b1" 4 "0 0 0"
-"c1" 4 "0 0 0"
-"d1" 4 "0 0 0"
-"e1" 4 "0 0 0"
-"f1" 4 "0 0 0"
-"g1" 4 "0 0 0"
-"h1" 4 "0 0 0"
-"i1" 4 "0 0 0"
-"j1" 4 "0 0 0"
-"k1" 4 "0 0 0"
-"l1" 4 "0 0 0"
-"m1" 4 "0 0 0"
-"n1" 4 "0 0 0"
-"o1" 4 "0 0 0"
-"p1" 4 "0 0 0"
-"q1" 4 "0 0 0"
-"r1" 4 "0 0 0"
-"s1" 4 "0 0 0"
-"t1" 4 "0 0 0"
-"t2" 4 "0 0 0"
-"t3" 4 "0 0 0"
-"t4" 4 "0 0 0"
-"t5" 4 "0 0 0"
-"t6" 4 "0 0 0"
-"t12" 4 "0 0 0"
-"t9" 4 "0 0 0"
-"t10" 4 "0 0 0"
-"t11" 4 "0 0 0"
-"t7" 4 "0 0 0"
-"t8" 4 "0 0 0"
-"t13" 4 "0 0 0"
-"t14" 4 "0 0 0"
-"t15" 4 "0 0 0"
-"t16" 4 "0 0 0"
-"t17" 4 "0 0 0"
-"t18" 4 "0 0 0"
-"t19" 4 "0 0 0"
-"t20" 4 "0 0 0"
-"r20" 4 "0 0 0"
-"q20" 4 "0 0 0"
-"p20" 4 "0 0 0"
-"s20" 4 "0 0 0"
-"o20" 4 "0 0 0"
-"n20" 4 "0 0 0"
-"m20" 4 "0 0 0"
-"l20" 4 "0 0 0"
-"k20" 4 "0 0 0"
-"j20" 4 "0 0 0"
-"i20" 4 "0 0 0"
-"h20" 4 "0 0 0"
-"g20" 4 "0 0 0"
-"f20" 4 "0 0 0"
-"e20" 4 "0 0 0"
-"d20" 4 "0 0 0"
-"c20" 4 "0 0 0"
-"b20" 4 "0 0 0"
-"c19" 4 "0 0 0"
-"b18" 4 "0 0 0"
-"b16" 4 "0 0 0"
-"c18" 4 "0 0 0"
-"b14" 4 "0 0 0"
-"b17" 7 "0 0 0"
-"b12" 4 "0 0 0"
-"b15" 7 "0 0 0"
-"b9" 7 "0 0 0"
-"b8" 4 "0 0 0"
-"b11" 7 "0 0 0"
-"b10" 4 "0 0 0"
-"b6" 4 "0 0 0"
-"b13" 7 "0 0 0"
-"b2" 4 "0 0 0"
-"b4" 4 "0 0 0"
-"s18" 4 "0 0 0"
-"s13" 7 "0 0 0"
-"s16" 4 "0 0 0"
-"s11" 7 "0 0 0"
-"s14" 4 "0 0 0"
-"s9" 7 "0 0 0"
-"s12" 4 "0 0 0"
-"s8" 4 "0 0 0"
-"s6" 4 "0 0 0"
-"s4" 4 "0 0 0"
-"s2" 4 "0 0 0"
-"s10" 4 "0 0 0"
-"s17" 7 "0 0 0"
-"s15" 7 "0 0 0"
-"c7" 4 "0 0 0"
-"c6" 4 "0 0 0"
-"d7" 4 "0 0 0"
-"f7" 4 "0 0 0"
-"e7" 4 "0 0 0"
-"g7" 4 "0 0 0"
-"h7" 4 "0 0 0"
-"i7" 4 "0 0 0"
-"j7" 4 "0 0 0"
-"k7" 4 "0 0 0"
-"l7" 4 "0 0 0"
-"m7" 4 "0 0 0"
-"n7" 4 "0 0 0"
-"o7" 4 "0 0 0"
-"p7" 4 "0 0 0"
-"q7" 4 "0 0 0"
-"r7" 4 "0 0 0"
-"h4" 4 "0 0 0"
-"g4" 4 "0 0 0"
-"f4" 4 "0 0 0"
-"e4" 4 "0 0 0"
-"d4" 4 "0 0 0"
-"c4" 4 "0 0 0"
-"c3" 4 "0 0 0"
-"d3" 4 "0 0 0"
-"e3" 4 "0 0 0"
-"f3" 4 "0 0 0"
-"g3" 4 "0 0 0"
-"h3" 4 "0 0 0"
-"i3" 4 "0 0 0"
-"j3" 4 "0 0 0"
-"k3" 4 "0 0 0"
-"l3" 4 "0 0 0"
-"m3" 4 "0 0 0"
-"n3" 4 "0 0 0"
-"o3" 4 "0 0 0"
-"p3" 4 "0 0 0"
-"q3" 4 "0 0 0"
-"r3" 4 "0 0 0"
-"r2" 4 "0 0 0"
-"q2" 4 "0 0 0"
-"p2" 4 "0 0 0"
-"o2" 4 "0 0 0"
-"n2" 4 "0 0 0"
-"m2" 4 "0 0 0"
-"l2" 4 "0 0 0"
-"k2" 4 "0 0 0"
-"j2" 4 "0 0 0"
-"i2" 4 "0 0 0"
-"h2" 4 "0 0 0"
-"g2" 4 "0 0 0"
-"f2" 4 "0 0 0"
-"e2" 4 "0 0 0"
-"d2" 4 "0 0 0"
-"c2" 4 "0 0 0"
-"r4" 4 "0 0 0"
-"q4" 4 "0 0 0"
-"p4" 4 "0 0 0"
-"o4" 4 "0 0 0"
-"n4" 4 "0 0 0"
-"m4" 4 "0 0 0"
-"l4" 4 "0 0 0"
-"k4" 4 "0 0 0"
-"j4" 4 "0 0 0"
-"i4" 4 "0 0 0"
-"i5" 4 "0 0 0"
-"h5" 4 "0 0 0"
-"g5" 4 "0 0 0"
-"f5" 4 "0 0 0"
-"e5" 4 "0 0 0"
-"d5" 4 "0 0 0"
-"c5" 4 "0 0 0"
-"d6" 4 "0 0 0"
-"e6" 4 "0 0 0"
-"f6" 4 "0 0 0"
-"g6" 4 "0 0 0"
-"h6" 4 "0 0 0"
-"i6" 4 "0 0 0"
-"j6" 4 "0 0 0"
-"k6" 4 "0 0 0"
-"l6" 4 "0 0 0"
-"m6" 4 "0 0 0"
-"n6" 4 "0 0 0"
-"o6" 4 "0 0 0"
-"p6" 4 "0 0 0"
-"q6" 4 "0 0 0"
-"r6" 4 "0 0 0"
-"r5" 4 "0 0 0"
-"q5" 4 "0 0 0"
-"p5" 4 "0 0 0"
-"o5" 4 "0 0 0"
-"n5" 4 "0 0 0"
-"m5" 4 "0 0 0"
-"l5" 4 "0 0 0"
-"k5" 4 "0 0 0"
-"j5" 4 "0 0 0"
-"d18" 4 "0 0 0"
-"e18" 4 "0 0 0"
-"g18" 4 "0 0 0"
-"h18" 4 "0 0 0"
-"f18" 4 "0 0 0"
-"j18" 4 "0 0 0"
-"i18" 4 "0 0 0"
-"k18" 4 "0 0 0"
-"m18" 4 "0 0 0"
-"l18" 4 "0 0 0"
-"n18" 4 "0 0 0"
-"o18" 4 "0 0 0"
-"p18" 4 "0 0 0"
-"q18" 4 "0 0 0"
-"r18" 4 "0 0 0"
-"q19" 4 "0 0 0"
-"p19" 4 "0 0 0"
-"o19" 4 "0 0 0"
-"n19" 4 "0 0 0"
-"m19" 4 "0 0 0"
-"l19" 4 "0 0 0"
-"k19" 4 "0 0 0"
-"j19" 4 "0 0 0"
-"i19" 4 "0 0 0"
-"h19" 4 "0 0 0"
-"g19" 4 "0 0 0"
-"f19" 4 "0 0 0"
-"e19" 4 "0 0 0"
-"d19" 4 "0 0 0"
-"r19" 4 "0 0 0"
-"f15" 5 "0 0 0"
-"f13" 5 "0 0 0"
-"f12" 5 "0 0 0"
-"g12" 5 "0 0 0"
-"g9" 5 "0 0 0"
-"g10" 5 "0 0 0"
-"g11" 5 "0 0 0"
-"h9" 5 "0 0 0"
-"i9" 5 "0 0 0"
-"k9" 5 "0 0 0"
-"l9" 5 "0 0 0"
-"l13" 5 "0 0 0"
-"l12" 5 "0 0 0"
-"l10" 5 "0 0 0"
-"l11" 5 "0 0 0"
-"l15" 5 "0 0 0"
-"l14" 5 "0 0 0"
-"l16" 5 "0 0 0"
-"g15" 5 "0 0 0"
-"h15" 5 "0 0 0"
-"i15" 5 "0 0 0"
-"j15" 5 "0 0 0"
-"k15" 5 "0 0 0"
-"p9" 5 "0 0 0"
-"p10" 5 "0 0 0"
-"q10" 5 "0 0 0"
-"q8" 2 "0 0 0"
-"r8" 2 "0 0 0"
-"r9" 2 "0 0 0"
-"j9" 3 "0 0 0"
-"i13" 2 "0 0 0"
-"k13" 1 "0 -1 0"
-"n13" 3 "0 0 0"
-"n14" 3 "0 0 0"
-"n12" 3 "0 0 0"
+"a20" 4 0
+"a19" 4 0
+"f14" 2 0
+"a18" 4 0
+"a17" 4 0
+"a16" 4 0
+"a15" 4 0
+"f14" 3 0
+"b7" 4 0
+"b5" 4 0
+"b3" 4 0
+"s7" 4 0
+"s5" 4 0
+"s3" 4 0
+"b19" 4 0
+"s19" 4 0
+"a14" 4 0
+"a13" 4 0
+"a12" 4 0
+"a11" 4 0
+"a10" 4 0
+"a9" 4 0
+"a8" 4 0
+"a7" 4 0
+"a6" 4 0
+"a5" 4 0
+"a4" 4 0
+"a3" 4 0
+"a2" 4 0
+"a1" 4 0
+"b1" 4 0
+"c1" 4 0
+"d1" 4 0
+"e1" 4 0
+"f1" 4 0
+"g1" 4 0
+"h1" 4 0
+"i1" 4 0
+"j1" 4 0
+"k1" 4 0
+"l1" 4 0
+"m1" 4 0
+"n1" 4 0
+"o1" 4 0
+"p1" 4 0
+"q1" 4 0
+"r1" 4 0
+"s1" 4 0
+"t1" 4 0
+"t2" 4 0
+"t3" 4 0
+"t4" 4 0
+"t5" 4 0
+"t6" 4 0
+"t12" 4 0
+"t9" 4 0
+"t10" 4 0
+"t11" 4 0
+"t7" 4 0
+"t8" 4 0
+"t13" 4 0
+"t14" 4 0
+"t15" 4 0
+"t16" 4 0
+"t17" 4 0
+"t18" 4 0
+"t19" 4 0
+"t20" 4 0
+"r20" 4 0
+"q20" 4 0
+"p20" 4 0
+"s20" 4 0
+"o20" 4 0
+"n20" 4 0
+"m20" 4 0
+"l20" 4 0
+"k20" 4 0
+"j20" 4 0
+"i20" 4 0
+"h20" 4 0
+"g20" 4 0
+"f20" 4 0
+"e20" 4 0
+"d20" 4 0
+"c20" 4 0
+"b20" 4 0
+"c19" 4 0
+"b18" 4 0
+"b16" 4 0
+"c18" 4 0
+"b14" 4 0
+"b17" 7 0
+"b12" 4 0
+"b15" 7 0
+"b9" 7 0
+"b8" 4 0
+"b11" 7 0
+"b10" 4 0
+"b6" 4 0
+"b13" 7 0
+"b2" 4 0
+"b4" 4 0
+"s18" 4 0
+"s13" 7 0
+"s16" 4 0
+"s11" 7 0
+"s14" 4 0
+"s9" 7 0
+"s12" 4 0
+"s8" 4 0
+"s6" 4 0
+"s4" 4 0
+"s2" 4 0
+"s10" 4 0
+"s17" 7 0
+"s15" 7 0
+"c7" 4 0
+"c6" 4 0
+"d7" 4 0
+"f7" 4 0
+"e7" 4 0
+"g7" 4 0
+"h7" 4 0
+"i7" 4 0
+"j7" 4 0
+"k7" 4 0
+"l7" 4 0
+"m7" 4 0
+"n7" 4 0
+"o7" 4 0
+"p7" 4 0
+"q7" 4 0
+"r7" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"e4" 4 0
+"d4" 4 0
+"c4" 4 0
+"c3" 4 0
+"d3" 4 0
+"e3" 4 0
+"f3" 4 0
+"g3" 4 0
+"h3" 4 0
+"i3" 4 0
+"j3" 4 0
+"k3" 4 0
+"l3" 4 0
+"m3" 4 0
+"n3" 4 0
+"o3" 4 0
+"p3" 4 0
+"q3" 4 0
+"r3" 4 0
+"r2" 4 0
+"q2" 4 0
+"p2" 4 0
+"o2" 4 0
+"n2" 4 0
+"m2" 4 0
+"l2" 4 0
+"k2" 4 0
+"j2" 4 0
+"i2" 4 0
+"h2" 4 0
+"g2" 4 0
+"f2" 4 0
+"e2" 4 0
+"d2" 4 0
+"c2" 4 0
+"r4" 4 0
+"q4" 4 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"i5" 4 0
+"h5" 4 0
+"g5" 4 0
+"f5" 4 0
+"e5" 4 0
+"d5" 4 0
+"c5" 4 0
+"d6" 4 0
+"e6" 4 0
+"f6" 4 0
+"g6" 4 0
+"h6" 4 0
+"i6" 4 0
+"j6" 4 0
+"k6" 4 0
+"l6" 4 0
+"m6" 4 0
+"n6" 4 0
+"o6" 4 0
+"p6" 4 0
+"q6" 4 0
+"r6" 4 0
+"r5" 4 0
+"q5" 4 0
+"p5" 4 0
+"o5" 4 0
+"n5" 4 0
+"m5" 4 0
+"l5" 4 0
+"k5" 4 0
+"j5" 4 0
+"d18" 4 0
+"e18" 4 0
+"g18" 4 0
+"h18" 4 0
+"f18" 4 0
+"j18" 4 0
+"i18" 4 0
+"k18" 4 0
+"m18" 4 0
+"l18" 4 0
+"n18" 4 0
+"o18" 4 0
+"p18" 4 0
+"q18" 4 0
+"r18" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"e19" 4 0
+"d19" 4 0
+"r19" 4 0
+"f15" 5 0
+"f13" 5 0
+"f12" 5 0
+"g12" 5 0
+"g9" 5 0
+"g10" 5 0
+"g11" 5 0
+"h9" 5 0
+"i9" 5 0
+"k9" 5 0
+"l9" 5 0
+"l13" 5 0
+"l12" 5 0
+"l10" 5 0
+"l11" 5 0
+"l15" 5 0
+"l14" 5 0
+"l16" 5 0
+"g15" 5 0
+"h15" 5 0
+"i15" 5 0
+"j15" 5 0
+"k15" 5 0
+"p9" 5 0
+"p10" 5 0
+"q10" 5 0
+"q8" 2 0
+"r8" 2 0
+"r9" 2 0
+"j9" 3 0
+"i13" 2 0
+"k13" 1 1
+"n13" 3 0
+"n14" 3 0
+"n12" 3 0
index 596ae6ee710cad1d3cf7d7eae68279ce56de484e..19fea571a8daf73018028c85b5e5cc3c9e6253c6 100644 (file)
 // bulldozer storage "level4" last updated 16-11-2015 05:09:50
 nextlevel = "level5"
-"k10" 9 "0 -1 0"
-"k11" 9 "0 -1 0"
-"k13" 9 "0 -1 0"
-"j14" 9 "0 -1 0"
-"k14" 9 "0 -1 0"
-"k8" 5 "0 -1 0"
-"k9" 5 "0 -1 0"
-"k7" 5 "0 -1 0"
-"m8" 4 "0 -1 0"
-"l14" 9 "0 -1 0"
-"i14" 9 "0 -1 0"
-"m12" 1 "0 -1 0"
-"l11" 2 "0 -1 0"
-"m11" 2 "0 -1 0"
-"m10" 2 "0 -1 0"
-"l10" 2 "0 -1 0"
-"k12" 3 "0 -1 0"
-"m16" 5 "0 -1 0"
-"l16" 5 "0 -1 0"
-"k16" 5 "0 -1 0"
-"j4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"h11" 3 "0 -1 0"
-"f20" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"h16" 5 "0 -1 0"
-"f16" 4 "0 -1 0"
-"f15" 4 "0 -1 0"
-"f14" 5 "0 -1 0"
-"g14" 5 "0 -1 0"
-"e13" 4 "0 -1 0"
-"e12" 4 "0 -1 0"
-"e11" 4 "0 -1 0"
-"e10" 4 "0 -1 0"
-"e9" 4 "0 -1 0"
-"e8" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"f9" 5 "0 -1 0"
-"f7" 5 "0 -1 0"
-"f8" 5 "0 -1 0"
-"f10" 5 "0 -1 0"
-"g7" 5 "0 -1 0"
-"h12" 3 "0 -1 0"
-"m14" 3 "0 -1 0"
-"a4" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"t3" 4 "0 -1 0"
-"t2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"a1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"t1" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"k6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"t7" 4 "0 -1 0"
-"s7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"s8" 8 "0 -1 0"
-"p7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"j7" 5 "0 -1 0"
-"i7" 5 "0 -1 0"
-"l8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"q8" 4 "0 -1 0"
-"r8" 4 "0 -1 0"
-"s12" 8 "0 -1 0"
-"t8" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"q7" 8 "0 -1 0"
-"p9" 4 "0 -1 0"
-"o9" 4 "0 -1 0"
-"m9" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"o10" 4 "0 -1 0"
-"p10" 4 "0 -1 0"
-"q10" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"s14" 8 "0 -1 0"
-"t10" 4 "0 -1 0"
-"t11" 4 "0 -1 0"
-"s11" 4 "0 -1 0"
-"r11" 4 "0 -1 0"
-"q9" 8 "0 -1 0"
-"p11" 4 "0 -1 0"
-"o11" 4 "0 -1 0"
-"n10" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"o12" 4 "0 -1 0"
-"p12" 4 "0 -1 0"
-"q12" 4 "0 -1 0"
-"r12" 4 "0 -1 0"
-"t12" 4 "0 -1 0"
-"t13" 4 "0 -1 0"
-"s13" 4 "0 -1 0"
-"r13" 4 "0 -1 0"
-"q11" 8 "0 -1 0"
-"p13" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"l9" 5 "0 -1 0"
-"n12" 5 "0 -1 0"
-"o14" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
-"q14" 4 "0 -1 0"
-"r14" 4 "0 -1 0"
-"f12" 5 "0 -1 0"
-"t14" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"s15" 4 "0 -1 0"
-"r15" 4 "0 -1 0"
-"q15" 8 "0 -1 0"
-"p15" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"n13" 5 "0 -1 0"
-"n16" 5 "0 -1 0"
-"o16" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"q16" 4 "0 -1 0"
-"r16" 4 "0 -1 0"
-"q13" 8 "0 -1 0"
-"t16" 4 "0 -1 0"
-"t17" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"r17" 4 "0 -1 0"
-"q17" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"t18" 4 "0 -1 0"
-"t19" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"t20" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"a9" 8 "0 -1 0"
-"a8" 4 "0 -1 0"
-"b8" 4 "0 -1 0"
-"a7" 8 "0 -1 0"
-"d8" 4 "0 -1 0"
-"d9" 4 "0 -1 0"
-"c9" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"a13" 8 "0 -1 0"
-"a10" 4 "0 -1 0"
-"b10" 4 "0 -1 0"
-"a11" 8 "0 -1 0"
-"d10" 4 "0 -1 0"
-"d11" 4 "0 -1 0"
-"c11" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"a15" 8 "0 -1 0"
-"a12" 4 "0 -1 0"
-"b12" 4 "0 -1 0"
-"c10" 8 "0 -1 0"
-"d12" 4 "0 -1 0"
-"d13" 4 "0 -1 0"
-"c13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"s16" 8 "0 -1 0"
-"a14" 4 "0 -1 0"
-"b14" 4 "0 -1 0"
-"c12" 8 "0 -1 0"
-"d14" 4 "0 -1 0"
-"e14" 4 "0 -1 0"
-"e15" 4 "0 -1 0"
-"d15" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"s10" 8 "0 -1 0"
-"a16" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"c16" 8 "0 -1 0"
-"d16" 4 "0 -1 0"
-"e16" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"d17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"a17" 4 "0 -1 0"
-"a18" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"a19" 4 "0 -1 0"
-"a20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"f11" 5 "0 -1 0"
-"c14" 8 "0 -1 0"
-"c8" 8 "0 -1 0"
-"f13" 5 "0 -1 0"
-"g15" 5 "0 -1 0"
-"g16" 5 "0 -1 0"
-"i16" 5 "0 -1 0"
-"j16" 5 "0 -1 0"
-"n15" 5 "0 -1 0"
-"n14" 5 "0 -1 0"
-"h7" 5 "0 -1 0"
+"k10" 9 0
+"k11" 9 0
+"k13" 9 0
+"j14" 9 0
+"k14" 9 0
+"k8" 5 0
+"k9" 5 0
+"k7" 5 0
+"m8" 4 0
+"l14" 9 0
+"i14" 9 0
+"m12" 1 1
+"l11" 2 0
+"m11" 2 0
+"m10" 2 0
+"l10" 2 0
+"k12" 3 0
+"m16" 5 0
+"l16" 5 0
+"k16" 5 0
+"j4" 4 0
+"i4" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"e4" 4 0
+"d4" 4 0
+"c4" 4 0
+"b4" 4 0
+"h11" 3 0
+"f20" 4 0
+"f19" 4 0
+"f17" 4 0
+"f18" 4 0
+"h16" 5 0
+"f16" 4 0
+"f15" 4 0
+"f14" 5 0
+"g14" 5 0
+"e13" 4 0
+"e12" 4 0
+"e11" 4 0
+"e10" 4 0
+"e9" 4 0
+"e8" 4 0
+"e7" 4 0
+"f9" 5 0
+"f7" 5 0
+"f8" 5 0
+"f10" 5 0
+"g7" 5 0
+"h12" 3 0
+"m14" 3 0
+"a4" 4 0
+"a3" 4 0
+"b3" 4 0
+"c3" 4 0
+"d3" 4 0
+"e3" 4 0
+"f3" 4 0
+"g3" 4 0
+"h3" 4 0
+"i3" 4 0
+"j3" 4 0
+"k3" 4 0
+"l3" 4 0
+"m3" 4 0
+"n3" 4 0
+"o3" 4 0
+"p3" 4 0
+"q3" 4 0
+"r3" 4 0
+"s3" 4 0
+"t3" 4 0
+"t2" 4 0
+"s2" 4 0
+"r2" 4 0
+"q2" 4 0
+"p2" 4 0
+"o2" 4 0
+"n2" 4 0
+"m2" 4 0
+"l2" 4 0
+"k2" 4 0
+"j2" 4 0
+"i2" 4 0
+"h2" 4 0
+"g2" 4 0
+"f2" 4 0
+"e2" 4 0
+"d2" 4 0
+"c2" 4 0
+"b2" 4 0
+"a2" 4 0
+"a1" 4 0
+"b1" 4 0
+"c1" 4 0
+"d1" 4 0
+"e1" 4 0
+"f1" 4 0
+"g1" 4 0
+"h1" 4 0
+"i1" 4 0
+"j1" 4 0
+"k1" 4 0
+"l1" 4 0
+"m1" 4 0
+"n1" 4 0
+"o1" 4 0
+"p1" 4 0
+"q1" 4 0
+"r1" 4 0
+"s1" 4 0
+"t1" 4 0
+"t4" 4 0
+"s4" 4 0
+"r4" 4 0
+"q4" 4 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"k5" 4 0
+"j5" 4 0
+"i5" 4 0
+"h5" 4 0
+"g5" 4 0
+"f5" 4 0
+"e5" 4 0
+"d5" 4 0
+"c5" 4 0
+"b5" 4 0
+"a5" 4 0
+"a6" 4 0
+"b6" 4 0
+"c6" 4 0
+"d6" 4 0
+"e6" 4 0
+"f6" 4 0
+"g6" 4 0
+"h6" 4 0
+"i6" 4 0
+"j6" 4 0
+"k6" 4 0
+"l6" 4 0
+"m6" 4 0
+"n6" 4 0
+"o6" 4 0
+"p6" 4 0
+"q6" 4 0
+"r6" 4 0
+"s6" 4 0
+"t6" 4 0
+"t5" 4 0
+"s5" 4 0
+"r5" 4 0
+"q5" 4 0
+"p5" 4 0
+"o5" 4 0
+"n5" 4 0
+"m5" 4 0
+"l5" 4 0
+"t7" 4 0
+"s7" 4 0
+"r7" 4 0
+"s8" 8 0
+"p7" 4 0
+"o7" 4 0
+"n7" 4 0
+"m7" 4 0
+"l7" 4 0
+"j7" 5 0
+"i7" 5 0
+"l8" 4 0
+"n8" 4 0
+"o8" 4 0
+"p8" 4 0
+"q8" 4 0
+"r8" 4 0
+"s12" 8 0
+"t8" 4 0
+"t9" 4 0
+"s9" 4 0
+"r9" 4 0
+"q7" 8 0
+"p9" 4 0
+"o9" 4 0
+"m9" 5 0
+"n9" 5 0
+"o10" 4 0
+"p10" 4 0
+"q10" 4 0
+"r10" 4 0
+"s14" 8 0
+"t10" 4 0
+"t11" 4 0
+"s11" 4 0
+"r11" 4 0
+"q9" 8 0
+"p11" 4 0
+"o11" 4 0
+"n10" 5 0
+"n11" 5 0
+"o12" 4 0
+"p12" 4 0
+"q12" 4 0
+"r12" 4 0
+"t12" 4 0
+"t13" 4 0
+"s13" 4 0
+"r13" 4 0
+"q11" 8 0
+"p13" 4 0
+"o13" 4 0
+"l9" 5 0
+"n12" 5 0
+"o14" 4 0
+"p14" 4 0
+"q14" 4 0
+"r14" 4 0
+"f12" 5 0
+"t14" 4 0
+"t15" 4 0
+"s15" 4 0
+"r15" 4 0
+"q15" 8 0
+"p15" 4 0
+"o15" 4 0
+"n13" 5 0
+"n16" 5 0
+"o16" 4 0
+"p16" 4 0
+"q16" 4 0
+"r16" 4 0
+"q13" 8 0
+"t16" 4 0
+"t17" 4 0
+"s17" 4 0
+"r17" 4 0
+"q17" 4 0
+"p17" 4 0
+"o17" 4 0
+"n17" 4 0
+"m17" 4 0
+"l17" 4 0
+"k17" 4 0
+"j17" 4 0
+"i17" 4 0
+"h17" 4 0
+"g17" 4 0
+"g18" 4 0
+"h18" 4 0
+"i18" 4 0
+"j18" 4 0
+"k18" 4 0
+"l18" 4 0
+"m18" 4 0
+"n18" 4 0
+"o18" 4 0
+"p18" 4 0
+"q18" 4 0
+"r18" 4 0
+"s18" 4 0
+"t18" 4 0
+"t19" 4 0
+"s19" 4 0
+"r19" 4 0
+"q19" 4 0
+"p19" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
+"t20" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"a9" 8 0
+"a8" 4 0
+"b8" 4 0
+"a7" 8 0
+"d8" 4 0
+"d9" 4 0
+"c9" 4 0
+"b9" 4 0
+"a13" 8 0
+"a10" 4 0
+"b10" 4 0
+"a11" 8 0
+"d10" 4 0
+"d11" 4 0
+"c11" 4 0
+"b11" 4 0
+"a15" 8 0
+"a12" 4 0
+"b12" 4 0
+"c10" 8 0
+"d12" 4 0
+"d13" 4 0
+"c13" 4 0
+"b13" 4 0
+"s16" 8 0
+"a14" 4 0
+"b14" 4 0
+"c12" 8 0
+"d14" 4 0
+"e14" 4 0
+"e15" 4 0
+"d15" 4 0
+"c15" 4 0
+"b15" 4 0
+"s10" 8 0
+"a16" 4 0
+"b16" 4 0
+"c16" 8 0
+"d16" 4 0
+"e16" 4 0
+"e17" 4 0
+"d17" 4 0
+"c17" 4 0
+"b17" 4 0
+"a17" 4 0
+"a18" 4 0
+"b18" 4 0
+"c18" 4 0
+"d18" 4 0
+"e18" 4 0
+"e19" 4 0
+"d19" 4 0
+"c19" 4 0
+"b19" 4 0
+"a19" 4 0
+"a20" 4 0
+"b20" 4 0
+"c20" 4 0
+"d20" 4 0
+"e20" 4 0
+"f11" 5 0
+"c14" 8 0
+"c8" 8 0
+"f13" 5 0
+"g15" 5 0
+"g16" 5 0
+"i16" 5 0
+"j16" 5 0
+"n15" 5 0
+"n14" 5 0
+"h7" 5 0
index e5d7c2177d81dd246b1421def6e61bae5565714a..535811d542a0dcfa21456681e63ed0ed70e023f6 100644 (file)
 // bulldozer storage "level5" last updated 17-11-2015 21:42:14
 nextlevel = "level6"
-"a7" 4 "0 -1 0"
-"c18" 6 "0 -1 0"
-"a19" 4 "0 -1 0"
-"d17" 6 "0 -1 0"
-"c19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"q17" 6 "0 -1 0"
-"t19" 4 "0 -1 0"
-"e10" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"e16" 4 "0 -1 0"
-"e8" 4 "0 -1 0"
-"e9" 4 "0 -1 0"
-"e15" 4 "0 -1 0"
-"e13" 4 "0 -1 0"
-"e12" 4 "0 -1 0"
-"e11" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"e14" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"k6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"p7" 4 "0 -1 0"
-"p9" 4 "0 -1 0"
-"p10" 4 "0 -1 0"
-"p11" 4 "0 -1 0"
-"p12" 4 "0 -1 0"
-"p13" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"s7" 4 "0 -1 0"
-"t7" 4 "0 -1 0"
-"c13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"a13" 4 "0 -1 0"
-"a12" 4 "0 -1 0"
-"b12" 4 "0 -1 0"
-"c12" 4 "0 -1 0"
-"d12" 4 "0 -1 0"
-"d11" 4 "0 -1 0"
-"c11" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"a11" 4 "0 -1 0"
-"a10" 4 "0 -1 0"
-"b10" 4 "0 -1 0"
-"c10" 4 "0 -1 0"
-"d10" 4 "0 -1 0"
-"d9" 4 "0 -1 0"
-"c9" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"a9" 4 "0 -1 0"
-"a8" 4 "0 -1 0"
-"b8" 4 "0 -1 0"
-"c8" 4 "0 -1 0"
-"d8" 4 "0 -1 0"
-"d13" 4 "0 -1 0"
-"d14" 4 "0 -1 0"
-"c14" 4 "0 -1 0"
-"b14" 4 "0 -1 0"
-"a14" 4 "0 -1 0"
-"a15" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"d15" 4 "0 -1 0"
-"d16" 4 "0 -1 0"
-"c16" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"a16" 4 "0 -1 0"
-"a17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"a1" 6 "0 -1 0"
-"d18" 4 "0 -1 0"
-"b2" 6 "0 -1 0"
-"b18" 4 "0 -1 0"
-"a18" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"r18" 6 "0 -1 0"
-"s13" 4 "0 -1 0"
-"r13" 4 "0 -1 0"
-"q13" 4 "0 -1 0"
-"q12" 4 "0 -1 0"
-"r12" 4 "0 -1 0"
-"s12" 4 "0 -1 0"
-"t12" 4 "0 -1 0"
-"t11" 4 "0 -1 0"
-"s11" 4 "0 -1 0"
-"r11" 4 "0 -1 0"
-"q11" 4 "0 -1 0"
-"q10" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"s10" 4 "0 -1 0"
-"t10" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"q9" 4 "0 -1 0"
-"q8" 4 "0 -1 0"
-"r8" 4 "0 -1 0"
-"s8" 4 "0 -1 0"
-"t8" 4 "0 -1 0"
-"t13" 4 "0 -1 0"
-"t14" 4 "0 -1 0"
-"s14" 4 "0 -1 0"
-"r14" 4 "0 -1 0"
-"q14" 4 "0 -1 0"
-"q15" 4 "0 -1 0"
-"r15" 4 "0 -1 0"
-"s15" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"t16" 4 "0 -1 0"
-"s16" 4 "0 -1 0"
-"r16" 4 "0 -1 0"
-"q16" 4 "0 -1 0"
-"g15" 1 "0 -1 0"
-"r17" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"t17" 4 "0 -1 0"
-"t18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"m13" 2 "0 -1 0"
-"q18" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"t1" 6 "0 -1 0"
-"b3" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"d4" 6 "0 -1 0"
-"c2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"q4" 6 "0 -1 0"
-"t2" 4 "0 -1 0"
-"r3" 6 "0 -1 0"
-"s1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"c3" 6 "0 -1 0"
-"t3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"t20" 6 "0 -1 0"
-"q3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"s2" 6 "0 -1 0"
-"c4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"a4" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"s19" 6 "0 -1 0"
-"p4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"h7" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"p15" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"f16" 5 "0 -1 0"
-"f13" 5 "0 -1 0"
-"f15" 5 "0 -1 0"
-"f14" 5 "0 -1 0"
-"f12" 5 "0 -1 0"
-"g12" 5 "0 -1 0"
-"h12" 5 "0 -1 0"
-"h11" 5 "0 -1 0"
-"g9" 5 "0 -1 0"
-"g8" 5 "0 -1 0"
-"g11" 5 "0 -1 0"
-"g10" 5 "0 -1 0"
-"h8" 5 "0 -1 0"
-"i8" 5 "0 -1 0"
-"j8" 5 "0 -1 0"
-"k8" 5 "0 -1 0"
-"k9" 5 "0 -1 0"
-"k10" 5 "0 -1 0"
-"l9" 5 "0 -1 0"
-"m9" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"n12" 5 "0 -1 0"
-"n13" 5 "0 -1 0"
-"n14" 5 "0 -1 0"
-"m14" 5 "0 -1 0"
-"l14" 5 "0 -1 0"
-"j17" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"l13" 5 "0 -1 0"
-"l12" 5 "0 -1 0"
-"k12" 5 "0 -1 0"
-"j12" 5 "0 -1 0"
-"j13" 5 "0 -1 0"
-"j15" 5 "0 -1 0"
-"j14" 5 "0 -1 0"
-"j16" 5 "0 -1 0"
-"i16" 5 "0 -1 0"
-"h16" 5 "0 -1 0"
-"g16" 5 "0 -1 0"
-"f10" 4 "0 -1 0"
-"f9" 4 "0 -1 0"
-"f8" 4 "0 -1 0"
-"f11" 4 "0 -1 0"
-"o9" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"m8" 4 "0 -1 0"
-"l8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"m15" 4 "0 -1 0"
-"l15" 4 "0 -1 0"
-"k15" 4 "0 -1 0"
-"o10" 4 "0 -1 0"
-"k14" 4 "0 -1 0"
-"k13" 4 "0 -1 0"
-"k16" 4 "0 -1 0"
-"o11" 4 "0 -1 0"
-"o12" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"o14" 4 "0 -1 0"
-"l16" 4 "0 -1 0"
-"o16" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"m16" 4 "0 -1 0"
-"n16" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"n15" 4 "0 -1 0"
-"a20" 6 "0 -1 0"
-"b19" 6 "0 -1 0"
-"m12" 2 "0 -1 0"
-"m11" 2 "0 -1 0"
-"i14" 3 "0 -1 0"
-"h14" 3 "0 -1 0"
-"h13" 3 "0 -1 0"
-"i17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
+"a7" 4 0
+"c18" 6 0
+"a19" 4 0
+"d17" 6 0
+"c19" 4 0
+"d19" 4 0
+"e19" 4 0
+"f19" 4 0
+"g19" 4 0
+"h19" 4 0
+"i19" 4 0
+"j19" 4 0
+"k19" 4 0
+"l19" 4 0
+"m19" 4 0
+"n19" 4 0
+"o19" 4 0
+"p19" 4 0
+"q19" 4 0
+"r19" 4 0
+"q17" 6 0
+"t19" 4 0
+"e10" 4 0
+"e18" 4 0
+"p8" 4 0
+"e17" 4 0
+"e16" 4 0
+"e8" 4 0
+"e9" 4 0
+"e15" 4 0
+"e13" 4 0
+"e12" 4 0
+"e11" 4 0
+"e7" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"e14" 4 0
+"f17" 4 0
+"o7" 4 0
+"f6" 4 0
+"i6" 4 0
+"g6" 4 0
+"h6" 4 0
+"k6" 4 0
+"j6" 4 0
+"l6" 4 0
+"m6" 4 0
+"p7" 4 0
+"p9" 4 0
+"p10" 4 0
+"p11" 4 0
+"p12" 4 0
+"p13" 4 0
+"p14" 4 0
+"o15" 4 0
+"p16" 4 0
+"p17" 4 0
+"p18" 4 0
+"q7" 4 0
+"r7" 4 0
+"s7" 4 0
+"t7" 4 0
+"c13" 4 0
+"b13" 4 0
+"a13" 4 0
+"a12" 4 0
+"b12" 4 0
+"c12" 4 0
+"d12" 4 0
+"d11" 4 0
+"c11" 4 0
+"b11" 4 0
+"a11" 4 0
+"a10" 4 0
+"b10" 4 0
+"c10" 4 0
+"d10" 4 0
+"d9" 4 0
+"c9" 4 0
+"b9" 4 0
+"a9" 4 0
+"a8" 4 0
+"b8" 4 0
+"c8" 4 0
+"d8" 4 0
+"d13" 4 0
+"d14" 4 0
+"c14" 4 0
+"b14" 4 0
+"a14" 4 0
+"a15" 4 0
+"b15" 4 0
+"c15" 4 0
+"d15" 4 0
+"d16" 4 0
+"c16" 4 0
+"b16" 4 0
+"a16" 4 0
+"a17" 4 0
+"b17" 4 0
+"c17" 4 0
+"a1" 6 0
+"d18" 4 0
+"b2" 6 0
+"b18" 4 0
+"a18" 4 0
+"f20" 4 0
+"e20" 4 0
+"d20" 4 0
+"c20" 4 0
+"b20" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"p20" 4 0
+"q20" 4 0
+"r20" 4 0
+"s20" 4 0
+"r18" 6 0
+"s13" 4 0
+"r13" 4 0
+"q13" 4 0
+"q12" 4 0
+"r12" 4 0
+"s12" 4 0
+"t12" 4 0
+"t11" 4 0
+"s11" 4 0
+"r11" 4 0
+"q11" 4 0
+"q10" 4 0
+"r10" 4 0
+"s10" 4 0
+"t10" 4 0
+"t9" 4 0
+"s9" 4 0
+"r9" 4 0
+"q9" 4 0
+"q8" 4 0
+"r8" 4 0
+"s8" 4 0
+"t8" 4 0
+"t13" 4 0
+"t14" 4 0
+"s14" 4 0
+"r14" 4 0
+"q14" 4 0
+"q15" 4 0
+"r15" 4 0
+"s15" 4 0
+"t15" 4 0
+"t16" 4 0
+"s16" 4 0
+"r16" 4 0
+"q16" 4 0
+"g15" 1 1
+"r17" 4 0
+"s17" 4 0
+"t17" 4 0
+"t18" 4 0
+"s18" 4 0
+"m13" 2 0
+"q18" 4 0
+"g3" 4 0
+"f3" 4 0
+"e3" 4 0
+"d3" 4 0
+"t1" 6 0
+"b3" 4 0
+"a3" 4 0
+"a2" 4 0
+"d4" 6 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"k2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"q4" 6 0
+"t2" 4 0
+"r3" 6 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"c3" 6 0
+"t3" 4 0
+"s3" 4 0
+"t20" 6 0
+"q3" 4 0
+"p3" 4 0
+"o3" 4 0
+"n3" 4 0
+"m3" 4 0
+"l3" 4 0
+"k3" 4 0
+"j3" 4 0
+"i3" 4 0
+"h3" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"e4" 4 0
+"s2" 6 0
+"c4" 4 0
+"b4" 4 0
+"a4" 4 0
+"a5" 4 0
+"b5" 4 0
+"c5" 4 0
+"d5" 4 0
+"e5" 4 0
+"f5" 4 0
+"g5" 4 0
+"h5" 4 0
+"i5" 4 0
+"j5" 4 0
+"k5" 4 0
+"l5" 4 0
+"m5" 4 0
+"n5" 4 0
+"o5" 4 0
+"p5" 4 0
+"q5" 4 0
+"r5" 4 0
+"s5" 4 0
+"t5" 4 0
+"t4" 4 0
+"s4" 4 0
+"r4" 4 0
+"s19" 6 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"t6" 4 0
+"s6" 4 0
+"r6" 4 0
+"q6" 4 0
+"p6" 4 0
+"o6" 4 0
+"n6" 4 0
+"n7" 4 0
+"l7" 4 0
+"j7" 4 0
+"h7" 4 0
+"f7" 4 0
+"g7" 4 0
+"i7" 4 0
+"k7" 4 0
+"e6" 4 0
+"d6" 4 0
+"c6" 4 0
+"b6" 4 0
+"a6" 4 0
+"p15" 4 0
+"m7" 4 0
+"f16" 5 0
+"f13" 5 0
+"f15" 5 0
+"f14" 5 0
+"f12" 5 0
+"g12" 5 0
+"h12" 5 0
+"h11" 5 0
+"g9" 5 0
+"g8" 5 0
+"g11" 5 0
+"g10" 5 0
+"h8" 5 0
+"i8" 5 0
+"j8" 5 0
+"k8" 5 0
+"k9" 5 0
+"k10" 5 0
+"l9" 5 0
+"m9" 5 0
+"n9" 5 0
+"n10" 5 0
+"n11" 5 0
+"n12" 5 0
+"n13" 5 0
+"n14" 5 0
+"m14" 5 0
+"l14" 5 0
+"j17" 4 0
+"j18" 4 0
+"i18" 4 0
+"h18" 4 0
+"g18" 4 0
+"f18" 4 0
+"l13" 5 0
+"l12" 5 0
+"k12" 5 0
+"j12" 5 0
+"j13" 5 0
+"j15" 5 0
+"j14" 5 0
+"j16" 5 0
+"i16" 5 0
+"h16" 5 0
+"g16" 5 0
+"f10" 4 0
+"f9" 4 0
+"f8" 4 0
+"f11" 4 0
+"o9" 4 0
+"o8" 4 0
+"m8" 4 0
+"l8" 4 0
+"n8" 4 0
+"m15" 4 0
+"l15" 4 0
+"k15" 4 0
+"o10" 4 0
+"k14" 4 0
+"k13" 4 0
+"k16" 4 0
+"o11" 4 0
+"o12" 4 0
+"o13" 4 0
+"o14" 4 0
+"l16" 4 0
+"o16" 4 0
+"o17" 4 0
+"n17" 4 0
+"m17" 4 0
+"l17" 4 0
+"k17" 4 0
+"m16" 4 0
+"n16" 4 0
+"k18" 4 0
+"l18" 4 0
+"m18" 4 0
+"n18" 4 0
+"o18" 4 0
+"n15" 4 0
+"a20" 6 0
+"b19" 6 0
+"m12" 2 0
+"m11" 2 0
+"i14" 3 0
+"h14" 3 0
+"h13" 3 0
+"i17" 4 0
+"h17" 4 0
+"g17" 4 0
index 44a8137f4b00630343ada72d38b9ed465eca00cf..a849a97ad534d1297f9f163581048f41e83ba674 100644 (file)
 // bulldozer storage "level6" last updated 06-12-2015 16:11:35
 nextlevel = "level7"
-"g13" 5 "0 -1 0"
-"f13" 5 "0 -1 0"
-"f12" 5 "0 -1 0"
-"f11" 5 "0 -1 0"
-"f10" 5 "0 -1 0"
-"g10" 5 "0 -1 0"
-"i9" 5 "0 -1 0"
-"g9" 5 "0 -1 0"
-"h9" 5 "0 -1 0"
-"j9" 5 "0 -1 0"
-"k9" 5 "0 -1 0"
-"l9" 5 "0 -1 0"
-"m9" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"o12" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"o11" 5 "0 -1 0"
-"n14" 5 "0 -1 0"
-"o13" 5 "0 -1 0"
-"o14" 5 "0 -1 0"
-"m14" 5 "0 -1 0"
-"l15" 5 "0 -1 0"
-"m15" 5 "0 -1 0"
-"g14" 5 "0 -1 0"
-"g15" 5 "0 -1 0"
-"h15" 5 "0 -1 0"
-"i15" 5 "0 -1 0"
-"j15" 5 "0 -1 0"
-"k15" 5 "0 -1 0"
-"i13" 5 "0 -1 0"
-"j13" 5 "0 -1 0"
-"k13" 5 "0 -1 0"
-"j10" 5 "0 -1 0"
-"j11" 5 "0 -1 0"
-"h10" 2 "0 -1 0"
-"i10" 2 "0 -1 0"
-"h11" 2 "0 -1 0"
-"i11" 2 "0 -1 0"
-"g12" 1 "0 -1 0"
-"h13" 3 "0 -1 0"
-"j12" 3 "0 -1 0"
-"l11" 3 "0 -1 0"
-"m12" 3 "0 -1 0"
-"b2" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"f5" 6 "0 -1 0"
-"e5" 6 "0 -1 0"
-"d5" 6 "0 -1 0"
-"c5" 6 "0 -1 0"
-"b5" 6 "0 -1 0"
-"a5" 6 "0 -1 0"
-"a4" 6 "0 -1 0"
-"b4" 6 "0 -1 0"
-"c4" 6 "0 -1 0"
-"d4" 6 "0 -1 0"
-"e4" 6 "0 -1 0"
-"f4" 6 "0 -1 0"
-"g4" 6 "0 -1 0"
-"h4" 6 "0 -1 0"
-"i4" 6 "0 -1 0"
-"j4" 6 "0 -1 0"
-"k4" 6 "0 -1 0"
-"l4" 6 "0 -1 0"
-"m4" 6 "0 -1 0"
-"n4" 6 "0 -1 0"
-"o4" 6 "0 -1 0"
-"p4" 6 "0 -1 0"
-"q4" 6 "0 -1 0"
-"r4" 6 "0 -1 0"
-"s4" 6 "0 -1 0"
-"t4" 6 "0 -1 0"
-"t3" 6 "0 -1 0"
-"t2" 6 "0 -1 0"
-"t1" 6 "0 -1 0"
-"s1" 6 "0 -1 0"
-"r1" 6 "0 -1 0"
-"q1" 6 "0 -1 0"
-"p1" 6 "0 -1 0"
-"o1" 6 "0 -1 0"
-"n1" 6 "0 -1 0"
-"m1" 6 "0 -1 0"
-"l1" 6 "0 -1 0"
-"k1" 6 "0 -1 0"
-"j1" 6 "0 -1 0"
-"i1" 6 "0 -1 0"
-"h1" 6 "0 -1 0"
-"g1" 6 "0 -1 0"
-"f1" 6 "0 -1 0"
-"e1" 6 "0 -1 0"
-"d1" 6 "0 -1 0"
-"c1" 6 "0 -1 0"
-"b1" 6 "0 -1 0"
-"a1" 6 "0 -1 0"
-"a2" 6 "0 -1 0"
-"a3" 6 "0 -1 0"
-"d2" 6 "0 -1 0"
-"e2" 6 "0 -1 0"
-"f2" 6 "0 -1 0"
-"g2" 6 "0 -1 0"
-"h2" 6 "0 -1 0"
-"i2" 6 "0 -1 0"
-"j2" 6 "0 -1 0"
-"k2" 6 "0 -1 0"
-"l2" 6 "0 -1 0"
-"m2" 6 "0 -1 0"
-"n2" 6 "0 -1 0"
-"o2" 6 "0 -1 0"
-"p2" 6 "0 -1 0"
-"q2" 6 "0 -1 0"
-"q3" 6 "0 -1 0"
-"p3" 6 "0 -1 0"
-"o3" 6 "0 -1 0"
-"n3" 6 "0 -1 0"
-"m3" 6 "0 -1 0"
-"l3" 6 "0 -1 0"
-"k3" 6 "0 -1 0"
-"j3" 6 "0 -1 0"
-"i3" 6 "0 -1 0"
-"h3" 6 "0 -1 0"
-"g3" 6 "0 -1 0"
-"f3" 6 "0 -1 0"
-"e3" 6 "0 -1 0"
-"d3" 6 "0 -1 0"
-"t5" 6 "0 -1 0"
-"s5" 6 "0 -1 0"
-"r5" 6 "0 -1 0"
-"q5" 6 "0 -1 0"
-"p5" 6 "0 -1 0"
-"o5" 6 "0 -1 0"
-"n5" 6 "0 -1 0"
-"m5" 6 "0 -1 0"
-"l5" 6 "0 -1 0"
-"k5" 6 "0 -1 0"
-"j5" 6 "0 -1 0"
-"i5" 6 "0 -1 0"
-"h5" 6 "0 -1 0"
-"g5" 6 "0 -1 0"
-"g6" 6 "0 -1 0"
-"f6" 6 "0 -1 0"
-"e6" 6 "0 -1 0"
-"d6" 6 "0 -1 0"
-"c6" 6 "0 -1 0"
-"b6" 6 "0 -1 0"
-"a6" 6 "0 -1 0"
-"a7" 6 "0 -1 0"
-"b7" 6 "0 -1 0"
-"c7" 6 "0 -1 0"
-"d7" 6 "0 -1 0"
-"e7" 6 "0 -1 0"
-"f7" 6 "0 -1 0"
-"g7" 6 "0 -1 0"
-"h7" 6 "0 -1 0"
-"i7" 6 "0 -1 0"
-"j7" 6 "0 -1 0"
-"k7" 6 "0 -1 0"
-"l7" 6 "0 -1 0"
-"m7" 6 "0 -1 0"
-"n7" 6 "0 -1 0"
-"o7" 6 "0 -1 0"
-"p7" 6 "0 -1 0"
-"q7" 6 "0 -1 0"
-"r7" 6 "0 -1 0"
-"s7" 6 "0 -1 0"
-"t7" 6 "0 -1 0"
-"t6" 6 "0 -1 0"
-"s6" 6 "0 -1 0"
-"r6" 6 "0 -1 0"
-"q6" 6 "0 -1 0"
-"p6" 6 "0 -1 0"
-"o6" 6 "0 -1 0"
-"n6" 6 "0 -1 0"
-"m6" 6 "0 -1 0"
-"l6" 6 "0 -1 0"
-"k6" 6 "0 -1 0"
-"j6" 6 "0 -1 0"
-"i6" 6 "0 -1 0"
-"h6" 6 "0 -1 0"
-"t8" 6 "0 -1 0"
-"s8" 6 "0 -1 0"
-"r8" 6 "0 -1 0"
-"q8" 6 "0 -1 0"
-"p8" 6 "0 -1 0"
-"o8" 6 "0 -1 0"
-"n8" 6 "0 -1 0"
-"m8" 6 "0 -1 0"
-"l8" 6 "0 -1 0"
-"k8" 6 "0 -1 0"
-"j8" 6 "0 -1 0"
-"i8" 6 "0 -1 0"
-"h8" 6 "0 -1 0"
-"g8" 6 "0 -1 0"
-"f8" 6 "0 -1 0"
-"e8" 6 "0 -1 0"
-"d8" 6 "0 -1 0"
-"c8" 6 "0 -1 0"
-"b8" 6 "0 -1 0"
-"a8" 6 "0 -1 0"
-"a9" 6 "0 -1 0"
-"b9" 6 "0 -1 0"
-"c9" 6 "0 -1 0"
-"d9" 6 "0 -1 0"
-"e9" 6 "0 -1 0"
-"f9" 6 "0 -1 0"
-"e10" 6 "0 -1 0"
-"d10" 6 "0 -1 0"
-"c10" 6 "0 -1 0"
-"b10" 6 "0 -1 0"
-"a10" 6 "0 -1 0"
-"a11" 6 "0 -1 0"
-"b11" 6 "0 -1 0"
-"c11" 6 "0 -1 0"
-"d11" 6 "0 -1 0"
-"e11" 6 "0 -1 0"
-"e12" 6 "0 -1 0"
-"d12" 6 "0 -1 0"
-"c12" 6 "0 -1 0"
-"b12" 6 "0 -1 0"
-"a12" 6 "0 -1 0"
-"a13" 6 "0 -1 0"
-"b13" 6 "0 -1 0"
-"c13" 6 "0 -1 0"
-"d13" 6 "0 -1 0"
-"e13" 6 "0 -1 0"
-"e14" 6 "0 -1 0"
-"d14" 6 "0 -1 0"
-"c14" 6 "0 -1 0"
-"b14" 6 "0 -1 0"
-"a14" 6 "0 -1 0"
-"a15" 6 "0 -1 0"
-"b15" 6 "0 -1 0"
-"c15" 6 "0 -1 0"
-"d15" 6 "0 -1 0"
-"e15" 6 "0 -1 0"
-"f15" 6 "0 -1 0"
-"f14" 6 "0 -1 0"
-"f16" 6 "0 -1 0"
-"e16" 6 "0 -1 0"
-"d16" 6 "0 -1 0"
-"c16" 6 "0 -1 0"
-"b16" 6 "0 -1 0"
-"a16" 6 "0 -1 0"
-"a17" 6 "0 -1 0"
-"b17" 6 "0 -1 0"
-"c17" 6 "0 -1 0"
-"d17" 6 "0 -1 0"
-"e17" 6 "0 -1 0"
-"f17" 6 "0 -1 0"
-"g17" 6 "0 -1 0"
-"h17" 6 "0 -1 0"
-"i17" 6 "0 -1 0"
-"j17" 6 "0 -1 0"
-"k17" 6 "0 -1 0"
-"l17" 6 "0 -1 0"
-"m17" 6 "0 -1 0"
-"n17" 6 "0 -1 0"
-"o17" 6 "0 -1 0"
-"p17" 6 "0 -1 0"
-"q17" 6 "0 -1 0"
-"r17" 6 "0 -1 0"
-"s17" 6 "0 -1 0"
-"t17" 6 "0 -1 0"
-"t16" 6 "0 -1 0"
-"s16" 6 "0 -1 0"
-"r16" 6 "0 -1 0"
-"q16" 6 "0 -1 0"
-"p16" 6 "0 -1 0"
-"o16" 6 "0 -1 0"
-"n16" 6 "0 -1 0"
-"m16" 6 "0 -1 0"
-"l16" 6 "0 -1 0"
-"k16" 6 "0 -1 0"
-"j16" 6 "0 -1 0"
-"i16" 6 "0 -1 0"
-"h16" 6 "0 -1 0"
-"g16" 6 "0 -1 0"
-"n15" 6 "0 -1 0"
-"o15" 6 "0 -1 0"
-"p15" 6 "0 -1 0"
-"q15" 6 "0 -1 0"
-"r15" 6 "0 -1 0"
-"s15" 6 "0 -1 0"
-"t15" 6 "0 -1 0"
-"t14" 6 "0 -1 0"
-"s14" 6 "0 -1 0"
-"r14" 6 "0 -1 0"
-"q14" 6 "0 -1 0"
-"p14" 6 "0 -1 0"
-"p13" 6 "0 -1 0"
-"q13" 6 "0 -1 0"
-"r13" 6 "0 -1 0"
-"s13" 6 "0 -1 0"
-"t13" 6 "0 -1 0"
-"t12" 6 "0 -1 0"
-"s12" 6 "0 -1 0"
-"r12" 6 "0 -1 0"
-"q12" 6 "0 -1 0"
-"p12" 6 "0 -1 0"
-"p11" 6 "0 -1 0"
-"q11" 6 "0 -1 0"
-"r11" 6 "0 -1 0"
-"s11" 6 "0 -1 0"
-"t11" 6 "0 -1 0"
-"t10" 6 "0 -1 0"
-"s10" 6 "0 -1 0"
-"r10" 6 "0 -1 0"
-"q10" 6 "0 -1 0"
-"p10" 6 "0 -1 0"
-"o10" 6 "0 -1 0"
-"o9" 6 "0 -1 0"
-"p9" 6 "0 -1 0"
-"q9" 6 "0 -1 0"
-"r9" 6 "0 -1 0"
-"s9" 6 "0 -1 0"
-"t9" 6 "0 -1 0"
-"t18" 6 "0 -1 0"
-"t19" 6 "0 -1 0"
-"t20" 6 "0 -1 0"
-"s20" 6 "0 -1 0"
-"r20" 6 "0 -1 0"
-"q20" 6 "0 -1 0"
-"p20" 6 "0 -1 0"
-"o20" 6 "0 -1 0"
-"n20" 6 "0 -1 0"
-"m20" 6 "0 -1 0"
-"l20" 6 "0 -1 0"
-"k20" 6 "0 -1 0"
-"j20" 6 "0 -1 0"
-"i20" 6 "0 -1 0"
-"h20" 6 "0 -1 0"
-"g20" 6 "0 -1 0"
-"f20" 6 "0 -1 0"
-"e20" 6 "0 -1 0"
-"d20" 6 "0 -1 0"
-"c20" 6 "0 -1 0"
-"b20" 6 "0 -1 0"
-"a20" 6 "0 -1 0"
-"a19" 6 "0 -1 0"
-"a18" 6 "0 -1 0"
-"d19" 6 "0 -1 0"
-"e19" 6 "0 -1 0"
-"f19" 6 "0 -1 0"
-"g19" 6 "0 -1 0"
-"h19" 6 "0 -1 0"
-"i19" 6 "0 -1 0"
-"j19" 6 "0 -1 0"
-"k19" 6 "0 -1 0"
-"l19" 6 "0 -1 0"
-"m19" 6 "0 -1 0"
-"n19" 6 "0 -1 0"
-"o19" 6 "0 -1 0"
-"p19" 6 "0 -1 0"
-"q19" 6 "0 -1 0"
-"q18" 6 "0 -1 0"
-"p18" 6 "0 -1 0"
-"o18" 6 "0 -1 0"
-"n18" 6 "0 -1 0"
-"m18" 6 "0 -1 0"
-"l18" 6 "0 -1 0"
-"k18" 6 "0 -1 0"
-"j18" 6 "0 -1 0"
-"i18" 6 "0 -1 0"
-"h18" 6 "0 -1 0"
-"g18" 6 "0 -1 0"
-"f18" 6 "0 -1 0"
-"e18" 6 "0 -1 0"
-"d18" 6 "0 -1 0"
+"g13" 5 0
+"f13" 5 0
+"f12" 5 0
+"f11" 5 0
+"f10" 5 0
+"g10" 5 0
+"i9" 5 0
+"g9" 5 0
+"h9" 5 0
+"j9" 5 0
+"k9" 5 0
+"l9" 5 0
+"m9" 5 0
+"n9" 5 0
+"n10" 5 0
+"o12" 5 0
+"n11" 5 0
+"o11" 5 0
+"n14" 5 0
+"o13" 5 0
+"o14" 5 0
+"m14" 5 0
+"l15" 5 0
+"m15" 5 0
+"g14" 5 0
+"g15" 5 0
+"h15" 5 0
+"i15" 5 0
+"j15" 5 0
+"k15" 5 0
+"i13" 5 0
+"j13" 5 0
+"k13" 5 0
+"j10" 5 0
+"j11" 5 0
+"h10" 2 0
+"i10" 2 0
+"h11" 2 0
+"i11" 2 0
+"g12" 1 1
+"h13" 3 0
+"j12" 3 0
+"l11" 3 0
+"m12" 3 0
+"b2" 4 0
+"b3" 4 0
+"c3" 4 0
+"c2" 4 0
+"b19" 4 0
+"b18" 4 0
+"c18" 4 0
+"c19" 4 0
+"r19" 4 0
+"r18" 4 0
+"s18" 4 0
+"s19" 4 0
+"r2" 4 0
+"r3" 4 0
+"s3" 4 0
+"s2" 4 0
+"f5" 6 0
+"e5" 6 0
+"d5" 6 0
+"c5" 6 0
+"b5" 6 0
+"a5" 6 0
+"a4" 6 0
+"b4" 6 0
+"c4" 6 0
+"d4" 6 0
+"e4" 6 0
+"f4" 6 0
+"g4" 6 0
+"h4" 6 0
+"i4" 6 0
+"j4" 6 0
+"k4" 6 0
+"l4" 6 0
+"m4" 6 0
+"n4" 6 0
+"o4" 6 0
+"p4" 6 0
+"q4" 6 0
+"r4" 6 0
+"s4" 6 0
+"t4" 6 0
+"t3" 6 0
+"t2" 6 0
+"t1" 6 0
+"s1" 6 0
+"r1" 6 0
+"q1" 6 0
+"p1" 6 0
+"o1" 6 0
+"n1" 6 0
+"m1" 6 0
+"l1" 6 0
+"k1" 6 0
+"j1" 6 0
+"i1" 6 0
+"h1" 6 0
+"g1" 6 0
+"f1" 6 0
+"e1" 6 0
+"d1" 6 0
+"c1" 6 0
+"b1" 6 0
+"a1" 6 0
+"a2" 6 0
+"a3" 6 0
+"d2" 6 0
+"e2" 6 0
+"f2" 6 0
+"g2" 6 0
+"h2" 6 0
+"i2" 6 0
+"j2" 6 0
+"k2" 6 0
+"l2" 6 0
+"m2" 6 0
+"n2" 6 0
+"o2" 6 0
+"p2" 6 0
+"q2" 6 0
+"q3" 6 0
+"p3" 6 0
+"o3" 6 0
+"n3" 6 0
+"m3" 6 0
+"l3" 6 0
+"k3" 6 0
+"j3" 6 0
+"i3" 6 0
+"h3" 6 0
+"g3" 6 0
+"f3" 6 0
+"e3" 6 0
+"d3" 6 0
+"t5" 6 0
+"s5" 6 0
+"r5" 6 0
+"q5" 6 0
+"p5" 6 0
+"o5" 6 0
+"n5" 6 0
+"m5" 6 0
+"l5" 6 0
+"k5" 6 0
+"j5" 6 0
+"i5" 6 0
+"h5" 6 0
+"g5" 6 0
+"g6" 6 0
+"f6" 6 0
+"e6" 6 0
+"d6" 6 0
+"c6" 6 0
+"b6" 6 0
+"a6" 6 0
+"a7" 6 0
+"b7" 6 0
+"c7" 6 0
+"d7" 6 0
+"e7" 6 0
+"f7" 6 0
+"g7" 6 0
+"h7" 6 0
+"i7" 6 0
+"j7" 6 0
+"k7" 6 0
+"l7" 6 0
+"m7" 6 0
+"n7" 6 0
+"o7" 6 0
+"p7" 6 0
+"q7" 6 0
+"r7" 6 0
+"s7" 6 0
+"t7" 6 0
+"t6" 6 0
+"s6" 6 0
+"r6" 6 0
+"q6" 6 0
+"p6" 6 0
+"o6" 6 0
+"n6" 6 0
+"m6" 6 0
+"l6" 6 0
+"k6" 6 0
+"j6" 6 0
+"i6" 6 0
+"h6" 6 0
+"t8" 6 0
+"s8" 6 0
+"r8" 6 0
+"q8" 6 0
+"p8" 6 0
+"o8" 6 0
+"n8" 6 0
+"m8" 6 0
+"l8" 6 0
+"k8" 6 0
+"j8" 6 0
+"i8" 6 0
+"h8" 6 0
+"g8" 6 0
+"f8" 6 0
+"e8" 6 0
+"d8" 6 0
+"c8" 6 0
+"b8" 6 0
+"a8" 6 0
+"a9" 6 0
+"b9" 6 0
+"c9" 6 0
+"d9" 6 0
+"e9" 6 0
+"f9" 6 0
+"e10" 6 0
+"d10" 6 0
+"c10" 6 0
+"b10" 6 0
+"a10" 6 0
+"a11" 6 0
+"b11" 6 0
+"c11" 6 0
+"d11" 6 0
+"e11" 6 0
+"e12" 6 0
+"d12" 6 0
+"c12" 6 0
+"b12" 6 0
+"a12" 6 0
+"a13" 6 0
+"b13" 6 0
+"c13" 6 0
+"d13" 6 0
+"e13" 6 0
+"e14" 6 0
+"d14" 6 0
+"c14" 6 0
+"b14" 6 0
+"a14" 6 0
+"a15" 6 0
+"b15" 6 0
+"c15" 6 0
+"d15" 6 0
+"e15" 6 0
+"f15" 6 0
+"f14" 6 0
+"f16" 6 0
+"e16" 6 0
+"d16" 6 0
+"c16" 6 0
+"b16" 6 0
+"a16" 6 0
+"a17" 6 0
+"b17" 6 0
+"c17" 6 0
+"d17" 6 0
+"e17" 6 0
+"f17" 6 0
+"g17" 6 0
+"h17" 6 0
+"i17" 6 0
+"j17" 6 0
+"k17" 6 0
+"l17" 6 0
+"m17" 6 0
+"n17" 6 0
+"o17" 6 0
+"p17" 6 0
+"q17" 6 0
+"r17" 6 0
+"s17" 6 0
+"t17" 6 0
+"t16" 6 0
+"s16" 6 0
+"r16" 6 0
+"q16" 6 0
+"p16" 6 0
+"o16" 6 0
+"n16" 6 0
+"m16" 6 0
+"l16" 6 0
+"k16" 6 0
+"j16" 6 0
+"i16" 6 0
+"h16" 6 0
+"g16" 6 0
+"n15" 6 0
+"o15" 6 0
+"p15" 6 0
+"q15" 6 0
+"r15" 6 0
+"s15" 6 0
+"t15" 6 0
+"t14" 6 0
+"s14" 6 0
+"r14" 6 0
+"q14" 6 0
+"p14" 6 0
+"p13" 6 0
+"q13" 6 0
+"r13" 6 0
+"s13" 6 0
+"t13" 6 0
+"t12" 6 0
+"s12" 6 0
+"r12" 6 0
+"q12" 6 0
+"p12" 6 0
+"p11" 6 0
+"q11" 6 0
+"r11" 6 0
+"s11" 6 0
+"t11" 6 0
+"t10" 6 0
+"s10" 6 0
+"r10" 6 0
+"q10" 6 0
+"p10" 6 0
+"o10" 6 0
+"o9" 6 0
+"p9" 6 0
+"q9" 6 0
+"r9" 6 0
+"s9" 6 0
+"t9" 6 0
+"t18" 6 0
+"t19" 6 0
+"t20" 6 0
+"s20" 6 0
+"r20" 6 0
+"q20" 6 0
+"p20" 6 0
+"o20" 6 0
+"n20" 6 0
+"m20" 6 0
+"l20" 6 0
+"k20" 6 0
+"j20" 6 0
+"i20" 6 0
+"h20" 6 0
+"g20" 6 0
+"f20" 6 0
+"e20" 6 0
+"d20" 6 0
+"c20" 6 0
+"b20" 6 0
+"a20" 6 0
+"a19" 6 0
+"a18" 6 0
+"d19" 6 0
+"e19" 6 0
+"f19" 6 0
+"g19" 6 0
+"h19" 6 0
+"i19" 6 0
+"j19" 6 0
+"k19" 6 0
+"l19" 6 0
+"m19" 6 0
+"n19" 6 0
+"o19" 6 0
+"p19" 6 0
+"q19" 6 0
+"q18" 6 0
+"p18" 6 0
+"o18" 6 0
+"n18" 6 0
+"m18" 6 0
+"l18" 6 0
+"k18" 6 0
+"j18" 6 0
+"i18" 6 0
+"h18" 6 0
+"g18" 6 0
+"f18" 6 0
+"e18" 6 0
+"d18" 6 0
index 5f351c629c3823a9b6259d2dabd8bb688e593dd2..a6473ae0ea2354bc712af4c9445219e60e549b5f 100644 (file)
 // bulldozer storage "level7" last updated 06-12-2015 16:19:03
 nextlevel = "level8"
-"g10" 5 "0 -1 0"
-"g9" 5 "0 -1 0"
-"g8" 5 "0 -1 0"
-"g11" 5 "0 -1 0"
-"g12" 5 "0 -1 0"
-"h8" 5 "0 -1 0"
-"i8" 5 "0 -1 0"
-"j8" 5 "0 -1 0"
-"l8" 5 "0 -1 0"
-"k8" 5 "0 -1 0"
-"m8" 5 "0 -1 0"
-"n8" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"n10" 5 "0 -1 0"
-"n11" 5 "0 -1 0"
-"n12" 5 "0 -1 0"
-"m12" 5 "0 -1 0"
-"m13" 5 "0 -1 0"
-"m14" 5 "0 -1 0"
-"h12" 5 "0 -1 0"
-"h13" 5 "0 -1 0"
-"h14" 5 "0 -1 0"
-"h15" 5 "0 -1 0"
-"i15" 5 "0 -1 0"
-"j15" 5 "0 -1 0"
-"k15" 5 "0 -1 0"
-"k14" 5 "0 -1 0"
-"l14" 5 "0 -1 0"
-"k12" 5 "0 -1 0"
-"k11" 5 "0 -1 0"
-"i12" 5 "0 -1 0"
-"i11" 5 "0 -1 0"
-"l10" 5 "0 -1 0"
-"j14" 1 "0 -1 0"
-"j13" 3 "0 -1 0"
-"i10" 3 "0 -1 0"
-"l9" 3 "0 -1 0"
-"h11" 2 "0 -1 0"
-"h10" 2 "0 -1 0"
-"h9" 2 "0 -1 0"
-"a19" 6 "0 -1 0"
-"b20" 6 "0 -1 0"
-"b18" 6 "0 -1 0"
-"a17" 6 "0 -1 0"
-"b16" 6 "0 -1 0"
-"a15" 6 "0 -1 0"
-"b14" 6 "0 -1 0"
-"a13" 6 "0 -1 0"
-"b12" 6 "0 -1 0"
-"a10" 11 "0 -1 0"
-"b10" 6 "0 -1 0"
-"a7" 6 "0 -1 0"
-"a9" 6 "0 -1 0"
-"b8" 6 "0 -1 0"
-"b6" 6 "0 -1 0"
-"a5" 6 "0 -1 0"
-"b4" 6 "0 -1 0"
-"a3" 6 "0 -1 0"
-"b2" 6 "0 -1 0"
-"a1" 6 "0 -1 0"
-"c1" 6 "0 -1 0"
-"d2" 6 "0 -1 0"
-"e1" 6 "0 -1 0"
-"c3" 6 "0 -1 0"
-"c5" 6 "0 -1 0"
-"c7" 6 "0 -1 0"
-"c9" 6 "0 -1 0"
-"c11" 6 "0 -1 0"
-"c13" 6 "0 -1 0"
-"c15" 6 "0 -1 0"
-"c17" 6 "0 -1 0"
-"c19" 6 "0 -1 0"
-"d20" 6 "0 -1 0"
-"d18" 6 "0 -1 0"
-"d16" 6 "0 -1 0"
-"d14" 6 "0 -1 0"
-"d12" 6 "0 -1 0"
-"d10" 6 "0 -1 0"
-"d8" 6 "0 -1 0"
-"d6" 6 "0 -1 0"
-"d4" 6 "0 -1 0"
-"e3" 6 "0 -1 0"
-"e9" 6 "0 -1 0"
-"e5" 6 "0 -1 0"
-"e7" 6 "0 -1 0"
-"e11" 6 "0 -1 0"
-"e13" 6 "0 -1 0"
-"e15" 6 "0 -1 0"
-"e17" 6 "0 -1 0"
-"e19" 6 "0 -1 0"
-"a20" 11 "0 -1 0"
-"b19" 11 "0 -1 0"
-"a18" 11 "0 -1 0"
-"a16" 11 "0 -1 0"
-"a14" 11 "0 -1 0"
-"a12" 11 "0 -1 0"
-"a8" 11 "0 -1 0"
-"a6" 11 "0 -1 0"
-"a4" 11 "0 -1 0"
-"a2" 11 "0 -1 0"
-"b1" 11 "0 -1 0"
-"b3" 11 "0 -1 0"
-"b5" 11 "0 -1 0"
-"b7" 11 "0 -1 0"
-"b9" 11 "0 -1 0"
-"b11" 11 "0 -1 0"
-"b13" 11 "0 -1 0"
-"b15" 11 "0 -1 0"
-"b17" 11 "0 -1 0"
-"c20" 11 "0 -1 0"
-"c18" 11 "0 -1 0"
-"c16" 11 "0 -1 0"
-"c14" 11 "0 -1 0"
-"c12" 11 "0 -1 0"
-"c10" 11 "0 -1 0"
-"c8" 11 "0 -1 0"
-"c6" 11 "0 -1 0"
-"c4" 11 "0 -1 0"
-"c2" 11 "0 -1 0"
-"d1" 11 "0 -1 0"
-"d3" 11 "0 -1 0"
-"d5" 11 "0 -1 0"
-"d7" 11 "0 -1 0"
-"d9" 11 "0 -1 0"
-"d11" 11 "0 -1 0"
-"d13" 11 "0 -1 0"
-"d15" 11 "0 -1 0"
-"d17" 11 "0 -1 0"
-"d19" 11 "0 -1 0"
-"e20" 11 "0 -1 0"
-"e18" 11 "0 -1 0"
-"e16" 11 "0 -1 0"
-"e14" 11 "0 -1 0"
-"e12" 11 "0 -1 0"
-"e10" 11 "0 -1 0"
-"e8" 11 "0 -1 0"
-"e6" 11 "0 -1 0"
-"e4" 11 "0 -1 0"
-"e2" 11 "0 -1 0"
-"a11" 6 "0 -1 0"
-"p1" 6 "0 -1 0"
-"p3" 6 "0 -1 0"
-"p5" 6 "0 -1 0"
-"p7" 6 "0 -1 0"
-"p9" 6 "0 -1 0"
-"p15" 6 "0 -1 0"
-"p11" 6 "0 -1 0"
-"p13" 6 "0 -1 0"
-"q20" 6 "0 -1 0"
-"p17" 6 "0 -1 0"
-"p19" 6 "0 -1 0"
-"q18" 6 "0 -1 0"
-"q16" 6 "0 -1 0"
-"q14" 6 "0 -1 0"
-"q12" 6 "0 -1 0"
-"q10" 6 "0 -1 0"
-"q8" 6 "0 -1 0"
-"q6" 6 "0 -1 0"
-"q4" 6 "0 -1 0"
-"q2" 6 "0 -1 0"
-"r1" 6 "0 -1 0"
-"r3" 6 "0 -1 0"
-"r5" 6 "0 -1 0"
-"r7" 6 "0 -1 0"
-"r9" 6 "0 -1 0"
-"r11" 6 "0 -1 0"
-"r13" 6 "0 -1 0"
-"r15" 6 "0 -1 0"
-"r17" 6 "0 -1 0"
-"r19" 6 "0 -1 0"
-"s20" 6 "0 -1 0"
-"s18" 6 "0 -1 0"
-"s16" 6 "0 -1 0"
-"s14" 6 "0 -1 0"
-"s12" 6 "0 -1 0"
-"s10" 6 "0 -1 0"
-"s8" 6 "0 -1 0"
-"s6" 6 "0 -1 0"
-"s4" 6 "0 -1 0"
-"s2" 6 "0 -1 0"
-"t1" 6 "0 -1 0"
-"t3" 6 "0 -1 0"
-"t5" 6 "0 -1 0"
-"t7" 6 "0 -1 0"
-"t9" 6 "0 -1 0"
-"t11" 6 "0 -1 0"
-"t13" 6 "0 -1 0"
-"t14" 11 "0 -1 0"
-"t17" 6 "0 -1 0"
-"t19" 6 "0 -1 0"
-"p20" 11 "0 -1 0"
-"p18" 11 "0 -1 0"
-"p16" 11 "0 -1 0"
-"p14" 11 "0 -1 0"
-"p12" 11 "0 -1 0"
-"p10" 11 "0 -1 0"
-"p8" 11 "0 -1 0"
-"p6" 11 "0 -1 0"
-"p4" 11 "0 -1 0"
-"p2" 11 "0 -1 0"
-"q1" 11 "0 -1 0"
-"q3" 11 "0 -1 0"
-"q5" 11 "0 -1 0"
-"q7" 11 "0 -1 0"
-"q9" 11 "0 -1 0"
-"q11" 11 "0 -1 0"
-"q13" 11 "0 -1 0"
-"q15" 11 "0 -1 0"
-"q17" 11 "0 -1 0"
-"q19" 11 "0 -1 0"
-"r20" 11 "0 -1 0"
-"r18" 11 "0 -1 0"
-"r16" 11 "0 -1 0"
-"r14" 11 "0 -1 0"
-"r12" 11 "0 -1 0"
-"r10" 11 "0 -1 0"
-"r8" 11 "0 -1 0"
-"r6" 11 "0 -1 0"
-"r4" 11 "0 -1 0"
-"r2" 11 "0 -1 0"
-"s1" 11 "0 -1 0"
-"s3" 11 "0 -1 0"
-"s5" 11 "0 -1 0"
-"s7" 11 "0 -1 0"
-"s9" 11 "0 -1 0"
-"s11" 11 "0 -1 0"
-"s13" 11 "0 -1 0"
-"s15" 11 "0 -1 0"
-"s17" 11 "0 -1 0"
-"s19" 11 "0 -1 0"
-"t20" 11 "0 -1 0"
-"t18" 11 "0 -1 0"
-"t16" 11 "0 -1 0"
-"t10" 11 "0 -1 0"
-"t12" 11 "0 -1 0"
-"t8" 11 "0 -1 0"
-"t6" 11 "0 -1 0"
-"t4" 11 "0 -1 0"
-"t2" 11 "0 -1 0"
-"t15" 6 "0 -1 0"
-"h18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"o16" 4 "0 -1 0"
-"n16" 4 "0 -1 0"
-"m16" 4 "0 -1 0"
-"l16" 4 "0 -1 0"
-"k16" 4 "0 -1 0"
-"j16" 4 "0 -1 0"
-"i16" 4 "0 -1 0"
-"h16" 4 "0 -1 0"
-"g16" 4 "0 -1 0"
-"f16" 4 "0 -1 0"
-"f15" 4 "0 -1 0"
-"g15" 4 "0 -1 0"
-"g14" 4 "0 -1 0"
-"f14" 4 "0 -1 0"
-"f13" 4 "0 -1 0"
-"g13" 4 "0 -1 0"
-"f12" 4 "0 -1 0"
-"f11" 4 "0 -1 0"
-"f10" 4 "0 -1 0"
-"f9" 4 "0 -1 0"
-"f8" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"h7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"k6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"o9" 4 "0 -1 0"
-"o10" 4 "0 -1 0"
-"o11" 4 "0 -1 0"
-"o12" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"n13" 4 "0 -1 0"
-"n14" 4 "0 -1 0"
-"o14" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"n15" 4 "0 -1 0"
-"m15" 4 "0 -1 0"
-"l15" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
+"g10" 5 0
+"g9" 5 0
+"g8" 5 0
+"g11" 5 0
+"g12" 5 0
+"h8" 5 0
+"i8" 5 0
+"j8" 5 0
+"l8" 5 0
+"k8" 5 0
+"m8" 5 0
+"n8" 5 0
+"n9" 5 0
+"n10" 5 0
+"n11" 5 0
+"n12" 5 0
+"m12" 5 0
+"m13" 5 0
+"m14" 5 0
+"h12" 5 0
+"h13" 5 0
+"h14" 5 0
+"h15" 5 0
+"i15" 5 0
+"j15" 5 0
+"k15" 5 0
+"k14" 5 0
+"l14" 5 0
+"k12" 5 0
+"k11" 5 0
+"i12" 5 0
+"i11" 5 0
+"l10" 5 0
+"j14" 1 1
+"j13" 3 0
+"i10" 3 0
+"l9" 3 0
+"h11" 2 0
+"h10" 2 0
+"h9" 2 0
+"a19" 6 0
+"b20" 6 0
+"b18" 6 0
+"a17" 6 0
+"b16" 6 0
+"a15" 6 0
+"b14" 6 0
+"a13" 6 0
+"b12" 6 0
+"a10" 11 0
+"b10" 6 0
+"a7" 6 0
+"a9" 6 0
+"b8" 6 0
+"b6" 6 0
+"a5" 6 0
+"b4" 6 0
+"a3" 6 0
+"b2" 6 0
+"a1" 6 0
+"c1" 6 0
+"d2" 6 0
+"e1" 6 0
+"c3" 6 0
+"c5" 6 0
+"c7" 6 0
+"c9" 6 0
+"c11" 6 0
+"c13" 6 0
+"c15" 6 0
+"c17" 6 0
+"c19" 6 0
+"d20" 6 0
+"d18" 6 0
+"d16" 6 0
+"d14" 6 0
+"d12" 6 0
+"d10" 6 0
+"d8" 6 0
+"d6" 6 0
+"d4" 6 0
+"e3" 6 0
+"e9" 6 0
+"e5" 6 0
+"e7" 6 0
+"e11" 6 0
+"e13" 6 0
+"e15" 6 0
+"e17" 6 0
+"e19" 6 0
+"a20" 11 0
+"b19" 11 0
+"a18" 11 0
+"a16" 11 0
+"a14" 11 0
+"a12" 11 0
+"a8" 11 0
+"a6" 11 0
+"a4" 11 0
+"a2" 11 0
+"b1" 11 0
+"b3" 11 0
+"b5" 11 0
+"b7" 11 0
+"b9" 11 0
+"b11" 11 0
+"b13" 11 0
+"b15" 11 0
+"b17" 11 0
+"c20" 11 0
+"c18" 11 0
+"c16" 11 0
+"c14" 11 0
+"c12" 11 0
+"c10" 11 0
+"c8" 11 0
+"c6" 11 0
+"c4" 11 0
+"c2" 11 0
+"d1" 11 0
+"d3" 11 0
+"d5" 11 0
+"d7" 11 0
+"d9" 11 0
+"d11" 11 0
+"d13" 11 0
+"d15" 11 0
+"d17" 11 0
+"d19" 11 0
+"e20" 11 0
+"e18" 11 0
+"e16" 11 0
+"e14" 11 0
+"e12" 11 0
+"e10" 11 0
+"e8" 11 0
+"e6" 11 0
+"e4" 11 0
+"e2" 11 0
+"a11" 6 0
+"p1" 6 0
+"p3" 6 0
+"p5" 6 0
+"p7" 6 0
+"p9" 6 0
+"p15" 6 0
+"p11" 6 0
+"p13" 6 0
+"q20" 6 0
+"p17" 6 0
+"p19" 6 0
+"q18" 6 0
+"q16" 6 0
+"q14" 6 0
+"q12" 6 0
+"q10" 6 0
+"q8" 6 0
+"q6" 6 0
+"q4" 6 0
+"q2" 6 0
+"r1" 6 0
+"r3" 6 0
+"r5" 6 0
+"r7" 6 0
+"r9" 6 0
+"r11" 6 0
+"r13" 6 0
+"r15" 6 0
+"r17" 6 0
+"r19" 6 0
+"s20" 6 0
+"s18" 6 0
+"s16" 6 0
+"s14" 6 0
+"s12" 6 0
+"s10" 6 0
+"s8" 6 0
+"s6" 6 0
+"s4" 6 0
+"s2" 6 0
+"t1" 6 0
+"t3" 6 0
+"t5" 6 0
+"t7" 6 0
+"t9" 6 0
+"t11" 6 0
+"t13" 6 0
+"t14" 11 0
+"t17" 6 0
+"t19" 6 0
+"p20" 11 0
+"p18" 11 0
+"p16" 11 0
+"p14" 11 0
+"p12" 11 0
+"p10" 11 0
+"p8" 11 0
+"p6" 11 0
+"p4" 11 0
+"p2" 11 0
+"q1" 11 0
+"q3" 11 0
+"q5" 11 0
+"q7" 11 0
+"q9" 11 0
+"q11" 11 0
+"q13" 11 0
+"q15" 11 0
+"q17" 11 0
+"q19" 11 0
+"r20" 11 0
+"r18" 11 0
+"r16" 11 0
+"r14" 11 0
+"r12" 11 0
+"r10" 11 0
+"r8" 11 0
+"r6" 11 0
+"r4" 11 0
+"r2" 11 0
+"s1" 11 0
+"s3" 11 0
+"s5" 11 0
+"s7" 11 0
+"s9" 11 0
+"s11" 11 0
+"s13" 11 0
+"s15" 11 0
+"s17" 11 0
+"s19" 11 0
+"t20" 11 0
+"t18" 11 0
+"t16" 11 0
+"t10" 11 0
+"t12" 11 0
+"t8" 11 0
+"t6" 11 0
+"t4" 11 0
+"t2" 11 0
+"t15" 6 0
+"h18" 4 0
+"g18" 4 0
+"f18" 4 0
+"f17" 4 0
+"g17" 4 0
+"h17" 4 0
+"i17" 4 0
+"j17" 4 0
+"k17" 4 0
+"l17" 4 0
+"m17" 4 0
+"n17" 4 0
+"o17" 4 0
+"o16" 4 0
+"n16" 4 0
+"m16" 4 0
+"l16" 4 0
+"k16" 4 0
+"j16" 4 0
+"i16" 4 0
+"h16" 4 0
+"g16" 4 0
+"f16" 4 0
+"f15" 4 0
+"g15" 4 0
+"g14" 4 0
+"f14" 4 0
+"f13" 4 0
+"g13" 4 0
+"f12" 4 0
+"f11" 4 0
+"f10" 4 0
+"f9" 4 0
+"f8" 4 0
+"f7" 4 0
+"g7" 4 0
+"h7" 4 0
+"i7" 4 0
+"j7" 4 0
+"k7" 4 0
+"l7" 4 0
+"m7" 4 0
+"n7" 4 0
+"o7" 4 0
+"o6" 4 0
+"n6" 4 0
+"m6" 4 0
+"l6" 4 0
+"k6" 4 0
+"j6" 4 0
+"i6" 4 0
+"h6" 4 0
+"g6" 4 0
+"f6" 4 0
+"f5" 4 0
+"g5" 4 0
+"h5" 4 0
+"i5" 4 0
+"j5" 4 0
+"k5" 4 0
+"l5" 4 0
+"m5" 4 0
+"n5" 4 0
+"o5" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"f3" 4 0
+"g3" 4 0
+"h3" 4 0
+"i3" 4 0
+"j3" 4 0
+"k3" 4 0
+"l3" 4 0
+"m3" 4 0
+"n3" 4 0
+"o3" 4 0
+"o2" 4 0
+"n2" 4 0
+"m2" 4 0
+"l2" 4 0
+"k2" 4 0
+"j2" 4 0
+"i2" 4 0
+"h2" 4 0
+"g2" 4 0
+"f2" 4 0
+"f1" 4 0
+"g1" 4 0
+"h1" 4 0
+"i1" 4 0
+"j1" 4 0
+"k1" 4 0
+"l1" 4 0
+"m1" 4 0
+"n1" 4 0
+"o1" 4 0
+"o8" 4 0
+"o9" 4 0
+"o10" 4 0
+"o11" 4 0
+"o12" 4 0
+"o13" 4 0
+"n13" 4 0
+"n14" 4 0
+"o14" 4 0
+"o15" 4 0
+"n15" 4 0
+"m15" 4 0
+"l15" 4 0
+"o18" 4 0
+"n18" 4 0
+"m18" 4 0
+"l18" 4 0
+"k18" 4 0
+"j18" 4 0
+"i18" 4 0
+"i19" 4 0
+"h19" 4 0
+"g19" 4 0
+"f19" 4 0
+"f20" 4 0
+"g20" 4 0
+"h20" 4 0
+"i20" 4 0
+"j20" 4 0
+"k20" 4 0
+"l20" 4 0
+"m20" 4 0
+"n20" 4 0
+"o20" 4 0
+"o19" 4 0
+"n19" 4 0
+"m19" 4 0
+"l19" 4 0
+"k19" 4 0
+"j19" 4 0
index 2d461a0b2cef50bda178df78e68880e98b6585f5..6d616e1e313530252d5ca3d709debb14b5bf30d9 100644 (file)
 // bulldozer storage "level8" last updated 06-12-2015 16:23:34
 nextlevel = "level9"
-"e13" 5 "0 -1 0"
-"e12" 5 "0 -1 0"
-"e11" 5 "0 -1 0"
-"e10" 5 "0 -1 0"
-"e9" 5 "0 -1 0"
-"f9" 5 "0 -1 0"
-"g9" 5 "0 -1 0"
-"h9" 5 "0 -1 0"
-"i10" 5 "0 -1 0"
-"i9" 5 "0 -1 0"
-"i11" 5 "0 -1 0"
-"j11" 5 "0 -1 0"
-"k11" 5 "0 -1 0"
-"l11" 5 "0 -1 0"
-"l10" 5 "0 -1 0"
-"l9" 5 "0 -1 0"
-"n9" 5 "0 -1 0"
-"m9" 5 "0 -1 0"
-"o9" 5 "0 -1 0"
-"o10" 5 "0 -1 0"
-"p10" 5 "0 -1 0"
-"p11" 5 "0 -1 0"
-"p12" 5 "0 -1 0"
-"o13" 5 "0 -1 0"
-"p13" 5 "0 -1 0"
-"o14" 5 "0 -1 0"
-"n14" 5 "0 -1 0"
-"l14" 5 "0 -1 0"
-"f14" 5 "0 -1 0"
-"g14" 5 "0 -1 0"
-"h14" 5 "0 -1 0"
-"i14" 5 "0 -1 0"
-"j14" 5 "0 -1 0"
-"m14" 5 "0 -1 0"
-"k14" 5 "0 -1 0"
-"f13" 5 "0 -1 0"
-"j13" 5 "0 -1 0"
-"i12" 2 "0 -1 0"
-"l12" 2 "0 -1 0"
-"j12" 2 "0 -1 0"
-"k12" 2 "0 -1 0"
-"h13" 1 "0 -1 0"
-"g12" 3 "0 -1 0"
-"h11" 3 "0 -1 0"
-"m12" 3 "0 -1 0"
-"n11" 3 "0 -1 0"
-"d13" 8 "0 -1 0"
-"d15" 8 "0 -1 0"
-"b16" 8 "0 -1 0"
-"b14" 8 "0 -1 0"
-"b12" 8 "0 -1 0"
-"b10" 8 "0 -1 0"
-"b8" 8 "0 -1 0"
-"d11" 8 "0 -1 0"
-"d9" 8 "0 -1 0"
-"d7" 8 "0 -1 0"
-"q10" 8 "0 -1 0"
-"q8" 8 "0 -1 0"
-"s15" 8 "0 -1 0"
-"s7" 8 "0 -1 0"
-"s9" 8 "0 -1 0"
-"s11" 8 "0 -1 0"
-"s13" 8 "0 -1 0"
-"q12" 8 "0 -1 0"
-"q14" 8 "0 -1 0"
-"q16" 8 "0 -1 0"
-"h17" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"f17" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"d17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"a17" 4 "0 -1 0"
-"a16" 4 "0 -1 0"
-"a15" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"c14" 4 "0 -1 0"
-"d14" 4 "0 -1 0"
-"e14" 4 "0 -1 0"
-"e15" 4 "0 -1 0"
-"f15" 4 "0 -1 0"
-"g15" 4 "0 -1 0"
-"h15" 4 "0 -1 0"
-"i15" 4 "0 -1 0"
-"j15" 4 "0 -1 0"
-"k15" 4 "0 -1 0"
-"l15" 4 "0 -1 0"
-"m15" 4 "0 -1 0"
-"n15" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"p15" 4 "0 -1 0"
-"q15" 4 "0 -1 0"
-"r15" 4 "0 -1 0"
-"r14" 4 "0 -1 0"
-"s14" 4 "0 -1 0"
-"t14" 4 "0 -1 0"
-"t13" 4 "0 -1 0"
-"t12" 4 "0 -1 0"
-"s12" 4 "0 -1 0"
-"r12" 4 "0 -1 0"
-"r11" 4 "0 -1 0"
-"q11" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"s10" 4 "0 -1 0"
-"t10" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"t8" 4 "0 -1 0"
-"s8" 4 "0 -1 0"
-"r8" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"p7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"h7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"k5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"a4" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"t3" 4 "0 -1 0"
-"t2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"a1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"t1" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"k6" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"h6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"t7" 4 "0 -1 0"
-"a7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"c8" 4 "0 -1 0"
-"d8" 4 "0 -1 0"
-"e8" 4 "0 -1 0"
-"f8" 4 "0 -1 0"
-"g8" 4 "0 -1 0"
-"h8" 4 "0 -1 0"
-"i8" 4 "0 -1 0"
-"j8" 4 "0 -1 0"
-"k8" 4 "0 -1 0"
-"l8" 4 "0 -1 0"
-"m8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"p9" 4 "0 -1 0"
-"q9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"k9" 4 "0 -1 0"
-"j9" 4 "0 -1 0"
-"j10" 4 "0 -1 0"
-"k10" 4 "0 -1 0"
-"c9" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"a9" 4 "0 -1 0"
-"a8" 4 "0 -1 0"
-"a10" 4 "0 -1 0"
-"a11" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"c11" 4 "0 -1 0"
-"c10" 4 "0 -1 0"
-"d10" 4 "0 -1 0"
-"c12" 4 "0 -1 0"
-"d12" 4 "0 -1 0"
-"c13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"a13" 4 "0 -1 0"
-"a12" 4 "0 -1 0"
-"a14" 4 "0 -1 0"
-"t11" 4 "0 -1 0"
-"r13" 4 "0 -1 0"
-"q13" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"t16" 4 "0 -1 0"
-"s16" 4 "0 -1 0"
-"r16" 4 "0 -1 0"
-"r17" 4 "0 -1 0"
-"q17" 4 "0 -1 0"
-"p17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"i16" 4 "0 -1 0"
-"h16" 4 "0 -1 0"
-"g16" 4 "0 -1 0"
-"f16" 4 "0 -1 0"
-"e16" 4 "0 -1 0"
-"d16" 4 "0 -1 0"
-"c16" 4 "0 -1 0"
-"j16" 4 "0 -1 0"
-"k16" 4 "0 -1 0"
-"l16" 4 "0 -1 0"
-"m16" 4 "0 -1 0"
-"n16" 4 "0 -1 0"
-"o16" 4 "0 -1 0"
-"p16" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"a18" 4 "0 -1 0"
-"a19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"t19" 4 "0 -1 0"
-"t18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"t17" 4 "0 -1 0"
-"t20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"a20" 4 "0 -1 0"
-"p14" 4 "0 -1 0"
+"e13" 5 0
+"e12" 5 0
+"e11" 5 0
+"e10" 5 0
+"e9" 5 0
+"f9" 5 0
+"g9" 5 0
+"h9" 5 0
+"i10" 5 0
+"i9" 5 0
+"i11" 5 0
+"j11" 5 0
+"k11" 5 0
+"l11" 5 0
+"l10" 5 0
+"l9" 5 0
+"n9" 5 0
+"m9" 5 0
+"o9" 5 0
+"o10" 5 0
+"p10" 5 0
+"p11" 5 0
+"p12" 5 0
+"o13" 5 0
+"p13" 5 0
+"o14" 5 0
+"n14" 5 0
+"l14" 5 0
+"f14" 5 0
+"g14" 5 0
+"h14" 5 0
+"i14" 5 0
+"j14" 5 0
+"m14" 5 0
+"k14" 5 0
+"f13" 5 0
+"j13" 5 0
+"i12" 2 0
+"l12" 2 0
+"j12" 2 0
+"k12" 2 0
+"h13" 1 1
+"g12" 3 0
+"h11" 3 0
+"m12" 3 0
+"n11" 3 0
+"d13" 8 0
+"d15" 8 0
+"b16" 8 0
+"b14" 8 0
+"b12" 8 0
+"b10" 8 0
+"b8" 8 0
+"d11" 8 0
+"d9" 8 0
+"d7" 8 0
+"q10" 8 0
+"q8" 8 0
+"s15" 8 0
+"s7" 8 0
+"s9" 8 0
+"s11" 8 0
+"s13" 8 0
+"q12" 8 0
+"q14" 8 0
+"q16" 8 0
+"h17" 4 0
+"g17" 4 0
+"f17" 4 0
+"e17" 4 0
+"d17" 4 0
+"c17" 4 0
+"b17" 4 0
+"a17" 4 0
+"a16" 4 0
+"a15" 4 0
+"b15" 4 0
+"c15" 4 0
+"c14" 4 0
+"d14" 4 0
+"e14" 4 0
+"e15" 4 0
+"f15" 4 0
+"g15" 4 0
+"h15" 4 0
+"i15" 4 0
+"j15" 4 0
+"k15" 4 0
+"l15" 4 0
+"m15" 4 0
+"n15" 4 0
+"o15" 4 0
+"p15" 4 0
+"q15" 4 0
+"r15" 4 0
+"r14" 4 0
+"s14" 4 0
+"t14" 4 0
+"t13" 4 0
+"t12" 4 0
+"s12" 4 0
+"r12" 4 0
+"r11" 4 0
+"q11" 4 0
+"r10" 4 0
+"s10" 4 0
+"t10" 4 0
+"t9" 4 0
+"t8" 4 0
+"s8" 4 0
+"r8" 4 0
+"r7" 4 0
+"q7" 4 0
+"p7" 4 0
+"o7" 4 0
+"n7" 4 0
+"m7" 4 0
+"l7" 4 0
+"k7" 4 0
+"j7" 4 0
+"i7" 4 0
+"h7" 4 0
+"g7" 4 0
+"f7" 4 0
+"e7" 4 0
+"e6" 4 0
+"d6" 4 0
+"c6" 4 0
+"b6" 4 0
+"a6" 4 0
+"a5" 4 0
+"b5" 4 0
+"c5" 4 0
+"d5" 4 0
+"e5" 4 0
+"f5" 4 0
+"g5" 4 0
+"h5" 4 0
+"i5" 4 0
+"j5" 4 0
+"k5" 4 0
+"l5" 4 0
+"m5" 4 0
+"n5" 4 0
+"o5" 4 0
+"p5" 4 0
+"q5" 4 0
+"r5" 4 0
+"s5" 4 0
+"t5" 4 0
+"t4" 4 0
+"s4" 4 0
+"r4" 4 0
+"q4" 4 0
+"p4" 4 0
+"o4" 4 0
+"n4" 4 0
+"m4" 4 0
+"l4" 4 0
+"k4" 4 0
+"j4" 4 0
+"i4" 4 0
+"h4" 4 0
+"g4" 4 0
+"f4" 4 0
+"e4" 4 0
+"d4" 4 0
+"c4" 4 0
+"b4" 4 0
+"a4" 4 0
+"a3" 4 0
+"b3" 4 0
+"c3" 4 0
+"d3" 4 0
+"e3" 4 0
+"f3" 4 0
+"g3" 4 0
+"h3" 4 0
+"i3" 4 0
+"j3" 4 0
+"k3" 4 0
+"l3" 4 0
+"m3" 4 0
+"n3" 4 0
+"o3" 4 0
+"p3" 4 0
+"q3" 4 0
+"r3" 4 0
+"s3" 4 0
+"t3" 4 0
+"t2" 4 0
+"s2" 4 0
+"r2" 4 0
+"q2" 4 0
+"p2" 4 0
+"o2" 4 0
+"n2" 4 0
+"m2" 4 0
+"l2" 4 0
+"k2" 4 0
+"j2" 4 0
+"i2" 4 0
+"h2" 4 0
+"g2" 4 0
+"f2" 4 0
+"e2" 4 0
+"d2" 4 0
+"c2" 4 0
+"b2" 4 0
+"a2" 4 0
+"a1" 4 0
+"b1" 4 0
+"c1" 4 0
+"d1" 4 0
+"e1" 4 0
+"f1" 4 0
+"g1" 4 0
+"h1" 4 0
+"i1" 4 0
+"j1" 4 0
+"k1" 4 0
+"l1" 4 0
+"m1" 4 0
+"n1" 4 0
+"o1" 4 0
+"p1" 4 0
+"q1" 4 0
+"r1" 4 0
+"s1" 4 0
+"t1" 4 0
+"t6" 4 0
+"s6" 4 0
+"r6" 4 0
+"q6" 4 0
+"p6" 4 0
+"o6" 4 0
+"n6" 4 0
+"m6" 4 0
+"l6" 4 0
+"k6" 4 0
+"j6" 4 0
+"i6" 4 0
+"h6" 4 0
+"g6" 4 0
+"f6" 4 0
+"t7" 4 0
+"a7" 4 0
+"b7" 4 0
+"c7" 4 0
+"c8" 4 0
+"d8" 4 0
+"e8" 4 0
+"f8" 4 0
+"g8" 4 0
+"h8" 4 0
+"i8" 4 0
+"j8" 4 0
+"k8" 4 0
+"l8" 4 0
+"m8" 4 0
+"n8" 4 0
+"o8" 4 0
+"p8" 4 0
+"p9" 4 0
+"q9" 4 0
+"r9" 4 0
+"k9" 4 0
+"j9" 4 0
+"j10" 4 0
+"k10" 4 0
+"c9" 4 0
+"b9" 4 0
+"a9" 4 0
+"a8" 4 0
+"a10" 4 0
+"a11" 4 0
+"b11" 4 0
+"c11" 4 0
+"c10" 4 0
+"d10" 4 0
+"c12" 4 0
+"d12" 4 0
+"c13" 4 0
+"b13" 4 0
+"a13" 4 0
+"a12" 4 0
+"a14" 4 0
+"t11" 4 0
+"r13" 4 0
+"q13" 4 0
+"t15" 4 0
+"t16" 4 0
+"s16" 4 0
+"r16" 4 0
+"r17" 4 0
+"q17" 4 0
+"p17" 4 0
+"o17" 4 0
+"n17" 4 0
+"m17" 4 0
+"l17" 4 0
+"k17" 4 0
+"j17" 4 0
+"i17" 4 0
+"i16" 4 0
+"h16" 4 0
+"g16" 4 0
+"f16" 4 0
+"e16" 4 0
+"d16" 4 0
+"c16" 4 0
+"j16" 4 0
+"k16" 4 0
+"l16" 4 0
+"m16" 4 0
+"n16" 4 0
+"o16" 4 0
+"p16" 4 0
+"i18" 4 0
+"h18" 4 0
+"g18" 4 0
+"f18" 4 0
+"e18" 4 0
+"d18" 4 0
+"c18" 4 0
+"b18" 4 0
+"a18" 4 0
+"a19" 4 0
+"b19" 4 0
+"c19" 4 0
+"d19" 4 0
+"e19" 4 0
+"f19" 4 0
+"g19" 4 0
+"h19" 4 0
+"i19" 4 0
+"j19" 4 0
+"k19" 4 0
+"l19" 4 0
+"m19" 4 0
+"n19" 4 0
+"o19" 4 0
+"p19" 4 0
+"q19" 4 0
+"r19" 4 0
+"s19" 4 0
+"t19" 4 0
+"t18" 4 0
+"s18" 4 0
+"r18" 4 0
+"q18" 4 0
+"p18" 4 0
+"o18" 4 0
+"n18" 4 0
+"m18" 4 0
+"l18" 4 0
+"k18" 4 0
+"j18" 4 0
+"s17" 4 0
+"t17" 4 0
+"t20" 4 0
+"s20" 4 0
+"r20" 4 0
+"q20" 4 0
+"p20" 4 0
+"o20" 4 0
+"n20" 4 0
+"m20" 4 0
+"l20" 4 0
+"k20" 4 0
+"j20" 4 0
+"i20" 4 0
+"h20" 4 0
+"g20" 4 0
+"f20" 4 0
+"e20" 4 0
+"d20" 4 0
+"c20" 4 0
+"b20" 4 0
+"a20" 4 0
+"p14" 4 0
index 676e4baa1514902efc7bfa4459e9b9e881b86252..a0da90da16f0e5a3bc6efa34938fd613763af07f 100644 (file)
 // bulldozer storage "level9" last updated 11-06-2016 14:53:20
 nextlevel = "level10"
-"c9" 4 "0 -1 0"
-"b9" 4 "0 -1 0"
-"a9" 4 "0 -1 0"
-"a8" 4 "0 -1 0"
-"b8" 4 "0 -1 0"
-"c8" 4 "0 -1 0"
-"d8" 4 "0 -1 0"
-"e8" 4 "0 -1 0"
-"f8" 4 "0 -1 0"
-"g8" 4 "0 -1 0"
-"h8" 4 "0 -1 0"
-"i8" 4 "0 -1 0"
-"j8" 4 "0 -1 0"
-"k8" 4 "0 -1 0"
-"l8" 4 "0 -1 0"
-"m8" 4 "0 -1 0"
-"n8" 4 "0 -1 0"
-"o8" 4 "0 -1 0"
-"p8" 4 "0 -1 0"
-"q8" 4 "0 -1 0"
-"r8" 4 "0 -1 0"
-"s8" 4 "0 -1 0"
-"t8" 4 "0 -1 0"
-"t7" 4 "0 -1 0"
-"s7" 4 "0 -1 0"
-"r7" 4 "0 -1 0"
-"q7" 4 "0 -1 0"
-"p7" 4 "0 -1 0"
-"o7" 4 "0 -1 0"
-"n7" 4 "0 -1 0"
-"m7" 4 "0 -1 0"
-"l7" 4 "0 -1 0"
-"i7" 4 "0 -1 0"
-"h5" 4 "0 -1 0"
-"j7" 4 "0 -1 0"
-"g7" 4 "0 -1 0"
-"f7" 4 "0 -1 0"
-"e7" 4 "0 -1 0"
-"d7" 4 "0 -1 0"
-"c7" 4 "0 -1 0"
-"b7" 4 "0 -1 0"
-"a7" 4 "0 -1 0"
-"a6" 4 "0 -1 0"
-"b6" 4 "0 -1 0"
-"c6" 4 "0 -1 0"
-"d6" 4 "0 -1 0"
-"e6" 4 "0 -1 0"
-"f6" 4 "0 -1 0"
-"g6" 4 "0 -1 0"
-"k7" 4 "0 -1 0"
-"j6" 4 "0 -1 0"
-"l6" 4 "0 -1 0"
-"m6" 4 "0 -1 0"
-"n6" 4 "0 -1 0"
-"o6" 4 "0 -1 0"
-"p6" 4 "0 -1 0"
-"q6" 4 "0 -1 0"
-"r6" 4 "0 -1 0"
-"s6" 4 "0 -1 0"
-"t6" 4 "0 -1 0"
-"t5" 4 "0 -1 0"
-"s5" 4 "0 -1 0"
-"r5" 4 "0 -1 0"
-"q5" 4 "0 -1 0"
-"p5" 4 "0 -1 0"
-"o5" 4 "0 -1 0"
-"n5" 4 "0 -1 0"
-"m5" 4 "0 -1 0"
-"l5" 4 "0 -1 0"
-"g5" 4 "0 -1 0"
-"f5" 4 "0 -1 0"
-"e5" 4 "0 -1 0"
-"d5" 4 "0 -1 0"
-"c5" 4 "0 -1 0"
-"b5" 4 "0 -1 0"
-"a5" 4 "0 -1 0"
-"a4" 4 "0 -1 0"
-"b4" 4 "0 -1 0"
-"c4" 4 "0 -1 0"
-"d4" 4 "0 -1 0"
-"e4" 4 "0 -1 0"
-"f4" 4 "0 -1 0"
-"g4" 4 "0 -1 0"
-"h4" 4 "0 -1 0"
-"i4" 4 "0 -1 0"
-"j4" 4 "0 -1 0"
-"l4" 4 "0 -1 0"
-"m4" 4 "0 -1 0"
-"n4" 4 "0 -1 0"
-"o4" 4 "0 -1 0"
-"p4" 4 "0 -1 0"
-"q4" 4 "0 -1 0"
-"r4" 4 "0 -1 0"
-"s4" 4 "0 -1 0"
-"t4" 4 "0 -1 0"
-"t3" 4 "0 -1 0"
-"s3" 4 "0 -1 0"
-"r3" 4 "0 -1 0"
-"q3" 4 "0 -1 0"
-"p3" 4 "0 -1 0"
-"o3" 4 "0 -1 0"
-"n3" 4 "0 -1 0"
-"m3" 4 "0 -1 0"
-"l3" 4 "0 -1 0"
-"j3" 4 "0 -1 0"
-"i3" 4 "0 -1 0"
-"h3" 4 "0 -1 0"
-"g3" 4 "0 -1 0"
-"f3" 4 "0 -1 0"
-"e3" 4 "0 -1 0"
-"d3" 4 "0 -1 0"
-"c3" 4 "0 -1 0"
-"b3" 4 "0 -1 0"
-"a3" 4 "0 -1 0"
-"a2" 4 "0 -1 0"
-"b2" 4 "0 -1 0"
-"c2" 4 "0 -1 0"
-"d2" 4 "0 -1 0"
-"e2" 4 "0 -1 0"
-"f2" 4 "0 -1 0"
-"g2" 4 "0 -1 0"
-"h2" 4 "0 -1 0"
-"i2" 4 "0 -1 0"
-"j2" 4 "0 -1 0"
-"l2" 4 "0 -1 0"
-"m2" 4 "0 -1 0"
-"n2" 4 "0 -1 0"
-"o2" 4 "0 -1 0"
-"p2" 4 "0 -1 0"
-"q2" 4 "0 -1 0"
-"r2" 4 "0 -1 0"
-"s2" 4 "0 -1 0"
-"t2" 4 "0 -1 0"
-"t1" 4 "0 -1 0"
-"s1" 4 "0 -1 0"
-"r1" 4 "0 -1 0"
-"q1" 4 "0 -1 0"
-"p1" 4 "0 -1 0"
-"o1" 4 "0 -1 0"
-"n1" 4 "0 -1 0"
-"m1" 4 "0 -1 0"
-"l1" 4 "0 -1 0"
-"k1" 4 "0 -1 0"
-"j1" 4 "0 -1 0"
-"i1" 4 "0 -1 0"
-"h1" 4 "0 -1 0"
-"g1" 4 "0 -1 0"
-"f1" 4 "0 -1 0"
-"e1" 4 "0 -1 0"
-"d1" 4 "0 -1 0"
-"c1" 4 "0 -1 0"
-"b1" 4 "0 -1 0"
-"a1" 4 "0 -1 0"
-"t9" 4 "0 -1 0"
-"s9" 4 "0 -1 0"
-"r9" 4 "0 -1 0"
-"q9" 4 "0 -1 0"
-"p11" 6 "0 -1 0"
-"o9" 4 "0 -1 0"
-"n9" 4 "0 -1 0"
-"m10" 5 "0 -1 0"
-"m9" 5 "0 -1 0"
-"l9" 5 "0 -1 0"
-"k9" 5 "0 -1 0"
-"j9" 5 "0 -1 0"
-"i9" 5 "0 -1 0"
-"g9" 4 "0 -1 0"
-"f11" 6 "0 -1 0"
-"e9" 4 "0 -1 0"
-"d9" 4 "0 -1 0"
-"d10" 4 "0 -1 0"
-"c10" 4 "0 -1 0"
-"b10" 4 "0 -1 0"
-"a10" 4 "0 -1 0"
-"a11" 4 "0 -1 0"
-"b11" 4 "0 -1 0"
-"c11" 4 "0 -1 0"
-"f9" 6 "0 -1 0"
-"f12" 11 "0 -1 0"
-"f15" 6 "0 -1 0"
-"g11" 4 "0 -1 0"
-"h9" 5 "0 -1 0"
-"i14" 1 "0 -1 0"
-"i11" 2 "0 -1 0"
-"i10" 2 "0 -1 0"
-"j10" 2 "0 -1 0"
-"h10" 5 "0 -1 0"
-"n11" 4 "0 -1 0"
-"o11" 4 "0 -1 0"
-"p13" 6 "0 -1 0"
-"h7" 4 "0 -1 0"
-"p9" 6 "0 -1 0"
-"s11" 4 "0 -1 0"
-"t11" 4 "0 -1 0"
-"t10" 4 "0 -1 0"
-"s10" 4 "0 -1 0"
-"r10" 4 "0 -1 0"
-"p15" 6 "0 -1 0"
-"q11" 11 "0 -1 0"
-"o10" 4 "0 -1 0"
-"n10" 4 "0 -1 0"
-"h11" 5 "0 -1 0"
-"k10" 2 "0 -1 0"
-"l10" 2 "0 -1 0"
-"j14" 3 "0 -1 0"
-"j13" 3 "0 -1 0"
-"h12" 5 "0 -1 0"
-"g10" 4 "0 -1 0"
-"f14" 11 "0 -1 0"
-"f13" 6 "0 -1 0"
-"t12" 4 "0 -1 0"
-"r11" 6 "0 -1 0"
-"h6" 4 "0 -1 0"
-"p14" 11 "0 -1 0"
-"p10" 11 "0 -1 0"
-"o12" 4 "0 -1 0"
-"n12" 4 "0 -1 0"
-"i12" 5 "0 -1 0"
-"k12" 3 "0 -1 0"
-"j11" 3 "0 -1 0"
-"k10" 3 "0 -1 0"
-"i13" 5 "0 -1 0"
-"h13" 5 "0 -1 0"
-"g12" 4 "0 -1 0"
-"f16" 11 "0 -1 0"
-"c13" 11 "0 -1 0"
-"e13" 11 "0 -1 0"
-"e10" 6 "0 -1 0"
-"b12" 4 "0 -1 0"
-"a12" 4 "0 -1 0"
-"a13" 4 "0 -1 0"
-"b13" 4 "0 -1 0"
-"d14" 11 "0 -1 0"
-"e12" 6 "0 -1 0"
-"f10" 11 "0 -1 0"
-"d13" 6 "0 -1 0"
-"g13" 4 "0 -1 0"
-"h14" 5 "0 -1 0"
-"h15" 5 "0 -1 0"
-"f17" 6 "0 -1 0"
-"e16" 6 "0 -1 0"
-"i15" 5 "0 -1 0"
-"d15" 6 "0 -1 0"
-"n13" 4 "0 -1 0"
-"o13" 4 "0 -1 0"
-"p17" 6 "0 -1 0"
-"r12" 11 "0 -1 0"
-"q13" 11 "0 -1 0"
-"s14" 6 "0 -1 0"
-"t13" 4 "0 -1 0"
-"t14" 4 "0 -1 0"
-"s12" 6 "0 -1 0"
-"s13" 11 "0 -1 0"
-"q12" 6 "0 -1 0"
-"p12" 11 "0 -1 0"
-"o14" 4 "0 -1 0"
-"n14" 4 "0 -1 0"
-"m14" 4 "0 -1 0"
-"i16" 5 "0 -1 0"
-"c14" 6 "0 -1 0"
-"c12" 6 "0 -1 0"
-"d11" 6 "0 -1 0"
-"j16" 5 "0 -1 0"
-"g14" 4 "0 -1 0"
-"p16" 11 "0 -1 0"
-"e14" 6 "0 -1 0"
-"e15" 11 "0 -1 0"
-"d12" 11 "0 -1 0"
-"b14" 4 "0 -1 0"
-"a14" 4 "0 -1 0"
-"a15" 4 "0 -1 0"
-"b15" 4 "0 -1 0"
-"c15" 4 "0 -1 0"
-"e11" 11 "0 -1 0"
-"q15" 11 "0 -1 0"
-"r14" 11 "0 -1 0"
-"k16" 5 "0 -1 0"
-"l16" 5 "0 -1 0"
-"l15" 5 "0 -1 0"
-"l14" 5 "0 -1 0"
-"q10" 6 "0 -1 0"
-"l13" 5 "0 -1 0"
-"m15" 4 "0 -1 0"
-"n15" 4 "0 -1 0"
-"o15" 4 "0 -1 0"
-"q16" 6 "0 -1 0"
-"r15" 6 "0 -1 0"
-"q14" 6 "0 -1 0"
-"s15" 4 "0 -1 0"
-"t15" 4 "0 -1 0"
-"t16" 4 "0 -1 0"
-"s16" 4 "0 -1 0"
-"r16" 4 "0 -1 0"
-"r13" 6 "0 -1 0"
-"k6" 4 "0 -1 0"
-"o16" 4 "0 -1 0"
-"n16" 4 "0 -1 0"
-"m16" 4 "0 -1 0"
-"m13" 5 "0 -1 0"
-"m12" 5 "0 -1 0"
-"h16" 4 "0 -1 0"
-"g16" 4 "0 -1 0"
-"g15" 4 "0 -1 0"
-"m11" 5 "0 -1 0"
-"k5" 4 "0 -1 0"
-"j5" 4 "0 -1 0"
-"d16" 4 "0 -1 0"
-"c16" 4 "0 -1 0"
-"b16" 4 "0 -1 0"
-"a16" 4 "0 -1 0"
-"a17" 4 "0 -1 0"
-"b17" 4 "0 -1 0"
-"c17" 4 "0 -1 0"
-"d17" 4 "0 -1 0"
-"e17" 4 "0 -1 0"
-"i5" 4 "0 -1 0"
-"g17" 4 "0 -1 0"
-"h17" 4 "0 -1 0"
-"i17" 4 "0 -1 0"
-"j17" 4 "0 -1 0"
-"k17" 4 "0 -1 0"
-"l17" 4 "0 -1 0"
-"m17" 4 "0 -1 0"
-"n17" 4 "0 -1 0"
-"o17" 4 "0 -1 0"
-"i6" 4 "0 -1 0"
-"q17" 4 "0 -1 0"
-"r17" 4 "0 -1 0"
-"s17" 4 "0 -1 0"
-"t17" 4 "0 -1 0"
-"t18" 4 "0 -1 0"
-"s18" 4 "0 -1 0"
-"r18" 4 "0 -1 0"
-"q18" 4 "0 -1 0"
-"p18" 4 "0 -1 0"
-"o18" 4 "0 -1 0"
-"n18" 4 "0 -1 0"
-"m18" 4 "0 -1 0"
-"l18" 4 "0 -1 0"
-"k18" 4 "0 -1 0"
-"j18" 4 "0 -1 0"
-"i18" 4 "0 -1 0"
-"h18" 4 "0 -1 0"
-"g18" 4 "0 -1 0"
-"f18" 4 "0 -1 0"
-"e18" 4 "0 -1 0"
-"d18" 4 "0 -1 0"
-"c18" 4 "0 -1 0"
-"b18" 4 "0 -1 0"
-"a18" 4 "0 -1 0"
-"a19" 4 "0 -1 0"
-"b19" 4 "0 -1 0"
-"c19" 4 "0 -1 0"
-"d19" 4 "0 -1 0"
-"e19" 4 "0 -1 0"
-"f19" 4 "0 -1 0"
-"g19" 4 "0 -1 0"
-"h19" 4 "0 -1 0"
-"i19" 4 "0 -1 0"
-"j19" 4 "0 -1 0"
-"k19" 4 "0 -1 0"
-"l19" 4 "0 -1 0"
-"m19" 4 "0 -1 0"
-"n19" 4 "0 -1 0"
-"o19" 4 "0 -1 0"
-"p19" 4 "0 -1 0"
-"q19" 4 "0 -1 0"
-"r19" 4 "0 -1 0"
-"s19" 4 "0 -1 0"
-"t19" 4 "0 -1 0"
-"t20" 4 "0 -1 0"
-"s20" 4 "0 -1 0"
-"r20" 4 "0 -1 0"
-"q20" 4 "0 -1 0"
-"p20" 4 "0 -1 0"
-"o20" 4 "0 -1 0"
-"n20" 4 "0 -1 0"
-"m20" 4 "0 -1 0"
-"l20" 4 "0 -1 0"
-"k20" 4 "0 -1 0"
-"j20" 4 "0 -1 0"
-"i20" 4 "0 -1 0"
-"h20" 4 "0 -1 0"
-"g20" 4 "0 -1 0"
-"f20" 4 "0 -1 0"
-"e20" 4 "0 -1 0"
-"d20" 4 "0 -1 0"
-"c20" 4 "0 -1 0"
-"b20" 4 "0 -1 0"
-"a20" 4 "0 -1 0"
-"k4" 4 "0 -1 0"
-"k3" 4 "0 -1 0"
-"k2" 4 "0 -1 0"
+"c9" 4 0
+"b9" 4 0
+"a9" 4 0
+"a8" 4 0
+"b8" 4 0
+"c8" 4 0
+"d8" 4 0
+"e8" 4 0
+"f8" 4 0
+"g8" 4 0
+"h8" 4 0
+"i8" 4 0
+"j8" 4 0
+"k8" 4 0
+"l8" 4 0
+"m8" 4 0
+"n8" 4 0
+"o8" 4 0
+"p8" 4 0
+"q8" 4 0
+"r8" 4 0
+"s8" 4 0
+"t8" 4 0
+"t7" 4 0
+"s7" 4 0
+"r7" 4 0
+"q7" 4 0
+"p7" 4 0
+"o7" 4 0
+"n7" 4 0
+"m7" 4 0
+"l7" 4 0
+"i7" 4 0
+"h5" 4 0
+"j7" 4 0
+"g7" 4 0
+"f7" 4 0
+"e7" 4 0
+"d7" 4 0
+"c7" 4 0
+"b7" 4 0
+"a7" 4 0
+"a6" 4 0
+"b6" 4 0
+"c6" 4 0
+"d6" 4 0
+"e6" 4 0
+"f6" 4 0
+"g6" 4 0
+"k7" 4 0
+"j6" 4 0
+"l6" 4 0
+"m6" 4 0
+"n6" 4 0
+"o6" 4 0
+"p6" 4 0
+"q6" 4 0
+"r6" 4 0
+"s6" 4 0
+"t6" 4 0
+"t5" 4 0
+"s5" 4 0
+"r5" 4 0
+"q5" 4 0
+"p5" 4 0
+"o5" 4 0
+"n5" 4 0
+"m5" 4 0
+"l5" 4 0
+"g5" 4 0
+"f5" 4 0
+"e5" 4 0
+"d5" 4 0
+"c5" 4 0
+"b5" 4 0
+"a5" 4 0
+"a4" 4 0
+"b4" 4 0
+"c4" 4 0
+"d4" 4 0
+"e4" 4 0
+"f4" 4 0
+"g4" 4 0
+"h4" 4 0
+"i4" 4 0
+"j4" 4 0
+"l4" 4 0
+"m4" 4 0
+"n4" 4 0
+"o4" 4 0
+"p4" 4 0
+"q4" 4 0
+"r4" 4 0
+"s4" 4 0
+"t4" 4 0
+"t3" 4 0
+"s3" 4 0
+"r3" 4 0
+"q3" 4 0
+"p3" 4 0
+"o3" 4 0
+"n3" 4 0
+"m3" 4 0
+"l3" 4 0
+"j3" 4 0
+"i3" 4 0
+"h3" 4 0
+"g3" 4 0
+"f3" 4 0
+"e3" 4 0
+"d3" 4 0
+"c3" 4 0
+"b3" 4 0
+"a3" 4 0
+"a2" 4 0
+"b2" 4 0
+"c2" 4 0
+"d2" 4 0
+"e2" 4 0
+"f2" 4 0
+"g2" 4 0
+"h2" 4 0
+"i2" 4 0
+"j2" 4 0
+"l2" 4 0
+"m2" 4 0
+"n2" 4 0
+"o2" 4 0
+"p2" 4 0
+"q2" 4 0
+"r2" 4 0
+"s2" 4 0
+"t2" 4 0
+"t1" 4 0
+"s1" 4 0
+"r1" 4 0
+"q1" 4 0
+"p1" 4 0
+"o1" 4 0
+"n1" 4 0
+"m1" 4 0
+"l1" 4 0
+"k1" 4 0
+"j1" 4 0
+"i1" 4 0
+"h1" 4 0
+"g1" 4 0
+"f1" 4 0
+"e1" 4 0
+"d1" 4 0
+"c1" 4 0
+"b1" 4 0
+"a1" 4 0
+"t9" 4 0
+"s9" 4 0
+"r9" 4 0
+"q9" 4 0
+"p11" 6 0
+"o9" 4 0
+"n9" 4 0
+"m10" 5 0
+"m9" 5 0
+"l9" 5 0
+"k9" 5 0
+"j9" 5 0
+"i9" 5 0
+"g9" 4 0
+"f11" 6 0
+"e9" 4 0
+"d9" 4 0
+"d10" 4 0
+"c10" 4 0
+"b10" 4 0
+"a10" 4 0
+"a11" 4 0
+"b11" 4 0
+"c11" 4 0
+"f9" 6 0
+"f12" 11 0
+"f15" 6 0
+"g11" 4 0
+"h9" 5 0
+"i14" 1 1
+"i11" 2 0
+"i10" 2 0
+"j10" 2 0
+"h10" 5 0
+"n11" 4 0
+"o11" 4 0
+"p13" 6 0
+"h7" 4 0
+"p9" 6 0
+"s11" 4 0
+"t11" 4 0
+"t10" 4 0
+"s10" 4 0
+"r10" 4 0
+"p15" 6 0
+"q11" 11 0
+"o10" 4 0
+"n10" 4 0
+"h11" 5 0
+"k10" 2 0
+"l10" 2 0
+"j14" 3 0
+"j13" 3 0
+"h12" 5 0
+"g10" 4 0
+"f14" 11 0
+"f13" 6 0
+"t12" 4 0
+"r11" 6 0
+"h6" 4 0
+"p14" 11 0
+"p10" 11 0
+"o12" 4 0
+"n12" 4 0
+"i12" 5 0
+"k12" 3 0
+"j11" 3 0
+"k10" 3 0
+"i13" 5 0
+"h13" 5 0
+"g12" 4 0
+"f16" 11 0
+"c13" 11 0
+"e13" 11 0
+"e10" 6 0
+"b12" 4 0
+"a12" 4 0
+"a13" 4 0
+"b13" 4 0
+"d14" 11 0
+"e12" 6 0
+"f10" 11 0
+"d13" 6 0
+"g13" 4 0
+"h14" 5 0
+"h15" 5 0
+"f17" 6 0
+"e16" 6 0
+"i15" 5 0
+"d15" 6 0
+"n13" 4 0
+"o13" 4 0
+"p17" 6 0
+"r12" 11 0
+"q13" 11 0
+"s14" 6 0
+"t13" 4 0
+"t14" 4 0
+"s12" 6 0
+"s13" 11 0
+"q12" 6 0
+"p12" 11 0
+"o14" 4 0
+"n14" 4 0
+"m14" 4 0
+"i16" 5 0
+"c14" 6 0
+"c12" 6 0
+"d11" 6 0
+"j16" 5 0
+"g14" 4 0
+"p16" 11 0
+"e14" 6 0
+"e15" 11 0
+"d12" 11 0
+"b14" 4 0
+"a14" 4 0
+"a15" 4 0
+"b15" 4 0
+"c15" 4 0
+"e11" 11 0
+"q15" 11 0
+"r14" 11 0
+"k16" 5 0
+"l16" 5 0
+"l15" 5 0
+"l14" 5 0
+"q10" 6 0
+"l13" 5 0
+"m15" 4 0
+"n15" 4 0
+"o15" 4 0
+"q16" 6 0
+"r15" 6 0
+"q14" 6 0
+"s15" 4 0
+"t15" 4 0
+"t16" 4 0
+"s16" 4 0
+"r16" 4 0
+"r13" 6 0
+"k6" 4 0
+"o16" 4 0
+"n16" 4 0
+"m16" 4 0
+"m13" 5 0
+"m12" 5 0
+"h16" 4 0
+"g16" 4 0
+"g15" 4 0
+"m11" 5 0
+"k5" 4 0
+"j5" 4 0
+"d16" 4 0
+"c16" 4 0
+"b16" 4 0
+"a16" 4 0
+"a17" 4 0
+"b17" 4 0
+"c17" 4 0
+"d17" 4 0
+"e17" 4 0
+"i5" 4 0
+"g17" 4 0
+"h17" 4 0
+"i17" 4 0
+"j17" 4 0
+"k17" 4 0
+"l17" 4 0
+"m17" 4 0
+"n17" 4 0
+"o17" 4 0
+"i6" 4 0
+"q17" 4 0
+"r17" 4 0
+"s17" 4 0
+"t17" 4 0
+"t18" 4 0
+"s18" 4 0
+"r18" 4 0
+"q18" 4 0
+"p18" 4 0
+"o18" 4 0
+"n18" 4 0
+"m18" 4 0
+"l18" 4 0
+"k18" 4 0
+"j18" 4 0
+"i18" 4 0
+"h18" 4 0
+"g18" 4 0
+"f18" 4 0
+"e18" 4 0
+"d18" 4 0
+"c18" 4 0
+"b18" 4 0
+"a18" 4 0
+"a19" 4 0
+"b19" 4 0
+"c19" 4 0
+"d19" 4 0
+"e19" 4 0
+"f19" 4 0
+"g19" 4 0
+"h19" 4 0
+"i19" 4 0
+"j19" 4 0
+"k19" 4 0
+"l19" 4 0
+"m19" 4 0
+"n19" 4 0
+"o19" 4 0
+"p19" 4 0
+"q19" 4 0
+"r19" 4 0
+"s19" 4 0
+"t19" 4 0
+"t20" 4 0
+"s20" 4 0
+"r20" 4 0
+"q20" 4 0
+"p20" 4 0
+"o20" 4 0
+"n20" 4 0
+"m20" 4 0
+"l20" 4 0
+"k20" 4 0
+"j20" 4 0
+"i20" 4 0
+"h20" 4 0
+"g20" 4 0
+"f20" 4 0
+"e20" 4 0
+"d20" 4 0
+"c20" 4 0
+"b20" 4 0
+"a20" 4 0
+"k4" 4 0
+"k3" 4 0
+"k2" 4 0
index 61384b5fe7d861ad0ce3ea0af0d34546d91f63de..3f1a594d0692979666b44cc660ac2493adf01597 100644 (file)
Binary files a/models/items/a_rockets.md3 and b/models/items/a_rockets.md3 differ
diff --git a/models/player/gakarmored.iqm b/models/player/gakarmored.iqm
deleted file mode 100644 (file)
index 824e52a..0000000
Binary files a/models/player/gakarmored.iqm and /dev/null differ
diff --git a/models/player/gakarmored.iqm.framegroups b/models/player/gakarmored.iqm.framegroups
deleted file mode 100644 (file)
index 98eabd7..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-0 36 15.000000 0 // dieone
-36 20 15.000000 0 // dietwo
-56 15 15.000000 1 // draw
-71 20 15.000000 1 // duck
-91 21 30.000000 1 // duckwalk
-112 16 15.000000 0 // duckjump
-128 15 5.000000 1 // duckidle
-143 41 5.000000 1 // idle
-184 160 25.000000 0 // jump
-344 15 15.000000 0 // painone
-359 17 15.000000 0 // paintwo
-376 3 15.000000 0 // shoot
-379 21 15.000000 1 // taunt
-400 21 35.000000 1 // run
-421 21 35.000000 1 // runbackwards
-442 21 35.000000 1 // strafeleft
-463 21 35.000000 1 // straferight
-484 2 15.000000 0 // deadone
-486 2 15.000000 0 // deadtwo
-488 21 35.000000 1 // forwardright
-509 21 35.000000 1 // forwardleft
-530 21 35.000000 1 // backright
-551 21 30.000000 1 // backleft
-572 21 20.000000 0 // melee
-593 21 30.000000 1 // duckwalkbackwards
-91 21 30.000000 1 // duckwalkstrafeleft
-91 21 30.000000 1 // duckwalkstraferight
-91 21 30.000000 1 // duckwalkforwardright
-91 21 30.000000 1 // duckwalkforwardleft
-593 21 30.000000 1 // duckwalkbackright
-593 21 30.000000 1 // duckwalkbackleft
\ No newline at end of file
diff --git a/models/player/gakarmored.iqm_0.skin b/models/player/gakarmored.iqm_0.skin
deleted file mode 100644 (file)
index 1e8d3e9..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-gak1.001,gak
-gak1,gakarmor
diff --git a/models/player/gakarmored.iqm_0.sounds b/models/player/gakarmored.iqm_0.sounds
deleted file mode 100644 (file)
index fb5b767..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-//TAG: reptilian
-//affirmative sound/player/carni-lycan/player/affirmative 0
-attack sound/player/reptilian/coms/attack 0
-//attacking sound/player/carni-lycan/player/attacking 0
-attackinfive sound/player/reptilian/coms/attackinfive 0
-coverme sound/player/reptilian/coms/coverme 0
-defend sound/player/reptilian/coms/defend 0
-//defending sound/player/carni-lycan/player/defending 0
-//droppedflag sound/player/carni-lycan/player/droppedflag 0
-//flagcarriertakingdamage sound/player/soldier/player/flagcarriertakingdamage 0
-freelance sound/player/reptilian/coms/freelance 2
-//getflag sound/player/soldier/player/getflag 0
-incoming sound/player/reptilian/coms/incoming 0
-meet sound/player/reptilian/coms/meet 0
-needhelp sound/player/reptilian/coms/needhelp 2
-//negative sound/player/carni-lycan/player/negative 0
-//onmyway sound/player/carni-lycan/player/onmyway 0
-//roaming sound/player/carni-lycan/player/roaming 0
-//seenenemy sound/player/carni-lycan/player/seenenemy 0
-seenflag sound/player/reptilian/coms/seenflag 0
-taunt sound/player/reptilian/coms/taunt 3
-teamshoot sound/player/reptilian/coms/teamshoot 3
-death sound/player/reptilian/player/death 3
-drown sound/player/reptilian/player/drown 0
-fall sound/player/reptilian/player/fall 0
-falling sound/player/reptilian/player/falling 0
-gasp sound/player/reptilian/player/gasp 0
-jump sound/player/reptilian/player/jump 0
-pain25 sound/player/reptilian/player/pain25 0
-pain50 sound/player/reptilian/player/pain50 0
-pain75 sound/player/reptilian/player/pain75 0
-pain100 sound/player/reptilian/player/pain100 0
diff --git a/models/player/gakarmored.iqm_0.tga b/models/player/gakarmored.iqm_0.tga
deleted file mode 100644 (file)
index 0fbb375..0000000
Binary files a/models/player/gakarmored.iqm_0.tga and /dev/null differ
diff --git a/models/player/gakarmored.iqm_0.txt b/models/player/gakarmored.iqm_0.txt
deleted file mode 100644 (file)
index 46977b8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-name Gak Armored
-species alien
-sex Male
-weight 90
-age 20
-description Heavyweight Xonotic Soldier
-bone_upperbody spine2
-bone_aim0 0.25 spine2
-bone_aim1 0.4 spine4
-bone_aim2 0.35 bip01 r hand
-bone_weapon bip01 r hand
-fixbone 1
diff --git a/models/player/gakarmored.iqm_1.skin b/models/player/gakarmored.iqm_1.skin
deleted file mode 100644 (file)
index 3377bcb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-gak1.001,gakfullbright
-gak1,gakarmorfb
diff --git a/models/player/gakarmored_lod1.iqm b/models/player/gakarmored_lod1.iqm
deleted file mode 100644 (file)
index bbf00f7..0000000
Binary files a/models/player/gakarmored_lod1.iqm and /dev/null differ
diff --git a/models/player/gakarmored_lod1.iqm.framegroups b/models/player/gakarmored_lod1.iqm.framegroups
deleted file mode 100644 (file)
index 98eabd7..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-0 36 15.000000 0 // dieone
-36 20 15.000000 0 // dietwo
-56 15 15.000000 1 // draw
-71 20 15.000000 1 // duck
-91 21 30.000000 1 // duckwalk
-112 16 15.000000 0 // duckjump
-128 15 5.000000 1 // duckidle
-143 41 5.000000 1 // idle
-184 160 25.000000 0 // jump
-344 15 15.000000 0 // painone
-359 17 15.000000 0 // paintwo
-376 3 15.000000 0 // shoot
-379 21 15.000000 1 // taunt
-400 21 35.000000 1 // run
-421 21 35.000000 1 // runbackwards
-442 21 35.000000 1 // strafeleft
-463 21 35.000000 1 // straferight
-484 2 15.000000 0 // deadone
-486 2 15.000000 0 // deadtwo
-488 21 35.000000 1 // forwardright
-509 21 35.000000 1 // forwardleft
-530 21 35.000000 1 // backright
-551 21 30.000000 1 // backleft
-572 21 20.000000 0 // melee
-593 21 30.000000 1 // duckwalkbackwards
-91 21 30.000000 1 // duckwalkstrafeleft
-91 21 30.000000 1 // duckwalkstraferight
-91 21 30.000000 1 // duckwalkforwardright
-91 21 30.000000 1 // duckwalkforwardleft
-593 21 30.000000 1 // duckwalkbackright
-593 21 30.000000 1 // duckwalkbackleft
\ No newline at end of file
diff --git a/models/player/gakarmored_lod1.iqm_0.skin b/models/player/gakarmored_lod1.iqm_0.skin
deleted file mode 100644 (file)
index 1e8d3e9..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-gak1.001,gak
-gak1,gakarmor
diff --git a/models/player/gakarmored_lod1.iqm_1.skin b/models/player/gakarmored_lod1.iqm_1.skin
deleted file mode 100644 (file)
index 3377bcb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-gak1.001,gakfullbright
-gak1,gakarmorfb
diff --git a/models/player/gakarmored_lod2.iqm b/models/player/gakarmored_lod2.iqm
deleted file mode 100644 (file)
index 8fdbcef..0000000
Binary files a/models/player/gakarmored_lod2.iqm and /dev/null differ
diff --git a/models/player/gakarmored_lod2.iqm.framegroups b/models/player/gakarmored_lod2.iqm.framegroups
deleted file mode 100644 (file)
index 98eabd7..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-0 36 15.000000 0 // dieone
-36 20 15.000000 0 // dietwo
-56 15 15.000000 1 // draw
-71 20 15.000000 1 // duck
-91 21 30.000000 1 // duckwalk
-112 16 15.000000 0 // duckjump
-128 15 5.000000 1 // duckidle
-143 41 5.000000 1 // idle
-184 160 25.000000 0 // jump
-344 15 15.000000 0 // painone
-359 17 15.000000 0 // paintwo
-376 3 15.000000 0 // shoot
-379 21 15.000000 1 // taunt
-400 21 35.000000 1 // run
-421 21 35.000000 1 // runbackwards
-442 21 35.000000 1 // strafeleft
-463 21 35.000000 1 // straferight
-484 2 15.000000 0 // deadone
-486 2 15.000000 0 // deadtwo
-488 21 35.000000 1 // forwardright
-509 21 35.000000 1 // forwardleft
-530 21 35.000000 1 // backright
-551 21 30.000000 1 // backleft
-572 21 20.000000 0 // melee
-593 21 30.000000 1 // duckwalkbackwards
-91 21 30.000000 1 // duckwalkstrafeleft
-91 21 30.000000 1 // duckwalkstraferight
-91 21 30.000000 1 // duckwalkforwardright
-91 21 30.000000 1 // duckwalkforwardleft
-593 21 30.000000 1 // duckwalkbackright
-593 21 30.000000 1 // duckwalkbackleft
\ No newline at end of file
diff --git a/models/player/gakarmored_lod2.iqm_0.skin b/models/player/gakarmored_lod2.iqm_0.skin
deleted file mode 100644 (file)
index 1e8d3e9..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-gak1.001,gak
-gak1,gakarmor
diff --git a/models/player/gakarmored_lod2.iqm_1.skin b/models/player/gakarmored_lod2.iqm_1.skin
deleted file mode 100644 (file)
index 3377bcb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-gak1.001,gakfullbright
-gak1,gakarmorfb
diff --git a/models/player/ignishalfmasked.iqm b/models/player/ignishalfmasked.iqm
deleted file mode 100644 (file)
index 7b11d80..0000000
Binary files a/models/player/ignishalfmasked.iqm and /dev/null differ
diff --git a/models/player/ignishalfmasked.iqm.framegroups b/models/player/ignishalfmasked.iqm.framegroups
deleted file mode 100644 (file)
index 6432971..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-0 36 30.000000 0 // dieone
-36 20 25.000000 0 // dietwo
-56 15 30.000000 1 // draw
-71 20 15.000000 1 // duck
-91 20 32.000000 1 // duckwalk
-112 16 15.000000 0 // duckjump
-128 15 10.000000 1 // duckidle
-143 41 5.000000 1 // idle
-184 160 25.000000 0 // jump
-344 15 24.000000 0 // painone
-359 17 34.000000 0 // paintwo
-376 3 3.000000 0 // shoot
-379 21 15.000000 1 // taunt
-400 20 29.000000 1 // run
-421 20 29.000000 1 // runbackwards
-442 20 29.000000 1 // strafeleft
-463 20 29.000000 1 // straferight
-484 2 15.000000 0 // deadone
-486 2 15.000000 0 // deadtwo
-488 20 29.000000 1 // forwardright
-509 20 29.000000 1 // forwardleft
-530 20 29.000000 1 // backright
-551 20 29.000000 1 // backleft
-572 21 20.000000 0 // melee
-593 20 32.000000 1 // duckwalkbackwards
-614 20 32.000000 1 // duckwalkstrafeleft
-635 20 32.000000 1 // duckwalkstraferight
-656 20 32.000000 1 // duckwalkforwardright
-677 20 32.000000 1 // duckwalkforwardleft
-698 20 32.000000 1 // duckwalkbackright
-719 20 32.000000 1 // duckwalkbackleft
diff --git a/models/player/ignishalfmasked.iqm_0.skin b/models/player/ignishalfmasked.iqm_0.skin
deleted file mode 100644 (file)
index 9523f9e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ignis1,ignis
-ignis2.001,ignishead
diff --git a/models/player/ignishalfmasked.iqm_0.sounds b/models/player/ignishalfmasked.iqm_0.sounds
deleted file mode 100644 (file)
index 952ba1c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-//TAG: insurrectionist
-//affirmative sound/player/carni-lycan/player/affirmative 0
-attack sound/player/insurrectionist/coms/attack 0
-//attacking sound/player/carni-lycan/player/attacking 0
-attackinfive sound/player/insurrectionist/coms/attackinfive 0
-coverme sound/player/insurrectionist/coms/coverme 0
-defend sound/player/insurrectionist/coms/defend 0
-//defending sound/player/carni-lycan/player/defending 0
-//droppedflag sound/player/carni-lycan/player/droppedflag 0
-//flagcarriertakingdamage sound/player/soldier/player/flagcarriertakingdamage 0
-freelance sound/player/insurrectionist/coms/freelance 2
-//getflag sound/player/soldier/player/getflag 0
-incoming sound/player/insurrectionist/coms/incoming 0
-meet sound/player/insurrectionist/coms/meet 0
-needhelp sound/player/insurrectionist/coms/needhelp 2
-//negative sound/player/carni-lycan/player/negative 0
-//onmyway sound/player/carni-lycan/player/onmyway 0
-//roaming sound/player/carni-lycan/player/roaming 0
-//seenenemy sound/player/carni-lycan/player/seenenemy 0
-seenflag sound/player/insurrectionist/coms/seenflag 0
-taunt sound/player/insurrectionist/coms/taunt 4
-teamshoot sound/player/insurrectionist/coms/teamshoot 3
-death sound/player/insurrectionist/player/death 3
-drown sound/player/insurrectionist/player/drown 0
-fall sound/player/insurrectionist/player/fall 0
-falling sound/player/insurrectionist/player/falling 0
-gasp sound/player/insurrectionist/player/gasp 0
-jump sound/player/insurrectionist/player/jump 0
-pain25 sound/player/insurrectionist/player/pain25 0
-pain50 sound/player/insurrectionist/player/pain50 0
-pain75 sound/player/insurrectionist/player/pain75 0
-pain100 sound/player/insurrectionist/player/pain100 0
diff --git a/models/player/ignishalfmasked.iqm_0.tga b/models/player/ignishalfmasked.iqm_0.tga
deleted file mode 100644 (file)
index 487a131..0000000
Binary files a/models/player/ignishalfmasked.iqm_0.tga and /dev/null differ
diff --git a/models/player/ignishalfmasked.iqm_0.txt b/models/player/ignishalfmasked.iqm_0.txt
deleted file mode 100644 (file)
index 01c99ad..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-name Ignis - Half Masked
-species human
-sex Male
-weight 90
-age 31
-description Heavyweight Xonotic Soldier
-bone_upperbody spine2
-bone_aim0 0.25 spine2
-bone_aim1 0.4 spine4
-bone_aim2 0.2 upperarm_L
-bone_aim3 0.35 bip01 r hand
-bone_weapon bip01 r hand
-fixbone 1
diff --git a/models/player/ignishalfmasked.iqm_1.skin b/models/player/ignishalfmasked.iqm_1.skin
deleted file mode 100644 (file)
index 89a0020..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ignis1,ignisfullbright
-ignis2.001,ignishead
diff --git a/models/player/ignishalfmasked_lod1.iqm b/models/player/ignishalfmasked_lod1.iqm
deleted file mode 100644 (file)
index e3877a7..0000000
Binary files a/models/player/ignishalfmasked_lod1.iqm and /dev/null differ
diff --git a/models/player/ignishalfmasked_lod1.iqm.framegroups b/models/player/ignishalfmasked_lod1.iqm.framegroups
deleted file mode 100644 (file)
index 6432971..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-0 36 30.000000 0 // dieone
-36 20 25.000000 0 // dietwo
-56 15 30.000000 1 // draw
-71 20 15.000000 1 // duck
-91 20 32.000000 1 // duckwalk
-112 16 15.000000 0 // duckjump
-128 15 10.000000 1 // duckidle
-143 41 5.000000 1 // idle
-184 160 25.000000 0 // jump
-344 15 24.000000 0 // painone
-359 17 34.000000 0 // paintwo
-376 3 3.000000 0 // shoot
-379 21 15.000000 1 // taunt
-400 20 29.000000 1 // run
-421 20 29.000000 1 // runbackwards
-442 20 29.000000 1 // strafeleft
-463 20 29.000000 1 // straferight
-484 2 15.000000 0 // deadone
-486 2 15.000000 0 // deadtwo
-488 20 29.000000 1 // forwardright
-509 20 29.000000 1 // forwardleft
-530 20 29.000000 1 // backright
-551 20 29.000000 1 // backleft
-572 21 20.000000 0 // melee
-593 20 32.000000 1 // duckwalkbackwards
-614 20 32.000000 1 // duckwalkstrafeleft
-635 20 32.000000 1 // duckwalkstraferight
-656 20 32.000000 1 // duckwalkforwardright
-677 20 32.000000 1 // duckwalkforwardleft
-698 20 32.000000 1 // duckwalkbackright
-719 20 32.000000 1 // duckwalkbackleft
diff --git a/models/player/ignishalfmasked_lod1.iqm_0.skin b/models/player/ignishalfmasked_lod1.iqm_0.skin
deleted file mode 100644 (file)
index 9523f9e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ignis1,ignis
-ignis2.001,ignishead
diff --git a/models/player/ignishalfmasked_lod1.iqm_1.skin b/models/player/ignishalfmasked_lod1.iqm_1.skin
deleted file mode 100644 (file)
index 89a0020..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ignis1,ignisfullbright
-ignis2.001,ignishead
diff --git a/models/player/ignishalfmasked_lod2.iqm b/models/player/ignishalfmasked_lod2.iqm
deleted file mode 100644 (file)
index 48b0696..0000000
Binary files a/models/player/ignishalfmasked_lod2.iqm and /dev/null differ
diff --git a/models/player/ignishalfmasked_lod2.iqm.framegroups b/models/player/ignishalfmasked_lod2.iqm.framegroups
deleted file mode 100644 (file)
index 6432971..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-0 36 30.000000 0 // dieone
-36 20 25.000000 0 // dietwo
-56 15 30.000000 1 // draw
-71 20 15.000000 1 // duck
-91 20 32.000000 1 // duckwalk
-112 16 15.000000 0 // duckjump
-128 15 10.000000 1 // duckidle
-143 41 5.000000 1 // idle
-184 160 25.000000 0 // jump
-344 15 24.000000 0 // painone
-359 17 34.000000 0 // paintwo
-376 3 3.000000 0 // shoot
-379 21 15.000000 1 // taunt
-400 20 29.000000 1 // run
-421 20 29.000000 1 // runbackwards
-442 20 29.000000 1 // strafeleft
-463 20 29.000000 1 // straferight
-484 2 15.000000 0 // deadone
-486 2 15.000000 0 // deadtwo
-488 20 29.000000 1 // forwardright
-509 20 29.000000 1 // forwardleft
-530 20 29.000000 1 // backright
-551 20 29.000000 1 // backleft
-572 21 20.000000 0 // melee
-593 20 32.000000 1 // duckwalkbackwards
-614 20 32.000000 1 // duckwalkstrafeleft
-635 20 32.000000 1 // duckwalkstraferight
-656 20 32.000000 1 // duckwalkforwardright
-677 20 32.000000 1 // duckwalkforwardleft
-698 20 32.000000 1 // duckwalkbackright
-719 20 32.000000 1 // duckwalkbackleft
diff --git a/models/player/ignishalfmasked_lod2.iqm_0.skin b/models/player/ignishalfmasked_lod2.iqm_0.skin
deleted file mode 100644 (file)
index 9523f9e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ignis1,ignis
-ignis2.001,ignishead
diff --git a/models/player/ignishalfmasked_lod2.iqm_1.skin b/models/player/ignishalfmasked_lod2.iqm_1.skin
deleted file mode 100644 (file)
index 89a0020..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-ignis1,ignisfullbright
-ignis2.001,ignishead
index f75a15fb8ef55b3cf7b06abeaec7ba451ae9ab9f..f4b62dea70bce940d29eb41f959c5e349580dd6f 100644 (file)
@@ -11,3 +11,4 @@ bone_aim2 0.2 upperarm_L
 bone_aim3 0.35 bip01 r hand
 bone_weapon bip01 r hand
 fixbone 1
+hidden 1
index 1932354e8078c3f24947504da7bbb93390c5a51a..7533a4c53adab086fe9866c766df919d631666f7 100644 (file)
@@ -1,2 +1,2 @@
 pyria_obj.001,pyriahair
-pyria_obj,pyriafullbright
+pyria_obj,pyria
index 1932354e8078c3f24947504da7bbb93390c5a51a..7533a4c53adab086fe9866c766df919d631666f7 100644 (file)
@@ -1,2 +1,2 @@
 pyria_obj.001,pyriahair
-pyria_obj,pyriafullbright
+pyria_obj,pyria
index 1932354e8078c3f24947504da7bbb93390c5a51a..7533a4c53adab086fe9866c766df919d631666f7 100644 (file)
@@ -1,2 +1,2 @@
 pyria_obj.001,pyriahair
-pyria_obj,pyriafullbright
+pyria_obj,pyria
index 7b94ebe4a4e61e1d7e0803fb86a91a60fb3ecf4e..7138ad587e31e81451b941144041fa5ffec81ec0 100644 (file)
@@ -1,2 +1,2 @@
-ignis42,ignisfullbright
+ignis42,ignis
 ignis42.002,ignishead
index 7b94ebe4a4e61e1d7e0803fb86a91a60fb3ecf4e..7138ad587e31e81451b941144041fa5ffec81ec0 100644 (file)
@@ -1,2 +1,2 @@
-ignis42,ignisfullbright
+ignis42,ignis
 ignis42.002,ignishead
index 7b94ebe4a4e61e1d7e0803fb86a91a60fb3ecf4e..7138ad587e31e81451b941144041fa5ffec81ec0 100644 (file)
@@ -1,2 +1,2 @@
-ignis42,ignisfullbright
+ignis42,ignis
 ignis42.002,ignishead
index c953426724ec3ba219dd00b6a5659e5fa837254d..b1463090f7d2d5783b86cf2c9c032b089c5e2f56 100644 (file)
Binary files a/models/weapons/g_fireball.md3 and b/models/weapons/g_fireball.md3 differ
diff --git a/models/weapons/g_ok_hmg_luma.iqm b/models/weapons/g_ok_hmg_luma.iqm
new file mode 100644 (file)
index 0000000..a5cc3e6
Binary files /dev/null and b/models/weapons/g_ok_hmg_luma.iqm differ
diff --git a/models/weapons/g_ok_hmg_luma.iqm_0.skin b/models/weapons/g_ok_hmg_luma.iqm_0.skin
new file mode 100644 (file)
index 0000000..2c77f8e
--- /dev/null
@@ -0,0 +1 @@
+Plane,g_ok_hmg_luma
diff --git a/models/weapons/g_ok_hmg_luma.tga b/models/weapons/g_ok_hmg_luma.tga
new file mode 100644 (file)
index 0000000..5f8e135
Binary files /dev/null and b/models/weapons/g_ok_hmg_luma.tga differ
diff --git a/models/weapons/g_ok_hmg_simple.iqm b/models/weapons/g_ok_hmg_simple.iqm
new file mode 100644 (file)
index 0000000..a5cc3e6
Binary files /dev/null and b/models/weapons/g_ok_hmg_simple.iqm differ
diff --git a/models/weapons/g_ok_hmg_simple.iqm_0.skin b/models/weapons/g_ok_hmg_simple.iqm_0.skin
new file mode 100644 (file)
index 0000000..f4316a5
--- /dev/null
@@ -0,0 +1 @@
+Plane,g_ok_hmg_simple
\ No newline at end of file
diff --git a/models/weapons/g_ok_hmg_simple.tga b/models/weapons/g_ok_hmg_simple.tga
new file mode 100644 (file)
index 0000000..c811a59
Binary files /dev/null and b/models/weapons/g_ok_hmg_simple.tga differ
diff --git a/models/weapons/g_ok_rl_luma.iqm b/models/weapons/g_ok_rl_luma.iqm
new file mode 100644 (file)
index 0000000..a5cc3e6
Binary files /dev/null and b/models/weapons/g_ok_rl_luma.iqm differ
diff --git a/models/weapons/g_ok_rl_luma.iqm_0.skin b/models/weapons/g_ok_rl_luma.iqm_0.skin
new file mode 100644 (file)
index 0000000..502ff5b
--- /dev/null
@@ -0,0 +1 @@
+Plane,g_ok_rl_luma
diff --git a/models/weapons/g_ok_rl_luma.tga b/models/weapons/g_ok_rl_luma.tga
new file mode 100644 (file)
index 0000000..b2471c8
Binary files /dev/null and b/models/weapons/g_ok_rl_luma.tga differ
diff --git a/models/weapons/g_ok_rl_simple.iqm b/models/weapons/g_ok_rl_simple.iqm
new file mode 100644 (file)
index 0000000..a5cc3e6
Binary files /dev/null and b/models/weapons/g_ok_rl_simple.iqm differ
diff --git a/models/weapons/g_ok_rl_simple.iqm_0.skin b/models/weapons/g_ok_rl_simple.iqm_0.skin
new file mode 100644 (file)
index 0000000..804fe52
--- /dev/null
@@ -0,0 +1 @@
+Plane,g_ok_rl_simple
\ No newline at end of file
diff --git a/models/weapons/g_ok_rl_simple.tga b/models/weapons/g_ok_rl_simple.tga
new file mode 100644 (file)
index 0000000..53f1be5
Binary files /dev/null and b/models/weapons/g_ok_rl_simple.tga differ
index 40a6b06a8341072f8d3a99f40e044535459ff544..3a9db00cedc20461beaf21a73e8527ec8e0b0d43 100644 (file)
Binary files a/models/weapons/h_fireball.iqm and b/models/weapons/h_fireball.iqm differ
index a5e847c37b375d7222611e30f63d6ce38fbfccd3..18fbf7489035b48c291686663142d718c2376058 100644 (file)
Binary files a/models/weapons/v_fireball.md3 and b/models/weapons/v_fireball.md3 differ
index f6eca00d004e4196ffff60499b30a5ab44bed257..ac6039041f5f3e98f492f8263ce48b6a490b901e 100644 (file)
@@ -88,11 +88,13 @@ set g_monster_shambler_speed_walk 150
 // {{{ Misc
 set g_monsters 1
 set g_monsters_edit 0
-set g_monsters_think_delay 0.1
+set g_monsters_think_delay 0.03333
 set g_monsters_skill 1 "Monster skill (affecting some of their attributes). 1 - easy, 2 - medium, 3 - hard, 4 - insane, 5 - nightmare"
 set g_monsters_miniboss_chance 5
 set g_monsters_miniboss_healthboost 100
 set g_monsters_drop_time 10
+set g_monsters_ignoretraces 1
+set g_monsters_lineofsight 1
 set g_monsters_owners 1
 set g_monsters_teams 1
 set g_monsters_score_kill 0
@@ -109,4 +111,7 @@ set g_monsters_respawn_delay 20
 set g_monsters_max 20
 set g_monsters_max_perplayer 0
 set g_monsters_armor_blockpercent 0.5
+set g_monsters_damageforcescale 0.8
+set g_monsters_quake_resize 1
+set g_monsters_healthbars 0
 // }}}
index 7910786107c1685dd05f2e896ea65d976a870607..cd6a9e231a2598dec4472d8b5e4b64da40bbc2eb 100644 (file)
@@ -10,7 +10,8 @@ set g_dodging 0 "set to 1 to enable dodging in games"
 
 seta cl_dodging_timeout 0.2 "determines how long apart (in seconds) two taps on the same direction key are considered a dodge. use 0 to disable"
 
-set sv_dodging_wall_dodging 0 "set to 1 to allow dodging off walls. 0 to disable"
+set sv_dodging_air_dodging 0
+set sv_dodging_wall_dodging 0 "allow dodging off walls"
 set sv_dodging_delay 0.7 "determines how long a player has to wait to be able to dodge again after dodging"
 set sv_dodging_up_speed 200 "the jump velocity of the dodge"
 set sv_dodging_horiz_speed 400 "the horizontal velocity of the dodge"
@@ -20,6 +21,7 @@ set sv_dodging_height_threshold 10 "the maximum height above ground where to all
 set sv_dodging_wall_distance_threshold 10 "the maximum distance from a wall that still allows dodging"
 set sv_dodging_sound 1 "if 1 dodging makes a sound. if 0 dodging is silent"
 set sv_dodging_frozen 0 "allow dodging while frozen"
+set sv_dodging_frozen_doubletap 0
 
 
 // ===========
@@ -29,6 +31,10 @@ set g_instagib 0 "enable instagib"
 set g_instagib_extralives 1 "how many extra lives you will get per powerup"
 set g_instagib_ammo_start 10 "starting ammo"
 set g_instagib_ammo_drop 5 "how much ammo you'll get for weapons or cells"
+set g_instagib_ammo_convert_bullets 0 "convert bullet ammo packs to insta cell ammo packs"
+set g_instagib_ammo_convert_cells 0 "convert normal cell ammo packs to insta cell ammo packs"
+set g_instagib_ammo_convert_rockets 0 "convert rocket ammo packs to insta cell ammo packs"
+set g_instagib_ammo_convert_shells 0 "convert shell ammo packs to insta cell ammo packs"
 set g_instagib_invis_alpha 0.15
 set g_instagib_speed_highspeed 1.5 "speed-multiplier that applies while you carry the invincibility powerup"
 set g_instagib_damagedbycontents 1 "allow damage from lava pits in instagib"
@@ -43,10 +49,11 @@ set g_instagib_friendlypush 1 "allow pushing teammates with the vaporizer primar
 // ==========
 set g_overkill 0 "enable overkill"
 
-set g_overkill_100a_anyway 1
-set g_overkill_100h_anyway 1
 set g_overkill_powerups_replace 1
-set g_overkill_superguns_respawn_time 120
+set g_overkill_filter_healthmega 0
+set g_overkill_filter_armormedium 0
+set g_overkill_filter_armorbig 0
+set g_overkill_filter_armormega 0
 
 set g_overkill_ammo_charge 0
 set g_overkill_ammo_charge_notice 1
@@ -118,8 +125,9 @@ seta cl_spawn_near_teammate 1 "toggle for spawning near teammates (only effectiv
 set g_spawn_near_teammate 0 "if set, players prefer spawns near a team mate"
 set g_spawn_near_teammate_distance 640 "max distance to consider a spawn to be near a team mate"
 set g_spawn_near_teammate_ignore_spawnpoint 0 "ignore spawnpoints and spawn right at team mates, if 2, clients can ignore this option"
+set g_spawn_near_teammate_ignore_spawnpoint_max 10 "if set, test at most this many of the available teammates"
 set g_spawn_near_teammate_ignore_spawnpoint_delay 2.5 "how long to wait before its OK to spawn at a player after someone just spawned at this player"
-set g_spawn_near_teammate_ignore_spawnpoint_delay_death 0 "how long to wait before its OK to spawn at a player after death"
+set g_spawn_near_teammate_ignore_spawnpoint_delay_death 3 "how long to wait before its OK to spawn at a player after death"
 set g_spawn_near_teammate_ignore_spawnpoint_check_health 1 "only allow spawn at this player if their health is full"
 set g_spawn_near_teammate_ignore_spawnpoint_closetodeath 1 "spawn as close to death location as possible"
 
@@ -187,6 +195,7 @@ set g_nades_spawn 1 "give nades right away when player spawns rather than delayi
 set g_nades_client_select 0 "allow client side selection of nade type"
 set g_nades_pickup 0 "allow picking up thrown nades (not your own)"
 set g_nades_pickup_time 2 "time until picked up nade explodes"
+set g_nades_override_dropweapon 1
 set g_nades_nade_lifetime 3.5
 set g_nades_nade_minforce 400
 set g_nades_nade_maxforce 2000
@@ -295,15 +304,15 @@ set g_new_toys_use_pickupsound 1 "play the 'new toys, new toys!' roflsound when
 //  buffs
 // =======
 set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another"
-set g_buffs 0 "enable buffs (requires buff items or powerups)"
+set g_buffs -1 "enable buffs (requires buff items or powerups)"
 set g_buffs_effects 1 "show particle effects from carried buffs"
 set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item"
 set g_buffs_randomize 1 "randomize buff type when player drops buff"
 set g_buffs_random_lifetime 30 "re-spawn the buff again if it hasn't been touched after this time in seconds"
 set g_buffs_random_location 0 "randomize buff location on start and when reset"
 set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up"
-set g_buffs_spawn_count 5 "how many buffs to spawn on the map if none exist already"
-set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs"
+set g_buffs_spawn_count 0 "how many buffs to spawn on the map if none exist already"
+set g_buffs_replace_powerups 0 "replace powerups on the map with random buffs"
 set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated"
 set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading"
 set g_buffs_ammo 1 "ammo buff: infinite ammunition"
@@ -368,6 +377,8 @@ set g_buffs_luck 1 "luck buff: randomly increased damage"
 set g_buffs_luck_time 60 "luck buff carry time"
 set g_buffs_luck_chance 0.15 "chance for 'critical' hit (multiplied damage) with luck buff"
 set g_buffs_luck_damagemultiplier 3 "luck damage multiplier"
+set g_buffs_flight 0 "flight buff: crouch jump to reverse your gravity!"
+set g_buffs_flight_time 60 "flight buff carry time"
 
 
 // ==============
@@ -419,3 +430,24 @@ seta cl_multijump 1 "allow multijump mutator"
 set g_multijump 0      "Number of multiple jumps to allow (jumping again in the air), -1 allows for infinite jumps"
 set g_multijump_add 0  "0 = make the current z velocity equal to jumpvelocity, 1 = add jumpvelocity to the current z velocity"
 set g_multijump_speed -999999  "Minimum vertical speed a player must have in order to jump again"
+set g_multijump_maxspeed 0
+set g_multijump_dodging 1
+
+
+// ===========
+//  wall jump
+// ===========
+set g_walljump 0 "Enable wall jumping mutator"
+set g_walljump_delay 1 "Minimum delay between wall jumps"
+set g_walljump_force 300 "How far to bounce/jump off the wall"
+set g_walljump_velocity_xy_factor 1.15 "How much to slow down along horizontal axis, higher value = higher deceleration, if factor is < 1, you accelerate by wall jumping"
+set g_walljump_velocity_z_factor 0.5 "Upwards velocity factor, multiplied by normal jump velocity"
+
+
+// ===============
+//  global forces
+// ===============
+set g_globalforces 0 "Global forces: knockback affects everyone"
+set g_globalforces_noself 1 "Global forces: ignore self damage"
+set g_globalforces_self 1 "Global forces: knockback self scale"
+set g_globalforces_range 1000 "Global forces: max range of effect"
index 2ee0a4084e51df9668f970647cf2d1cbe0faa406..8ca401bb423f43be3e960a9b23cd2215f889ed9a 100644 (file)
@@ -102,7 +102,7 @@ seta notification_ANNCE_VOTE_ACCEPT "2" "0 = disabled, 1 = enabled if gentle mod
 seta notification_ANNCE_VOTE_CALL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
 seta notification_ANNCE_VOTE_FAIL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
 
-// MSG_INFO notifications (count = 316):
+// MSG_INFO notifications (count = 320):
 seta notification_INFO_CA_JOIN_LATE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_CA_LEAVE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_CHAT_NOSPECTATORS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@ -311,6 +311,10 @@ seta notification_INFO_LMS_FORFEIT "1" "0 = off, 1 = print to console, 2 = print
 seta notification_INFO_LMS_NOLIVES "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_MINIGAME_INVITE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_MONSTERS_DISABLED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_NEXBALL_RETURN_HELD_BLUE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_NEXBALL_RETURN_HELD_PINK "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_NEXBALL_RETURN_HELD_RED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_NEXBALL_RETURN_HELD_YELLOW "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_ONSLAUGHT_CAPTURE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_ONSLAUGHT_CPDESTROYED_BLUE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_ONSLAUGHT_CPDESTROYED_PINK "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@ -420,10 +424,11 @@ seta notification_INFO_WEAPON_TUBA_SUICIDE "1" "0 = off, 1 = print to console, 2
 seta notification_INFO_WEAPON_VAPORIZER_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_WEAPON_VORTEX_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 
-// MSG_CENTER notifications (count = 224):
+// MSG_CENTER notifications (count = 231):
 seta notification_CENTER_ALONE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_ASSAULT_ATTACKING "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_ASSAULT_DEFENDING "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_ASSAULT_OBJ_DESTROYED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CAMPCHECK "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_COINTOSS "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_COUNTDOWN_BEGIN "1" "0 = off, 1 = centerprint"
@@ -478,6 +483,7 @@ seta notification_CENTER_CTF_PICKUP_TEAM_VERBOSE_PINK "1" "0 = off, 1 = centerpr
 seta notification_CENTER_CTF_PICKUP_TEAM_VERBOSE_RED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CTF_PICKUP_TEAM_VERBOSE_YELLOW "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CTF_PICKUP_TEAM_YELLOW "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_CTF_PICKUP_VISIBLE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CTF_PICKUP_YELLOW "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CTF_RETURN_BLUE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CTF_RETURN_PINK "1" "0 = off, 1 = centerprint"
@@ -487,7 +493,15 @@ seta notification_CENTER_CTF_STALEMATE_CARRIER "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CTF_STALEMATE_OTHER "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DEATH_MURDER_FRAG "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DEATH_MURDER_FRAGGED "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAGGED_FIRE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAGGED_FIRE_VERBOSE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAGGED_FREEZE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAGGED_FREEZE_VERBOSE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DEATH_MURDER_FRAGGED_VERBOSE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAG_FIRE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAG_FIRE_VERBOSE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAG_FREEZE "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_DEATH_MURDER_FRAG_FREEZE_VERBOSE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DEATH_MURDER_FRAG_VERBOSE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DEATH_MURDER_TYPEFRAG "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DEATH_MURDER_TYPEFRAGGED "1" "0 = off, 1 = centerprint"
@@ -536,8 +550,6 @@ seta notification_CENTER_DOOR_LOCKED_NEED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_DOOR_UNLOCKED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_EXTRALIVES "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_FREEZETAG_AUTO_REVIVED "1" "0 = off, 1 = centerprint"
-seta notification_CENTER_FREEZETAG_FREEZE "1" "0 = off, 1 = centerprint"
-seta notification_CENTER_FREEZETAG_FROZEN "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_FREEZETAG_REVIVE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_FREEZETAG_REVIVED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_FREEZETAG_REVIVE_SELF "1" "0 = off, 1 = centerprint"
@@ -582,7 +594,6 @@ seta notification_CENTER_LMS_NOLIVES "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_MISSING_PLAYERS "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_MISSING_TEAMS "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_MOTD "1" "0 = off, 1 = centerprint"
-seta notification_CENTER_NADE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_NADE_BONUS "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_NADE_THROW "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_NIX_COUNTDOWN "1" "0 = off, 1 = centerprint"
@@ -801,7 +812,7 @@ seta notification_WEAPON_TUBA_SUICIDE "1" "Enable this multiple notification"
 seta notification_WEAPON_VAPORIZER_MURDER "1" "Enable this multiple notification"
 seta notification_WEAPON_VORTEX_MURDER "1" "Enable this multiple notification"
 
-// MSG_CHOICE notifications (count = 24):
+// MSG_CHOICE notifications (count = 28):
 seta notification_CHOICE_CTF_CAPTURE_BROKEN_BLUE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
 seta notification_CHOICE_CTF_CAPTURE_BROKEN_BLUE_ALLOWED "2" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
 seta notification_CHOICE_CTF_CAPTURE_BROKEN_PINK "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
@@ -846,6 +857,14 @@ seta notification_CHOICE_FRAG "1" "Choice for this notification 0 = off, 1 = def
 seta notification_CHOICE_FRAG_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
 seta notification_CHOICE_FRAGGED "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
 seta notification_CHOICE_FRAGGED_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
+seta notification_CHOICE_FRAGGED_FIRE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
+seta notification_CHOICE_FRAGGED_FIRE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
+seta notification_CHOICE_FRAGGED_FREEZE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
+seta notification_CHOICE_FRAGGED_FREEZE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
+seta notification_CHOICE_FRAG_FIRE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
+seta notification_CHOICE_FRAG_FIRE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
+seta notification_CHOICE_FRAG_FREEZE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
+seta notification_CHOICE_FRAG_FREEZE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
 seta notification_CHOICE_TYPEFRAG "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
 seta notification_CHOICE_TYPEFRAG_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
 seta notification_CHOICE_TYPEFRAGGED "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message"
@@ -868,4 +887,4 @@ seta notification_show_sprees_info "3" "Show spree information in MSG_INFO messa
 seta notification_show_sprees_info_newline "1" "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
 seta notification_show_sprees_info_specialonly "1" "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
 
-// Notification counts (total = 806): MSG_ANNCE = 89, MSG_INFO = 316, MSG_CENTER = 224, MSG_MULTI = 153, MSG_CHOICE = 24
+// Notification counts (total = 821): MSG_ANNCE = 89, MSG_INFO = 320, MSG_CENTER = 231, MSG_MULTI = 153, MSG_CHOICE = 28
index e9fe5628940d9597fd5843533b54bced13066ae0..1a38a7f617ad8f82d439ea85062c08f64a30b886 100644 (file)
@@ -28,6 +28,7 @@ set g_physics_xonotic_airaccel_qw_stretchfactor 2
 set g_physics_xonotic_airaccel_sideways_friction 0
 set g_physics_xonotic_aircontrol 100
 set g_physics_xonotic_aircontrol_power 2
+set g_physics_xonotic_aircontrol_backwards 0
 set g_physics_xonotic_aircontrol_penalty 0
 set g_physics_xonotic_warsowbunny_airforwardaccel 1.00001
 set g_physics_xonotic_warsowbunny_topspeed 925
@@ -56,6 +57,7 @@ set g_physics_nexuiz_airaccel_qw_stretchfactor 0
 set g_physics_nexuiz_airaccel_sideways_friction 0.35
 set g_physics_nexuiz_aircontrol 0
 set g_physics_nexuiz_aircontrol_power 2
+set g_physics_nexuiz_aircontrol_backwards 0
 set g_physics_nexuiz_aircontrol_penalty 0
 set g_physics_nexuiz_warsowbunny_airforwardaccel 1.00001
 set g_physics_nexuiz_warsowbunny_topspeed 925
@@ -84,6 +86,7 @@ set g_physics_quake_airaccel_qw_stretchfactor 0
 set g_physics_quake_airaccel_sideways_friction 0
 set g_physics_quake_aircontrol 0
 set g_physics_quake_aircontrol_power 2
+set g_physics_quake_aircontrol_backwards 0
 set g_physics_quake_aircontrol_penalty 0
 set g_physics_quake_warsowbunny_airforwardaccel 1.00001
 set g_physics_quake_warsowbunny_topspeed 925
@@ -112,6 +115,7 @@ set g_physics_warsow_airaccel_qw_stretchfactor 0
 set g_physics_warsow_airaccel_sideways_friction 0
 set g_physics_warsow_aircontrol 0
 set g_physics_warsow_aircontrol_power 2
+set g_physics_warsow_aircontrol_backwards 0
 set g_physics_warsow_aircontrol_penalty 0
 set g_physics_warsow_warsowbunny_airforwardaccel 1.00001
 set g_physics_warsow_warsowbunny_topspeed 925
@@ -140,6 +144,7 @@ set g_physics_defrag_airaccel_qw_stretchfactor 0
 set g_physics_defrag_airaccel_sideways_friction 0
 set g_physics_defrag_aircontrol 150
 set g_physics_defrag_aircontrol_power 2
+set g_physics_defrag_aircontrol_backwards 0
 set g_physics_defrag_aircontrol_penalty 0
 set g_physics_defrag_warsowbunny_airforwardaccel 1.00001
 set g_physics_defrag_warsowbunny_topspeed 925
@@ -168,6 +173,7 @@ set g_physics_quake3_airaccel_qw_stretchfactor 0
 set g_physics_quake3_airaccel_sideways_friction 0
 set g_physics_quake3_aircontrol 0
 set g_physics_quake3_aircontrol_power 2
+set g_physics_quake3_aircontrol_backwards 0
 set g_physics_quake3_aircontrol_penalty 0
 set g_physics_quake3_warsowbunny_airforwardaccel 1.00001
 set g_physics_quake3_warsowbunny_topspeed 925
@@ -196,6 +202,7 @@ set g_physics_vecxis_airaccel_qw_stretchfactor 0
 set g_physics_vecxis_airaccel_sideways_friction 0.3
 set g_physics_vecxis_aircontrol 0
 set g_physics_vecxis_aircontrol_power 2
+set g_physics_vecxis_aircontrol_backwards 0
 set g_physics_vecxis_aircontrol_penalty 0
 set g_physics_vecxis_warsowbunny_airforwardaccel 1.00001
 set g_physics_vecxis_warsowbunny_topspeed 925
@@ -224,6 +231,7 @@ set g_physics_quake2_airaccel_qw_stretchfactor 0
 set g_physics_quake2_airaccel_sideways_friction 0
 set g_physics_quake2_aircontrol 0
 set g_physics_quake2_aircontrol_power 2
+set g_physics_quake2_aircontrol_backwards 0
 set g_physics_quake2_aircontrol_penalty 0
 set g_physics_quake2_warsowbunny_airforwardaccel 1.00001
 set g_physics_quake2_warsowbunny_topspeed 925
@@ -252,6 +260,7 @@ set g_physics_bones_airaccel_qw_stretchfactor 0
 set g_physics_bones_airaccel_sideways_friction 0
 set g_physics_bones_aircontrol 150
 set g_physics_bones_aircontrol_power 2
+set g_physics_bones_aircontrol_backwards 0
 set g_physics_bones_aircontrol_penalty 0
 set g_physics_bones_warsowbunny_airforwardaccel 1.00001
 set g_physics_bones_warsowbunny_topspeed 925
@@ -263,3 +272,32 @@ set g_physics_bones_stopspeed 100
 set g_physics_bones_airaccelerate 1
 set g_physics_bones_airstopaccelerate 2.5
 set g_physics_bones_track_canjump 0
+
+// ==========
+//  Overkill
+// ==========
+set g_physics_overkill_airaccel_qw -0.8
+set g_physics_overkill_airstrafeaccel_qw -0.95
+set g_physics_overkill_airspeedlimit_nonqw 900
+set g_physics_overkill_maxspeed 400
+set g_physics_overkill_jumpvelocity 260
+set g_physics_overkill_maxairstrafespeed 100
+set g_physics_overkill_maxairspeed 360
+set g_physics_overkill_airstrafeaccelerate 24
+set g_physics_overkill_warsowbunny_turnaccel 0
+set g_physics_overkill_airaccel_qw_stretchfactor 2
+set g_physics_overkill_airaccel_sideways_friction 0
+set g_physics_overkill_aircontrol 125
+set g_physics_overkill_aircontrol_power 2
+set g_physics_overkill_aircontrol_backwards 0
+set g_physics_overkill_aircontrol_penalty 180
+set g_physics_overkill_warsowbunny_airforwardaccel 1.00001
+set g_physics_overkill_warsowbunny_topspeed 925
+set g_physics_overkill_warsowbunny_accel 0.1593
+set g_physics_overkill_warsowbunny_backtosideratio 0.8
+set g_physics_overkill_friction 8
+set g_physics_overkill_accelerate 15
+set g_physics_overkill_stopspeed 100
+set g_physics_overkill_airaccelerate 2
+set g_physics_overkill_airstopaccelerate 3
+set g_physics_overkill_track_canjump 0
index e4bbad3d48630aa95c9f3463fdd1d327c7323a12..f2a4209c25eb44ee435273856d0c87720d7c00de 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 150
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index ee44b5547a052717cde7f28fb0f52063e638c65f..efe5b46ac3854ad774019787a0391140e315de76 100644 (file)
@@ -22,6 +22,7 @@ sv_maxairstrafespeed 100
 sv_airstrafeaccel_qw -0.97
 sv_aircontrol 125
 sv_aircontrol_power 2.5
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 6aac341780e7573cbe580b714e62b747d61e6612..8d02ff46271260092d0fa52b2902adda3599965f 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 90eaa49c1223cd4bacf7aa7e228dc4670f88240b..61ce6557dfb15d7ac1b55d0128039c12d92e1507 100644 (file)
@@ -31,6 +31,7 @@ sv_airstrafeaccel_qw -0.97
 sv_aircontrol 125
 sv_aircontrol_penalty 150
 sv_aircontrol_power 2.5
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 800
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 03081fdab7554b9c5118b795ac8b86987ff966a6..53ec90148e32aa7b1819454f57cba876ea759958 100644 (file)
@@ -31,6 +31,7 @@ sv_airstrafeaccel_qw -0.95
 sv_aircontrol 125
 sv_aircontrol_penalty 150
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 800
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index ef213a70f9d356226b401c0779752d85d69c7157..6a24567a93e518f68b347c6292d2e962c4279130 100644 (file)
@@ -26,6 +26,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 51c49d7832dbf0197ca6a2ab5d456389ce675ebf..f6f74cd21a9b88eabd7f1c278cf6f4ad320b6c98 100644 (file)
@@ -24,6 +24,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 0ce33aa95132362f57bb28eeec904c6549bed91e..9292016afe5d483864082f9f87e0b01483635f70 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 08711fd5b0f6905b33ff2688261f0e0543611922..660e7b818323ce0b654d4a58a9743c88d8897809 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 092985cae51a00a87a76cf26a78519c9f5d3f127..2c2e94c5f4dd7042ca9e041ed2244485ff500237 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index c6d9bef4b912d82e5200691d82a3fb55627b4d88..45a46cb46e24711f5cb5055b144af9a989efb399 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 5e0ee6ae0c6f265690c3783be05c3d10f35a1d19..86f1c31c744679d6bd61a548f5882f1a038f8817 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index d129a2db05ebada4a9698aeafcfedbb9456ac2d9..fe461768851079b603981d8d90cd6d815be5a143 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 8a52299763d537423fb2012f6a9ed304c969ef0e..ed45598a9bb4a31039f5bf1804b2f1c61c9282b9 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 4df3f211ebe490134394e21d60a35a0b739fae6b..3f7cd58f18f35567b43d626ccb55a0e3cbed412f 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 3e905428f76d52f1183cde4fc116ccabc0268afa..77660a426a10abb2fa60168c63016f7e65eb5ef3 100644 (file)
@@ -32,6 +32,7 @@ sv_airstrafeaccel_qw -0.9825
 sv_aircontrol 125
 sv_aircontrol_penalty 100
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 448e087f46f20c1d0287dfd29c9a605a93266fcc..609a8f3d30f6d7829fc779c6147b8bce79134a36 100644 (file)
@@ -24,6 +24,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 124c50660a1046ca8bf9ce7245a4d9579a0ff71c..a9fbe166da3bbe3321a7741d5eda482c3507295b 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 580f6257fa65f2bde52c74983f72492e45456f2f..87817dfcfd46a275d968daf57005c1d627be36ae 100644 (file)
@@ -24,6 +24,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 6f00896b671b4b542115d6270c96b4bac7d08c20..7363b216dea7e8e34be9dc4871066baae213bc9c 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 31963e9feee4dc1dc6691514a081bde9d3765c7f..adc9df69c97af22d1644dab9d4cec72a78882982 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 82463859835480526e90bbae65db204238344fd7..596d6ca3b8ea78b2e524d60a423f56bc627b2f18 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 6858892e3b3ad27b1c8a8e8d4257c7a650e269c6..9041601e2fd601ea734d80b12440ea0a4d1ab728 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 50101d73d43165843ecf32f08b55fb33aff8fb2d..5fbe22bab998900fd02a8e7ff24c1cebe9b2eb8f 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 9 // activates warsow movement mode
 sv_warsowbunny_accel 0.1593
index c4c3535a7f9ac1c74d3daa7a7c7645f4daaa9e27..3b28d90999966f1cc73b4c04fbcbf639e4173f2b 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 150
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 331723ec9317539cbc2b2e4bdc139a99b52abb94..cf31a3f932790052f79df433b372a5f022cae33b 100644 (file)
@@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0
 sv_aircontrol 0
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 6 // activates warsow movement mode
 sv_warsowbunny_accel 0.1585
index c109e1157636e5b6fb91f658a0c8ba37910b922f..2ef0689755b7814c6913b67a73a623fb000cf42b 100644 (file)
@@ -17,7 +17,7 @@ sv_stepheight 31
 // Samual: 31 (just below 32, keeping things smooth without allowing 32qu steps)
 
 // jump duration == 2*sv_jumpvelocity / sv_gravity
-// in this case: 0.6888888888 (thus either 20 or 21 frames)
+// in this case: 0.65 (thus either 19 or 20 frames)
 // jump height == sv_jumpvelocity^2 / (2*sv_gravity)
 // in this case: 42.25
 // player: 24+45 qu
@@ -38,6 +38,7 @@ sv_airstrafeaccel_qw -0.95
 sv_aircontrol 100
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 900
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 6dadcf161bd1078d9b6b4227e84c6d9624b0f7e0..0dfe0b46c9970bf689e257c720dab1dd846ff9d2 100644 (file)
@@ -31,6 +31,7 @@ sv_airstrafeaccel_qw -0.95
 sv_aircontrol 125
 sv_aircontrol_penalty 150
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 800
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index a3e42a977eca1a69fc3ba438f7aa69f305466e34..132aa9eff68cec24fde30da5dfb24bfb2becc04f 100644 (file)
@@ -37,6 +37,7 @@ sv_airstrafeaccel_qw -0.95
 sv_aircontrol 125
 sv_aircontrol_penalty 180
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 800
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 8597e9e005d77020ea96c2375efa78ba56887b8c..5a3aaa393bd117eaf455bfe99342e4979adc1b98 100644 (file)
@@ -29,6 +29,7 @@ sv_airstrafeaccel_qw 1
 sv_aircontrol 150
 sv_aircontrol_penalty 0
 sv_aircontrol_power 2
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
index 6cb0780dd988ee2f7ffafed71d16e8348d0a8b4b..4d1f652bb81bbae8bfa7ba6c653d0300012cc5e5 100644 (file)
@@ -29,6 +29,7 @@ sv_airstrafeaccel_qw -0.987
 sv_aircontrol 100
 sv_aircontrol_penalty 100
 sv_aircontrol_power 2.5
+sv_aircontrol_backwards 0
 sv_airspeedlimit_nonqw 0
 sv_warsowbunny_turnaccel 0
 sv_warsowbunny_accel 0.1593
diff --git a/qcsrc/.editorconfig b/qcsrc/.editorconfig
new file mode 100644 (file)
index 0000000..013c331
--- /dev/null
@@ -0,0 +1,7 @@
+root = true
+
+[*.{qc,qh,inc}]
+end_of_line = lf
+insert_final_newline = true
+indent_style = tab
+charset = utf-8
index cb9ca2a883afc26501bec16c6ebcf17687241cfc..06ee7468ddd83bfbb93c83a15e0fe61a0d6ae45f 100644 (file)
@@ -1997,15 +1997,17 @@ INCLUDE_FILE_PATTERNS  =
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 PREDEFINED             = \
+    "XONOTIC" \
     "USING(name, T)=using name = T" \
     "CLASS(name, base)=class name : public base { public:" \
     "INIT(class)=class::class()" \
     "CONSTRUCTOR(class)=class::class(" \
     "DESTRUCTOR(class)=class::~class()" \
-    "ATTRIB(class, name, T, val)=T name = val;" \
-    "ATTRIB_STRZONE(class, name, T, val)=T name = val;" \
-    "STATIC_ATTRIB(class, name, T, val)=static T name = val;" \
-    "STATIC_ATTRIB_STRZONE(class, name, T, val)=static T name = val;" \
+    "ATTRIB(class, name, T, val)=T name = val" \
+    "ATTRIB_STRZONE(class, name, T, val)=T name = val" \
+    "ATTRIBARRAY(class, name, T, val)=T name[val]" \
+    "STATIC_ATTRIB(class, name, T, val)=static T name = val" \
+    "STATIC_ATTRIB_STRZONE(class, name, T, val)=static T name = val" \
     "METHOD(class, name, prototype)=virtual void class::name()" \
     "ENDCLASS(class)=};" \
     __STDC__
index ee335d9b0013817edf041d88082c9d8a8a9d2cea..903d8537e695bf35b33fb78889c814a38536cf7b 100644 (file)
@@ -7,7 +7,8 @@ WORKDIR ?= ../.tmp
 QCCFLAGS_WATERMARK ?= $(shell git describe --tags --dirty='~')
 VER = $(subst *,\*,$(QCCFLAGS_WATERMARK))
 NDEBUG ?= 1
-BUILD_MOD ?= 0
+XONOTIC ?= 1
+BUILD_MOD ?=
 
 ifndef ZIP
        ifneq ($(shell which zip),)
@@ -30,9 +31,10 @@ QCCFLAGS_WTFS ?= \
        -Wno-field-redeclared
 
 QCCDEFS ?= \
+       -DXONOTIC=$(XONOTIC) \
        -DWATERMARK="$(QCCFLAGS_WATERMARK)" \
        -DNDEBUG=$(NDEBUG) \
-       -DBUILD_MOD=$(BUILD_MOD) \
+       $(if $(BUILD_MOD), -DBUILD_MOD="$(BUILD_MOD)" -I$(BUILD_MOD), ) \
        $(QCCDEFS_EXTRA)
 
 # -Ooverlap-locals is required
diff --git a/qcsrc/client/_all.inc b/qcsrc/client/_all.inc
new file mode 100644 (file)
index 0000000..f592f8a
--- /dev/null
@@ -0,0 +1,20 @@
+#include <client/_all.qh>
+#include "_mod.inc"
+
+#include "commands/_mod.inc"
+#include "hud/_mod.inc"
+#include "mutators/_mod.inc"
+#include "weapons/_mod.inc"
+
+#include <common/_all.inc>
+#include <common/effects/qc/all.qc>
+
+#include <lib/csqcmodel/cl_model.qc>
+#include <lib/csqcmodel/cl_player.qc>
+#include <lib/csqcmodel/interpolate.qc>
+
+#include <lib/warpzone/anglestransform.qc>
+#include <lib/warpzone/common.qc>
+#include <lib/warpzone/client.qc>
+#include <lib/warpzone/server.qc>
+#include <lib/warpzone/util_server.qc>
index 746734dee2011e048533d0113c4d59a089ea4e82..077b5f450a5ff2eee4b0ca4dd52a5aee382c77ee 100644 (file)
@@ -1,4 +1,5 @@
 #pragma once
+//#include "_mod.qh"
 
 #include <common/util.qh>
 
@@ -10,3 +11,5 @@
 #include "defs.qh"
 #include "main.qh"
 #include "miscfunctions.qh"
+
+#include <common/ent_cs.qh>
index 186be97400aaa76e6253d2786d525c31c234f28d..0920265d630bec596040cd55735ce790510adc76 100644 (file)
@@ -6,7 +6,6 @@
 #include <client/mapvoting.qc>
 #include <client/miscfunctions.qc>
 #include <client/player_skeleton.qc>
-#include <client/scoreboard.qc>
 #include <client/shownames.qc>
 #include <client/teamradar.qc>
 #include <client/view.qc>
index 1c66ff781e07ceb95562c9cd57ec7238311a772f..75266dfa2d0aaec18e189dcc84ee21f9e00abcee 100644 (file)
@@ -6,7 +6,6 @@
 #include <client/mapvoting.qh>
 #include <client/miscfunctions.qh>
 #include <client/player_skeleton.qh>
-#include <client/scoreboard.qh>
 #include <client/shownames.qh>
 #include <client/teamradar.qh>
 #include <client/view.qh>
index 0a0959b87ae09bb088725c13a4a8c970a5876264..62b732bec25034e773801113d86aa9f268bfcbf4 100644 (file)
@@ -24,7 +24,7 @@ void Announcer_Countdown(entity this)
        if(roundstarttime == -1)
        {
                Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP);
-               remove(this);
+               delete(this);
                announcer_countdown = NULL;
                return;
        }
@@ -40,7 +40,7 @@ void Announcer_Countdown(entity this)
        {
                Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN);
                Local_Notification(MSG_MULTI, MULTI_COUNTDOWN_BEGIN);
-               remove(this);
+               delete(this);
                announcer_countdown = NULL;
                return;
        }
@@ -58,7 +58,7 @@ void Announcer_Countdown(entity this)
                {
                        Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
                        Notification annce_num = Announcer_PickNumber(CNT_GAMESTART, countdown_rounded);
-                       if(annce_num != NULL)           
+                       if(annce_num != NULL)
                                Local_Notification(MSG_ANNCE, annce_num);
                }
 
@@ -87,7 +87,7 @@ void Announcer_Gamestart()
                        centerprint_kill(ORDINAL(CPID_ROUND));
                        if(announcer_countdown)
                        {
-                               remove(announcer_countdown);
+                               delete(announcer_countdown);
                                announcer_countdown = NULL;
                        }
                }
index d159cd3170e27426c5d40653f8f6141c463e6ccb..193b7ef7cae691955478d714797e2b41206e9dd9 100644 (file)
@@ -93,14 +93,13 @@ string autocvar_cl_weaponpriority;
 float autocvar_cl_zoomfactor;
 float autocvar_cl_zoomsensitivity;
 float autocvar_cl_zoomspeed;
-bool autocvar_cl_unpress_zoom_on_spawn = 1;
-bool autocvar_cl_unpress_zoom_on_death = 1;
-bool autocvar_cl_unpress_zoom_on_weapon_switch = 1;
-bool autocvar_cl_unpress_attack_on_weapon_switch = 1;
+bool autocvar_cl_unpress_zoom_on_spawn = true;
+bool autocvar_cl_unpress_zoom_on_death = true;
+bool autocvar_cl_unpress_zoom_on_weapon_switch = true;
+bool autocvar_cl_unpress_attack_on_weapon_switch = false;
 bool autocvar_con_chat;
 bool autocvar_con_chatrect;
 float autocvar_con_chatsize;
-float autocvar_con_chattime;
 float autocvar_con_notify;
 float autocvar_con_notifysize;
 string autocvar_crosshair;
@@ -123,7 +122,7 @@ string autocvar_crosshair_hitindication_per_weapon_color;
 float autocvar_crosshair_hitindication_speed;
 bool autocvar_crosshair_hittest;
 bool autocvar_crosshair_hittest_blur;
-float autocvar_crosshair_hittest_scale = 1.25;
+//float autocvar_crosshair_hittest_scale = 1.25;
 bool autocvar_crosshair_hittest_showimpact;
 bool autocvar_crosshair_per_weapon;
 float autocvar_crosshair_pickup;
@@ -209,7 +208,11 @@ bool autocvar_hud_panel_engineinfo_dynamichud   = true;
 bool autocvar_hud_panel_infomessages_dynamichud = false;
 bool autocvar_hud_panel_physics_dynamichud      = true;
 bool autocvar_hud_panel_centerprint_dynamichud  = true;
-bool autocvar_hud_panel_itemstime_dynamichud    = true;
+//bool autocvar_hud_panel_itemstime_dynamichud    = true;
+bool autocvar_hud_panel_healtharmor_hide_ondeath  = false;
+bool autocvar_hud_panel_ammo_hide_ondeath         = false;
+bool autocvar_hud_panel_powerups_hide_ondeath     = false;
+bool autocvar_hud_panel_weapons_hide_ondeath      = false;
 bool autocvar_hud_panel_ammo;
 bool autocvar_hud_panel_ammo_iconalign;
 int autocvar_hud_panel_ammo_maxammo;
@@ -262,6 +265,7 @@ float autocvar_hud_panel_healtharmor_progressbar_gfx_smooth;
 int autocvar_hud_panel_healtharmor_text;
 bool autocvar_hud_panel_infomessages;
 bool autocvar_hud_panel_infomessages_flip;
+float autocvar_hud_panel_mapvote_highlight_border = 1;
 bool autocvar_hud_panel_modicons;
 int autocvar_hud_panel_modicons_ca_layout;
 int autocvar_hud_panel_modicons_dom_layout;
@@ -319,7 +323,6 @@ bool autocvar_hud_panel_timer_increment;
 float autocvar_hud_panel_update_interval;
 bool autocvar_hud_panel_vote;
 float autocvar_hud_panel_vote_alreadyvoted_alpha;
-string autocvar_hud_panel_vote_bg_alpha;
 bool autocvar_hud_panel_weapons;
 bool autocvar_hud_panel_weapons_accuracy;
 bool autocvar_hud_panel_weapons_ammo;
@@ -391,34 +394,10 @@ float autocvar_hud_shownames_offset;
 string autocvar_hud_skin;
 float autocvar_menu_mouse_speed;
 string autocvar_menu_skin;
-float autocvar_r_drawviewmodel;
 int autocvar_r_fakelight;
 int autocvar_r_fullbright;
 float autocvar_r_letterbox;
-bool autocvar_scoreboard_accuracy;
-bool autocvar_scoreboard_accuracy_doublerows;
-bool autocvar_scoreboard_accuracy_nocolors;
-float autocvar_scoreboard_alpha_bg;
-float autocvar_scoreboard_alpha_fg = 1.0;
-float autocvar_scoreboard_alpha_name = 0.9;
-float autocvar_scoreboard_alpha_name_self = 1;
-float autocvar_scoreboard_bg_scale;
-float autocvar_scoreboard_border_thickness;
-float autocvar_scoreboard_color_bg_b;
-float autocvar_scoreboard_color_bg_g;
-float autocvar_scoreboard_color_bg_r;
-float autocvar_scoreboard_color_bg_team;
 string autocvar_scoreboard_columns;
-float autocvar_scoreboard_fadeinspeed = 10;
-float autocvar_scoreboard_fadeoutspeed = 5;
-bool autocvar_scoreboard_highlight;
-float autocvar_scoreboard_highlight_alpha = 0.10;
-float autocvar_scoreboard_highlight_alpha_self = 0.25;
-float autocvar_scoreboard_offset_left;
-float autocvar_scoreboard_offset_right;
-float autocvar_scoreboard_offset_vertical;
-float autocvar_scoreboard_respawntime_decimals;
-bool autocvar_scoreboard_dynamichud = false;
 bool autocvar_v_flipped;
 float autocvar_vid_conheight;
 float autocvar_vid_conwidth;
@@ -433,8 +412,6 @@ float autocvar_cl_hitsound_max_pitch = 1.5;
 float autocvar_cl_hitsound_nom_damage = 25;
 float autocvar_cl_hitsound_antispam_time;
 int autocvar_cl_eventchase_death = 1;
-vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80';
-float autocvar_cl_eventchase_generator_distance = 400;
 float autocvar_cl_eventchase_distance = 140;
 float autocvar_cl_eventchase_speed = 1.3;
 vector autocvar_cl_eventchase_maxs = '12 12 8';
@@ -464,14 +441,6 @@ string autocvar__cl_playermodel;
 float autocvar_cl_deathglow;
 bool autocvar_developer_csqcentities;
 float autocvar_g_jetpack_attenuation;
-string autocvar_crosshair_hmg = "";
-vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2';
-float autocvar_crosshair_hmg_alpha = 1;
-float autocvar_crosshair_hmg_size = 1;
-string autocvar_crosshair_rpc = "";
-vector autocvar_crosshair_rpc_color = '0.2 1.0 0.2';
-float autocvar_crosshair_rpc_alpha = 1;
-float autocvar_crosshair_rpc_size = 1;
+bool autocvar_cl_showspectators;
 int autocvar_cl_nade_timer;
-bool autocvar_cl_items_nofade;
-float autocvar_slowmo;
+bool autocvar_r_drawviewmodel;
index 235f1297fda6e5c7fdc3d6b43c3cab94e0af6109..dcfc6ecea56e94103c08ae21a99997bf815dff72 100644 (file)
@@ -1,3 +1,4 @@
 // generated file; do not modify
-#include <client/commands/all.qc>
-#include <client/commands/cl_cmd.qc>
+#ifdef CSQC
+    #include <client/commands/cl_cmd.qc>
+#endif
index 03df56398ccf89c0e09f1a70011698f8e1465d50..7829965a2350f0bb075fd0431447aabe030f7d57 100644 (file)
@@ -1,3 +1,4 @@
 // generated file; do not modify
-#include <client/commands/all.qh>
-#include <client/commands/cl_cmd.qh>
+#ifdef CSQC
+    #include <client/commands/cl_cmd.qh>
+#endif
diff --git a/qcsrc/client/commands/all.qc b/qcsrc/client/commands/all.qc
deleted file mode 100644 (file)
index bc15eeb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "all.qh"
-#include <common/command/all.qc>
diff --git a/qcsrc/client/commands/all.qh b/qcsrc/client/commands/all.qh
deleted file mode 100644 (file)
index 2df61f0..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-
-#include <common/command/all.qh>
-
-#include "cl_cmd.qh"
index dce058c816da9f2c4abbfae97c4bad68d34931dd..631090cc1f9b7d119a2a9847b9d6ee5619cdd568 100644 (file)
@@ -4,12 +4,12 @@
 //  Last updated: December 28th, 2011
 // ==============================================
 
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 #include "cl_cmd.qh"
 
 #include "../autocvars.qh"
 #include "../defs.qh"
-#include <client/hud/all.qh>
+#include <client/hud/_mod.qh>
 #include "../main.qh"
 #include "../mapvoting.qh"
 #include "../miscfunctions.qh"
@@ -18,8 +18,6 @@
 
 #include <common/mapinfo.qh>
 
-#include <common/command/generic.qh>
-
 void DrawDebugModel(entity this)
 {
        if (time - floor(time) > 0.5)
@@ -183,6 +181,7 @@ void LocalCommand_debugmodel(int request, int argc)
                        setorigin(debugmodel_entity, view_origin);
                        debugmodel_entity.angles = view_angles;
                        debugmodel_entity.draw = DrawDebugModel;
+                       IL_PUSH(g_drawables, debugmodel_entity);
 
                        return;
                }
@@ -306,13 +305,13 @@ void LocalCommand_hud(int request, int argc)
 
                                case "scoreboard_columns_set":
                                {
-                                       Cmd_HUD_SetFields(argc);
+                                       Cmd_Scoreboard_SetFields(argc);
                                        return;
                                }
 
                                case "scoreboard_columns_help":
                                {
-                                       Cmd_HUD_Help();
+                                       Cmd_Scoreboard_Help();
                                        return;
                                }
 
index 65a723389e793645f7f2a674a8277ad85d61f0a8..f1be4315fe5df1caeaaca2b6e29b62031bae2001 100644 (file)
@@ -1,7 +1,7 @@
 #pragma once
 
-void Cmd_HUD_SetFields(int);
-void Cmd_HUD_Help();
+void Cmd_Scoreboard_SetFields(int);
+void Cmd_Scoreboard_Help();
 
 // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file
 void LocalCommand_macro_write_aliases(int fh);
index 2ea1e746f7c60e12104563d3dd2751ff66bb2fcc..d8906b12ba466a2f0bef24b191e4935e0e30a58e 100644 (file)
@@ -132,7 +132,7 @@ const int SOLID_CORPSE      = 5; // same as SOLID_BBOX, except it behaves as SOLID_N
 
 const int MOVE_NORMAL = 0; // same as false
 const int MOVE_NOMONSTERS = 1; // same as true
-const int MOVE_MISSILE = 2; // save as movement with .movetype == MOVETYPE_FLYMISSILE
+const int MOVE_MISSILE = 2; // save as movement with .move_movetype == MOVETYPE_FLYMISSILE
 const int MOVE_HITMODEL = 4;
 const int MOVE_WORLDONLY = 3;
 
index 30148bdd44e7f4be1fb88b33d406ae203b525788..104465360770beaddc14298f06d2872f0d2d6d1f 100644 (file)
@@ -3,6 +3,7 @@
 #include "player_skeleton.qh"
 #include "weapons/projectile.qh"
 #include <common/animdecide.qh>
+#include <common/ent_cs.qh>
 #include <common/physics/movetypes/movetypes.qh>
 #include <common/viewloc.qh>
 #include <lib/csqcmodel/cl_model.qh>
@@ -142,7 +143,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer)
                _setmodel(e, cvar_defstring("_cl_playermodel"));
                forceplayermodels_goodmodel = e.model;
                forceplayermodels_goodmodelindex = e.modelindex;
-               remove(e);
+               delete(e);
        }
 
        // first, try finding it from the server
@@ -175,7 +176,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer)
                forceplayermodels_model = e.model;
                forceplayermodels_modelindex = e.modelindex;
                forceplayermodels_skin = autocvar__cl_playerskin;
-               remove(e);
+               delete(e);
        }
 
        if(autocvar_cl_forcemyplayermodel != "" && autocvar_cl_forcemyplayermodel != forceplayermodels_mymodel)
@@ -185,14 +186,14 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer)
                forceplayermodels_myisgoodmodel = fexists(e.model);
                forceplayermodels_mymodel = e.model;
                forceplayermodels_mymodelindex = e.modelindex;
-               remove(e);
+               delete(e);
        }
 
        // apply it
        bool isfriend;
        int cm;
        cm = this.forceplayermodels_savecolormap;
-       cm = (cm >= 1024) ? cm : (stof(getplayerkeyvalue(cm - 1, "colors")) + 1024);
+       cm = (cm >= 1024) ? cm : (entcs_GetClientColors(cm - 1) + 1024);
 
        if(teamplay)
                isfriend = (cm == 1024 + 17 * myteam);
@@ -279,7 +280,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer)
 
        // GLOWMOD AND DEATH FADING
        if(this.colormap > 0)
-               this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : stof(getplayerkeyvalue(this.colormap - 1, "colors"))) & 0x0F, true) * 2;
+               this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true) * 2;
        else
                this.glowmod = '1 1 1';
 
@@ -369,7 +370,7 @@ int CSQCPlayer_FallbackFrame(entity this, int f)
                case 29: return 4; // anim_duckwalkbackright -> anim_duckwalk
                case 30: return 4; // anim_duckwalkbackleft -> anim_duckwalk
        }
-       LOG_DEBUGF("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, this.model);
+       LOG_DEBUGF("Frame %d missing in model %s, and we have no fallback - FAIL!", f, this.model);
        return f;
 }
 void CSQCPlayer_FallbackFrame_Apply(entity this)
@@ -391,8 +392,6 @@ void CSQCModel_AutoTagIndex_Apply(entity this)
        if(this.tag_entity && wasfreed(this.tag_entity))
                this.tag_entity = NULL;
 
-       viewloc_SetTags(this);
-
        MUTATOR_CALLHOOK(TagIndex_Update, this);
 
        if(this.tag_networkentity)
@@ -433,7 +432,7 @@ void CSQCModel_AutoTagIndex_Apply(entity this)
                                                        // we need to prevent this from 'appening
                                                        this.tag_entity = NULL;
                                                        this.drawmask = 0;
-                                                       LOG_TRACE("h_ model lacks weapon attachment, but v_ model is attached to it\n");
+                                                       LOG_TRACE("h_ model lacks weapon attachment, but v_ model is attached to it");
                                                }
                                        }
                                        else if(this.tag_entity.isplayermodel)
index 84bcb2b3d1de61b9417c250c5f28d1ad4ce79270..309ed1d81572998098dcc40fdaad24ae72df6ba1 100644 (file)
@@ -27,7 +27,7 @@ float         dmg_take;
 .int team;
 .int team_size;
 
-float vid_conwidth, vid_conheight;
+float vid_conheight;
 int binddb;
 
 // QUALIFYING
@@ -47,8 +47,10 @@ string race_penaltyreason; // reason for penalty
 float race_server_record; // server record
 float race_speedaward;
 string race_speedaward_holder;
+string race_speedaward_unit;
 float race_speedaward_alltimebest;
 string race_speedaward_alltimebest_holder;
+string race_speedaward_alltimebest_unit;
 
 // RACE
 float race_mycheckpoint;
@@ -86,7 +88,7 @@ vector lightning_shotorigin[4];
 float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power;
 #endif
 
-float servertime, serverprevtime, serverdeltatime;
+float serverprevtime, serverdeltatime;
 
 float ticrate;
 
@@ -118,6 +120,8 @@ int serverflags;
 
 float uid2name_dialog;
 
+float intermission_time;
+
 .bool csqcmodel_isdead; // used by shownames and miscfunctions (entcs_IsDead) to know when a player is dead
 
 #define player_currententnum (spectatee_status > 0 ? spectatee_status : player_localnum + 1)
index b90f61b8cc443286de41af3507cfbf146f8c988c..aa785a9e94c5524789bf7e47dbe0adeebe6ed8c3 100644 (file)
@@ -1,3 +1,6 @@
 // generated file; do not modify
 #include <client/hud/hud.qc>
 #include <client/hud/hud_config.qc>
+#include <client/hud/panel.qc>
+
+#include <client/hud/panel/_mod.inc>
index ee9ac8f76f56736a9f05f6d0a60834abf3849b99..2d4850d521d360565cacf66e4dabf3e7949f6d66 100644 (file)
@@ -1,3 +1,6 @@
 // generated file; do not modify
 #include <client/hud/hud.qh>
 #include <client/hud/hud_config.qh>
+#include <client/hud/panel.qh>
+
+#include <client/hud/panel/_mod.qh>
diff --git a/qcsrc/client/hud/all.inc b/qcsrc/client/hud/all.inc
deleted file mode 100644 (file)
index aa36eec..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "panel/weapons.qc"
-#include "panel/ammo.qc"
-#include "panel/powerups.qc"
-#include "panel/healtharmor.qc"
-#include "panel/notify.qc"
-#include "panel/timer.qc"
-#include "panel/radar.qc"
-#include "panel/score.qc"
-#include "panel/racetimer.qc"
-#include "panel/vote.qc"
-#include "panel/modicons.qc"
-#include "panel/pressedkeys.qc"
-#include "panel/chat.qc"
-#include "panel/engineinfo.qc"
-#include "panel/infomessages.qc"
-#include "panel/physics.qc"
-#include "panel/centerprint.qc"
-#include "panel/minigame.qc"
-// #include "panel/mapvote.qc"
-// #include "panel/itemstime.qc"
-#include "panel/quickmenu.qc"
diff --git a/qcsrc/client/hud/all.qh b/qcsrc/client/hud/all.qh
deleted file mode 100644 (file)
index 2e458e6..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#include "hud.qh"
-#include "hud_config.qh"
index c6cb5257b132fc9787ace53ac97bd9d51c338d90..ee94af72705e032158b5139980e91a18429a087e 100644 (file)
@@ -1,12 +1,13 @@
 #include "hud.qh"
 
+#include "panel/scoreboard.qh"
 #include "hud_config.qh"
 #include "../mapvoting.qh"
-#include "../scoreboard.qh"
 #include "../teamradar.qh"
+#include <common/minigames/cl_minigames.qh>
 #include <common/t_items.qh>
 #include <common/deathtypes/all.qh>
-#include <common/items/all.qc>
+#include <common/items/_mod.qh>
 #include <common/mapinfo.qh>
 #include <common/vehicles/all.qh>
 #include <common/mutators/mutator/waypoints/all.qh>
@@ -21,34 +22,34 @@ Misc HUD functions
 ==================
 */
 
-vector HUD_Get_Num_Color (float x, float maxvalue)
+vector HUD_Get_Num_Color (float hp, float maxvalue)
 {
        float blinkingamt;
        vector color;
-       if(x >= maxvalue) {
+       if(hp >= maxvalue) {
                color.x = sin(2*M_PI*time);
                color.y = 1;
                color.z = sin(2*M_PI*time);
        }
-       else if(x > maxvalue * 0.75) {
-               color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
-               color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
+       else if(hp > maxvalue * 0.75) {
+               color.x = 0.4 - (hp-150)*0.02 * 0.4; //red value between 0.4 -> 0
+               color.y = 0.9 + (hp-150)*0.02 * 0.1; // green value between 0.9 -> 1
                color.z = 0;
        }
-       else if(x > maxvalue * 0.5) {
-               color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
-               color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
-               color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0
+       else if(hp > maxvalue * 0.5) {
+               color.x = 1 - (hp-100)*0.02 * 0.6; //red value between 1 -> 0.4
+               color.y = 1 - (hp-100)*0.02 * 0.1; // green value between 1 -> 0.9
+               color.z = 1 - (hp-100)*0.02; // blue value between 1 -> 0
        }
-       else if(x > maxvalue * 0.25) {
+       else if(hp > maxvalue * 0.25) {
                color.x = 1;
                color.y = 1;
-               color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
+               color.z = 0.2 + (hp-50)*0.02 * 0.8; // blue value between 0.2 -> 1
        }
-       else if(x > maxvalue * 0.1) {
+       else if(hp > maxvalue * 0.1) {
                color.x = 1;
-               color.y = (x-20)*90/27/100; // green value between 0 -> 1
-               color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
+               color.y = (hp-20)*90/27/100; // green value between 0 -> 1
+               color.z = (hp-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
        }
        else {
                color.x = 1;
@@ -56,7 +57,7 @@ vector HUD_Get_Num_Color (float x, float maxvalue)
                color.z = 0;
        }
 
-       blinkingamt = (1 - x/maxvalue/0.25);
+       blinkingamt = (1 - hp/maxvalue/0.25);
        if(blinkingamt > 0)
        {
                color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time);
@@ -120,6 +121,59 @@ HUD panels
 ==================
 */
 
+void HUD_Panel_LoadCvars()
+{
+       // NOTE: in hud_configure mode cvars must be reloaded every frame
+       if (panel.update_time <= time)
+       {
+               panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos")));
+               panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size")));
+               HUD_Panel_ScalePosSize();
+               panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg"));
+               panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color"));
+               panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team"));
+               panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha"));
+               panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border"));
+               panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding"));
+               HUD_Panel_GetBg();
+               if (panel.current_panel_bg != "0")
+               {
+                       HUD_Panel_GetBgAlpha();
+                       HUD_Panel_GetBorder();
+               }
+               HUD_Panel_GetColorTeam();
+               HUD_Panel_GetColor();
+               HUD_Panel_GetFgAlpha();
+               HUD_Panel_GetPadding();
+               panel.current_panel_bg_alpha = panel_bg_alpha;
+               panel.current_panel_fg_alpha = panel_fg_alpha;
+               if (hud_configure_menu_open == 2 && panel == highlightedPanel)
+                       HUD_Panel_UpdatePosSize_ForMenu();
+               else
+               {
+                       panel_bg_alpha *= hud_fade_alpha * panel_fade_alpha;
+                       panel_fg_alpha *= hud_fade_alpha * panel_fade_alpha;
+               }
+               panel.current_panel_pos = panel_pos;
+               panel.current_panel_size = panel_size;
+               panel.current_panel_bg_border = panel_bg_border;
+               panel.current_panel_bg_color = panel_bg_color;
+               panel.current_panel_bg_color_team = panel_bg_color_team;
+               panel.current_panel_bg_padding = panel_bg_padding;
+               panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval;
+               return;
+       }
+
+       panel_pos = panel.current_panel_pos;
+       panel_size = panel.current_panel_size;
+       panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha * panel_fade_alpha;
+       panel_bg_border = panel.current_panel_bg_border;
+       panel_bg_color = panel.current_panel_bg_color;
+       panel_bg_color_team = panel.current_panel_bg_color_team;
+       panel_bg_padding = panel.current_panel_bg_padding;
+       panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha * panel_fade_alpha;
+}
+
 //basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu
 void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag)
 {
@@ -249,7 +303,7 @@ void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theA
        drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag);
 }
 
-void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp)
+void DrawNumIcon_expanding(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp)
 {
     TC(bool, vertical); TC(int, icon_right_align);
        vector newPos = '0 0 0', newSize = '0 0 0';
@@ -291,7 +345,7 @@ void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bo
                // reduce only y to draw numbers with different number of digits with the same y size
                numpos.y += newSize.y * ((1 - 0.7) / 2);
                newSize.y *= 0.7;
-               drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+               drawstring_aspect(numpos, ftos(theTime), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
                return;
        }
 
@@ -325,18 +379,16 @@ void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bo
 
        // NOTE: newSize_x is always equal to 3 * mySize_y so we can use
        // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y
-       drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
+       drawstring_aspect_expanding(numpos, ftos(theTime), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
        drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
 }
 
-void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, int icon_right_align, vector color, float theAlpha)
+void DrawNumIcon(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha)
 {
     TC(bool, vertical); TC(int, icon_right_align);
-       DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0);
+       DrawNumIcon_expanding(myPos, mySize, theTime, icon, vertical, icon_right_align, color, theAlpha, 0);
 }
 
-#include "all.inc"
-
 /*
 ==================
 Main HUD system
@@ -358,33 +410,52 @@ void HUD_Vehicle()
        }
 }
 
-bool HUD_Panel_CheckFlags(int showflags)
-{
-    TC(int, showflags);
-       if ( HUD_Minigame_Showpanels() )
-               return showflags & PANEL_SHOW_MINIGAME;
-       if(intermission == 2)
-               return showflags & PANEL_SHOW_MAPVOTE;
-       return showflags & PANEL_SHOW_MAINGAME;
-}
-
 void HUD_Panel_Draw(entity panent)
 {
        panel = panent;
-       if(autocvar__hud_configure)
+       if (autocvar__hud_configure)
+       {
+               if (!(panel.panel_configflags & PANEL_CONFIG_MAIN))
+                       return;
+               panel_fade_alpha = 1;
+               Hud_Panel_GetPanelEnabled();
+               panel.panel_draw();
+               return;
+       }
+
+       bool draw_allowed = false;
+       if (active_minigame && HUD_MinigameMenu_IsOpened())
        {
-               if(panel.panel_configflags & PANEL_CONFIG_MAIN)
-                       panel.panel_draw();
+               if (panel.panel_showflags & PANEL_SHOW_MINIGAME)
+                       draw_allowed = true;
        }
-       else if(HUD_Panel_CheckFlags(panel.panel_showflags))
+       else if(intermission == 2)
+       {
+               if(panel.panel_showflags & PANEL_SHOW_MAPVOTE)
+                       draw_allowed = true;
+       }
+       else if (panel.panel_showflags & PANEL_SHOW_MAINGAME)
+               draw_allowed = true;
+
+       if (draw_allowed)
+       {
+               if (panel.panel_showflags & PANEL_SHOW_WITH_SB)
+                       panel_fade_alpha = 1;
+               else
+               {
+                       panel_fade_alpha = 1 - scoreboard_fade_alpha;
+                       if(!panel_fade_alpha)
+                               return;
+               }
                panel.panel_draw();
+       }
 }
 
 void HUD_Reset()
 {
        // reset gametype specific icons
-       if(gametype == MAPINFO_TYPE_CTF)
-               HUD_Mod_CTF_Reset();
+       if(gametype.m_modicons_reset)
+               gametype.m_modicons_reset();
 }
 
 float autocvar_hud_dynamic_shake = 1;
@@ -414,6 +485,7 @@ bool Hud_Shake_Update()
        return true;
 }
 
+entity CSQCModel_server2csqc(int i);
 void calc_followmodel_ofs(entity view);
 void Hud_Dynamic_Frame()
 {
@@ -493,34 +565,18 @@ void Hud_Dynamic_Frame()
 void HUD_Main()
 {
        int i;
-       // global hud theAlpha fade
-       if(menu_enabled == 1)
+       if(hud_configure_menu_open == 1)
                hud_fade_alpha = 1;
        else
-               hud_fade_alpha = (1 - autocvar__menu_alpha);
-
-       if(scoreboard_fade_alpha)
-               hud_fade_alpha = (1 - scoreboard_fade_alpha);
+               hud_fade_alpha = 1 - autocvar__menu_alpha;
 
        HUD_Configure_Frame();
 
        Hud_Dynamic_Frame();
 
-       // panels that we want to be active together with the scoreboard
-       // they must fade only when the menu does
        if(scoreboard_fade_alpha == 1)
-       {
-               HUD_Panel_Draw(HUD_PANEL(CENTERPRINT));
-               return;
-       }
-
-       if(!autocvar__hud_configure && !hud_fade_alpha)
-       {
-               hud_fade_alpha = 1;
-               HUD_Panel_Draw(HUD_PANEL(VOTE));
-               hud_fade_alpha = 0;
-               return;
-       }
+               if(autocvar__menu_alpha == 1)
+                       return;
 
        // Drawing stuff
        if (hud_skin_prev != autocvar_hud_skin)
@@ -552,11 +608,11 @@ void HUD_Main()
                {
                        string hud_dock_color = autocvar_hud_dock_color;
                        if(hud_dock_color == "shirt") {
-                               f = stof(getplayerkeyvalue(current_player, "colors"));
+                               f = entcs_GetClientColors(current_player);
                                color = colormapPaletteColor(floor(f / 16), 0);
                        }
                        else if(hud_dock_color == "pants") {
-                               f = stof(getplayerkeyvalue(current_player, "colors"));
+                               f = entcs_GetClientColors(current_player);
                                color = colormapPaletteColor(f % 16, 1);
                        }
                        else
@@ -602,7 +658,7 @@ void HUD_Main()
                        }
                }
                if (warning)
-                       LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
+                       LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder");
 
                cvar_set("_hud_panelorder", s);
                if(hud_panelorder_prev)
@@ -631,8 +687,9 @@ void HUD_Main()
                HUD_Panel_Draw(HUD_PANEL(CHAT));
        if(hud_panel_quickmenu)
                HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
+       HUD_Panel_Draw(HUD_PANEL(SCOREBOARD));
 
-       if (scoreboard_active || intermission == 2)
+       if (intermission == 2)
                HUD_Reset();
 
        HUD_Configure_PostDraw();
index 2a53444669b7829c9818ca342ae8bf6484f0fafa..06a34911453f3a8120a89171ab237e1283dd2172 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 bool HUD_Radar_Clickable();
 void HUD_Radar_Mouse();
@@ -22,14 +22,13 @@ REGISTER_REGISTRY(hud_panels)
 #define HUD_PANEL(NAME) HUD_PANEL_##NAME
 
 // draw the background/borders
-#define HUD_Panel_DrawBg(theAlpha) MACRO_BEGIN { \
+#define HUD_Panel_DrawBg() MACRO_BEGIN { \
        if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") \
                draw_BorderPicture( \
                        HUD_Shift(panel_pos - '1 1 0' * panel_bg_border), \
                        panel.current_panel_bg, \
                        HUD_Scale(panel_size + '1 1 0' * 2 * panel_bg_border), \
-                       panel_bg_color, \
-                       panel_bg_alpha * theAlpha, \
+                       panel_bg_color, panel_bg_alpha, \
                        HUD_Scale('1 1 0' * (panel_bg_border/BORDER_MULTIPLIER)) \
                ); \
 } MACRO_END
@@ -49,9 +48,9 @@ float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary);
 void HUD_Radar_Hide_Maximized();
 
 float HUD_GetRowCount(int item_count, vector size, float item_aspect);
-vector HUD_Get_Num_Color (float x, float maxvalue);
-void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha);
-void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp);
+vector HUD_Get_Num_Color (float hp, float maxvalue);
+void DrawNumIcon(vector myPos, vector mySize, float theTime, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha);
+void DrawNumIcon_expanding(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp);
 void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag);
 vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect);
 
@@ -88,7 +87,7 @@ int complain_weapon;
 float complain_weapon_type;
 float complain_weapon_time;
 
-int ps_primary, ps_secondary;
+PlayerScoreField ps_primary, ps_secondary;
 int ts_primary, ts_secondary;
 
 Weapon last_switchweapon;
@@ -98,18 +97,11 @@ float weaponprevtime;
 
 float teamnagger;
 
-float hud_configure_checkcollisions;
-float hud_configure_prev;
-vector hud_configure_gridSize;
-vector hud_configure_realGridSize;
-
 int hudShiftState;
 const int S_SHIFT = 1;
 const int S_CTRL = 2;
 const int S_ALT = 4;
 
-float menu_enabled; // 1 showing the entire HUD, 2 showing only the clicked panel
-
 float hud_fade_alpha;
 
 string hud_skin_path;
@@ -189,12 +181,13 @@ const int PANEL_SHOW_NEVER    = 0x00;
 const int PANEL_SHOW_MAINGAME = 0x01;
 const int PANEL_SHOW_MINIGAME = 0x02;
 const int PANEL_SHOW_MAPVOTE  = 0x04;
+const int PANEL_SHOW_WITH_SB  = 0x08;
 const int PANEL_SHOW_ALWAYS   = 0xff;
-bool HUD_Panel_CheckFlags(int showflags);
 
 .int panel_configflags;
-const int PANEL_CONFIG_NO    = 0x00;
-const int PANEL_CONFIG_MAIN  = 0x01;
+const int PANEL_CONFIG_NO        = 0x00;
+const int PANEL_CONFIG_MAIN      = 0x01;
+const int PANEL_CONFIG_CANBEOFF  = 0x02; // panel can be disabled (if disabled it's displayed with a low alpha)
 
 
 // prev_* vars contain the health/armor at the previous FRAME
@@ -211,30 +204,31 @@ int prev_p_health, prev_p_armor;
 
 void HUD_ItemsTime();
 
-REGISTER_HUD_PANEL(WEAPONS,         HUD_Weapons,        weapons,        PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(AMMO,            HUD_Ammo,           ammo,           PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(POWERUPS,        HUD_Powerups,       powerups,       PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(HEALTHARMOR,     HUD_HealthArmor,    healtharmor,    PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(NOTIFY,          HUD_Notify,         notify,         PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
-REGISTER_HUD_PANEL(TIMER,           HUD_Timer,          timer,          PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
-REGISTER_HUD_PANEL(RADAR,           HUD_Radar,          radar,          PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(SCORE,           HUD_Score,          score,          PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
-REGISTER_HUD_PANEL(RACETIMER,       HUD_RaceTimer,      racetimer,      PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(VOTE,            HUD_Vote,           vote,           PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS  )
-REGISTER_HUD_PANEL(MODICONS,        HUD_ModIcons,       modicons,       PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(PRESSEDKEYS,     HUD_PressedKeys,    pressedkeys,    PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(CHAT,            HUD_Chat,           chat,           PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS  )
-REGISTER_HUD_PANEL(ENGINEINFO,      HUD_EngineInfo,     engineinfo,     PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS  )
-REGISTER_HUD_PANEL(INFOMESSAGES,    HUD_InfoMessages,   infomessages,   PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(PHYSICS,         HUD_Physics,        physics,        PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(CENTERPRINT,     HUD_CenterPrint,    centerprint,    PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(MINIGAME_BOARD,  HUD_MinigameBoard,  minigameboard,  PANEL_CONFIG_NO  , PANEL_SHOW_MINIGAME)
-REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO  , PANEL_SHOW_MINIGAME)
-REGISTER_HUD_PANEL(MINIGAME_HELP,   HUD_MinigameHelp,   minigamehelp,   PANEL_CONFIG_NO  , PANEL_SHOW_MINIGAME)
-REGISTER_HUD_PANEL(MINIGAME_MENU,   HUD_MinigameMenu,   minigamemenu,   PANEL_CONFIG_NO  , PANEL_SHOW_ALWAYS  )
-REGISTER_HUD_PANEL(MAPVOTE,         MapVote_Draw,       mapvote,        PANEL_CONFIG_NO  , PANEL_SHOW_MAPVOTE )
-REGISTER_HUD_PANEL(ITEMSTIME,       HUD_ItemsTime,      itemstime,      PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(QUICKMENU,       HUD_QuickMenu,      quickmenu,      PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(WEAPONS,         HUD_Weapons,        weapons,        PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // WEAPONS
+REGISTER_HUD_PANEL(AMMO,            HUD_Ammo,           ammo,           PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // AMMO
+REGISTER_HUD_PANEL(POWERUPS,        HUD_Powerups,       powerups,       PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // POWERUPS
+REGISTER_HUD_PANEL(HEALTHARMOR,     HUD_HealthArmor,    healtharmor,    PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // HEALTHARMOR
+REGISTER_HUD_PANEL(NOTIFY,          HUD_Notify,         notify,         PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME                                          ) // NOTIFY
+REGISTER_HUD_PANEL(TIMER,           HUD_Timer,          timer,          PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME                      | PANEL_SHOW_WITH_SB) // TIMER
+REGISTER_HUD_PANEL(RADAR,           HUD_Radar,          radar,          PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // RADAR
+REGISTER_HUD_PANEL(SCORE,           HUD_Score,          score,          PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME                                          ) // SCORE
+REGISTER_HUD_PANEL(RACETIMER,       HUD_RaceTimer,      racetimer,      PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // RACETIMER
+REGISTER_HUD_PANEL(VOTE,            HUD_Vote,           vote,           PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // VOTE
+REGISTER_HUD_PANEL(MODICONS,        HUD_ModIcons,       modicons,       PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                            | PANEL_SHOW_WITH_SB) // MODICONS
+REGISTER_HUD_PANEL(PRESSEDKEYS,     HUD_PressedKeys,    pressedkeys,    PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // PRESSEDKEYS
+REGISTER_HUD_PANEL(CHAT,            HUD_Chat,           chat,           PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // CHAT
+REGISTER_HUD_PANEL(ENGINEINFO,      HUD_EngineInfo,     engineinfo,     PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // ENGINEINFO
+REGISTER_HUD_PANEL(INFOMESSAGES,    HUD_InfoMessages,   infomessages,   PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // INFOMESSAGES
+REGISTER_HUD_PANEL(PHYSICS,         HUD_Physics,        physics,        PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // PHYSICS
+REGISTER_HUD_PANEL(CENTERPRINT,     HUD_CenterPrint,    centerprint,    PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                            | PANEL_SHOW_WITH_SB) // CENTERPRINT
+REGISTER_HUD_PANEL(MINIGAME_BOARD,  HUD_MinigameBoard,  minigameboard,  PANEL_CONFIG_NO                          ,                       PANEL_SHOW_MINIGAME                      | PANEL_SHOW_WITH_SB) // MINIGAME_BOARD
+REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO                          ,                       PANEL_SHOW_MINIGAME                      | PANEL_SHOW_WITH_SB) // MINIGAME_STATUS
+REGISTER_HUD_PANEL(MINIGAME_HELP,   HUD_MinigameHelp,   minigamehelp,   PANEL_CONFIG_NO                          ,                       PANEL_SHOW_MINIGAME                      | PANEL_SHOW_WITH_SB) // MINIGAME_HELP
+REGISTER_HUD_PANEL(MINIGAME_MENU,   HUD_MinigameMenu,   minigamemenu,   PANEL_CONFIG_NO                          , PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // MINIGAME_MENU
+REGISTER_HUD_PANEL(MAPVOTE,         MapVote_Draw,       mapvote,        PANEL_CONFIG_NO                          ,                                             PANEL_SHOW_MAPVOTE                     ) // MAPVOTE
+REGISTER_HUD_PANEL(ITEMSTIME,       HUD_ItemsTime,      itemstime,      PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME                                                                ) // ITEMSTIME
+REGISTER_HUD_PANEL(QUICKMENU,       HUD_QuickMenu,      quickmenu,      PANEL_CONFIG_MAIN                        , PANEL_SHOW_MAINGAME                                                                ) // QUICKMENU
+REGISTER_HUD_PANEL(SCOREBOARD,      Scoreboard_Draw,    scoreboard,     PANEL_CONFIG_NO                          , PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // SCOREBOARD
 // always add new panels to the end of list
 
 // Because calling lots of functions in QC apparently cuts fps in half on many machines:
@@ -274,21 +268,21 @@ REGISTER_HUD_PANEL(QUICKMENU,       HUD_QuickMenu,      quickmenu,      PANEL_CO
 
 // Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector.
 #define HUD_Panel_GetColor() MACRO_BEGIN {                                                                          \
-       if ((teamplay) && panel_bg_color_team) {                                                                        \
+       if ((teamplay) && panel_bg_color_team > 0) {                                                                        \
                if (autocvar__hud_configure && myteam == NUM_SPECTATOR)                                                     \
                        panel_bg_color = '1 0 0' * panel_bg_color_team;                                                         \
                else                                                                                                        \
                        panel_bg_color = myteamcolors * panel_bg_color_team;                                                    \
-       } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) {          \
+       } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team > 0) {          \
                panel_bg_color = '1 0 0' * panel_bg_color_team;                                                             \
        } else {                                                                                                        \
                if (panel_bg_color_str == "") {                                                                             \
                        panel_bg_color = autocvar_hud_panel_bg_color;                                                           \
                } else {                                                                                                    \
                        if (panel_bg_color_str == "shirt") {                                                                    \
-                               panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \
+                               panel_bg_color = colormapPaletteColor(floor(entcs_GetClientColors(current_player) / 16), 0); \
                        } else if (panel_bg_color_str == "pants") {                                                             \
-                               panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \
+                               panel_bg_color = colormapPaletteColor(entcs_GetClientColors(current_player) % 16, 1); \
                        } else {                                                                                                \
                                panel_bg_color = stov(panel_bg_color_str);                                                          \
                        }                                                                                                       \
@@ -315,7 +309,7 @@ REGISTER_HUD_PANEL(QUICKMENU,       HUD_QuickMenu,      quickmenu,      PANEL_CO
        if (autocvar__hud_configure) {                                                                                  \
                if (!panel_enabled)                                                                                         \
                        panel_bg_alpha = 0.25;                                                                                  \
-               else if (menu_enabled == 2 && panel == highlightedPanel)                                                    \
+               else if (hud_configure_menu_open == 2 && panel == highlightedPanel)                                                    \
                        panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\
                else                                                                                                        \
                        panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha);                                \
@@ -353,16 +347,16 @@ REGISTER_HUD_PANEL(QUICKMENU,       HUD_QuickMenu,      quickmenu,      PANEL_CO
 // return smoothly faded pos and size of given panel when a dialog is active
 // don't center too wide panels, it doesn't work with different resolutions
 #define HUD_Panel_UpdatePosSize_ForMenu() MACRO_BEGIN { \
-       vector menu_enable_size = panel_size; \
+       vector new_size = panel_size; \
        float max_panel_width = 0.52 * vid_conwidth; \
        if(panel_size.x > max_panel_width) \
        { \
-               menu_enable_size.x = max_panel_width; \
-               menu_enable_size.y = panel_size.y * (menu_enable_size.x / panel_size.x); \
+               new_size.x = max_panel_width; \
+               new_size.y = panel_size.y * (new_size.x / panel_size.x); \
        } \
-       vector menu_enable_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * menu_enable_size; \
-       panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * menu_enable_pos; \
-       panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * menu_enable_size; \
+       vector new_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * new_size; \
+       panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * new_pos; \
+       panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * new_size; \
 } MACRO_END
 
 // Scale the pos and size vectors to absolute coordinates
@@ -371,61 +365,19 @@ REGISTER_HUD_PANEL(QUICKMENU,       HUD_QuickMenu,      quickmenu,      PANEL_CO
        panel_size.x *= vid_conwidth; panel_size.y *= vid_conheight;                                                    \
 } MACRO_END
 
-// NOTE: in hud_configure mode cvars must be reloaded every frame
-#define HUD_Panel_UpdateCvars() MACRO_BEGIN {                                                                       \
-       if (panel.update_time <= time) {                                                                                \
-               if (autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name));                  \
-               panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos")));                              \
-               panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size")));                            \
-               HUD_Panel_ScalePosSize();                                                                                   \
-               panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg"));                                  \
-               panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color"));                      \
-               panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team"));            \
-               panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha"));                      \
-               panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border"));                    \
-               panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding"));                  \
-               HUD_Panel_GetBg();                                                                                          \
-               if (panel.current_panel_bg != "0") {                                                                        \
-                       HUD_Panel_GetColorTeam();                                                                               \
-                       HUD_Panel_GetColor();                                                                                   \
-                       HUD_Panel_GetBgAlpha();                                                                                 \
-                       HUD_Panel_GetBorder();                                                                                  \
-               }                                                                                                           \
-               HUD_Panel_GetFgAlpha();                                                                                     \
-               HUD_Panel_GetPadding();                                                                                     \
-               panel.current_panel_bg_alpha = panel_bg_alpha;                                                              \
-               panel.current_panel_fg_alpha = panel_fg_alpha;                                                              \
-               if (menu_enabled == 2 && panel == highlightedPanel) {                                                       \
-                       HUD_Panel_UpdatePosSize_ForMenu();                                                                      \
-               } else {                                                                                                    \
-                       panel_bg_alpha *= hud_fade_alpha;                                                                       \
-                       panel_fg_alpha *= hud_fade_alpha;                                                                       \
-               }                                                                                                           \
-               panel.current_panel_pos = panel_pos;                                                                        \
-               panel.current_panel_size = panel_size;                                                                      \
-               panel.current_panel_bg_border = panel_bg_border;                                                            \
-               panel.current_panel_bg_color = panel_bg_color;                                                              \
-               panel.current_panel_bg_color_team = panel_bg_color_team;                                                    \
-               panel.current_panel_bg_padding = panel_bg_padding;                                                          \
-               panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval;           \
-       } else {                                                                                                        \
-               panel_pos = panel.current_panel_pos;                                                                        \
-               panel_size = panel.current_panel_size;                                                                      \
-               panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha;                                             \
-               panel_bg_border = panel.current_panel_bg_border;                                                            \
-               panel_bg_color = panel.current_panel_bg_color;                                                              \
-               panel_bg_color_team = panel.current_panel_bg_color_team;                                                    \
-               panel_bg_padding = panel.current_panel_bg_padding;                                                          \
-               panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha;                                             \
-       }                                                                                                               \
-} MACRO_END
+float panel_fade_alpha;
+void HUD_Panel_LoadCvars();
+
+#define Hud_Panel_GetPanelEnabled() \
+       panel_enabled = ((panel.panel_configflags & PANEL_CONFIG_CANBEOFF) \
+                                       ? cvar(strcat("hud_panel_", panel.panel_name)) : true)
 
 #define HUD_Panel_UpdatePosSize() MACRO_BEGIN {                                                                     \
-       panel_enabled = cvar(strcat("hud_panel_", panel.panel_name));                                                   \
+       Hud_Panel_GetPanelEnabled(); \
        panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos")));                                  \
        panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size")));                                \
        HUD_Panel_ScalePosSize();                                                                                       \
-       if (menu_enabled == 2 && panel == highlightedPanel) {                                                           \
+       if (hud_configure_menu_open == 2 && panel == highlightedPanel) {                                                           \
                HUD_Panel_UpdatePosSize_ForMenu();                                                                          \
        }                                                                                                               \
        panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border"));                        \
index f45a052aa1859d94ca8e9d5c2658f4f9d5178865..f2b23e39256217b9f33019b51c2e94bdd1e8b59a 100644 (file)
@@ -1,6 +1,7 @@
 #include "hud_config.qh"
 
 #include "hud.qh"
+#include "panel/scoreboard.qh"
 
 #define HUD_Write(s) fputs(fh, s)
 #define HUD_Write_Cvar(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n"))
@@ -207,6 +208,21 @@ void HUD_Panel_ExportCfg(string cfgname)
                                case HUD_PANEL_QUICKMENU:
                                        HUD_Write_PanelCvar("_align");
                                        break;
+                               case HUD_PANEL_SCOREBOARD:
+                                       HUD_Write_PanelCvar("_fadeinspeed");
+                                       HUD_Write_PanelCvar("_fadeoutspeed");
+                                       HUD_Write_PanelCvar("_respawntime_decimals");
+                                       HUD_Write_PanelCvar("_table_bg_alpha");
+                                       HUD_Write_PanelCvar("_table_bg_scale");
+                                       HUD_Write_PanelCvar("_table_fg_alpha");
+                                       HUD_Write_PanelCvar("_table_fg_alpha_self");
+                                       HUD_Write_PanelCvar("_table_highlight");
+                                       HUD_Write_PanelCvar("_table_highlight_alpha");
+                                       HUD_Write_PanelCvar("_table_highlight_alpha_self");
+                                       HUD_Write_PanelCvar("_bg_teams_color_team");
+                                       HUD_Write_PanelCvar("_accuracy_doublerows");
+                                       HUD_Write_PanelCvar("_accuracy_nocolors");
+                                       break;
                        }
                        HUD_Write("\n");
                }
@@ -221,9 +237,9 @@ void HUD_Panel_ExportCfg(string cfgname)
 
 void HUD_Configure_Exit_Force()
 {
-       if (menu_enabled)
+       if (hud_configure_menu_open)
        {
-               menu_enabled = 0;
+               hud_configure_menu_open = 0;
                localcmd("togglemenu\n");
        }
        cvar_set("_hud_configure", "0");
@@ -671,10 +687,8 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
        }
 
        // allow console bind to work
-       string con_keys;
-       float keys;
-       con_keys = findkeysforcommand("toggleconsole", 0);
-       keys = tokenize(con_keys); // findkeysforcommand returns data for this
+       string con_keys = findkeysforcommand("toggleconsole", 0);
+       int keys = tokenize(con_keys); // findkeysforcommand returns data for this
 
        bool hit_con_bind = false;
        int i;
@@ -729,14 +743,14 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
        {
                if (bInputType == 1)
                        return true;
-               menu_enabled = 1;
+               hud_configure_menu_open = 1;
                localcmd("menu_showhudexit\n");
        }
        else if(nPrimary == K_BACKSPACE && hudShiftState & S_CTRL)
        {
                if (bInputType == 1)
                        return true;
-               if (!menu_enabled)
+               if (!hud_configure_menu_open)
                        cvar_set("_hud_configure", "0");
        }
        else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel
@@ -834,7 +848,10 @@ LABEL(find_tab_panel)
                        return true;
 
                if (highlightedPanel)
-                       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name))));
+               {
+                       if(panel.panel_configflags & PANEL_CONFIG_CANBEOFF)
+                               cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name))));
+               }
                else
                        cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : "");
        }
@@ -1084,7 +1101,7 @@ void HUD_Panel_Highlight(float allow_move)
 
 void HUD_Panel_EnableMenu()
 {
-       menu_enabled = 2;
+       hud_configure_menu_open = 2;
        localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n");
 }
 float mouse_over_panel;
@@ -1094,12 +1111,7 @@ void HUD_Panel_Mouse()
                return;
 
        if (!autocvar_hud_cursormode)
-       {
-               mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
-
-               mousepos.x = bound(0, mousepos.x, vid_conwidth);
-               mousepos.y = bound(0, mousepos.y, vid_conheight);
-       }
+               update_mousepos();
 
        if(mouseClicked)
        {
@@ -1183,7 +1195,7 @@ void HUD_Panel_Mouse()
        {
                if(prevMouseClicked)
                        highlightedAction = 0;
-               if(menu_enabled == 2)
+               if(hud_configure_menu_open == 2)
                        mouse_over_panel = 0;
                else
                        mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true);
@@ -1231,7 +1243,7 @@ void HUD_Configure_Frame()
        int i;
        if(autocvar__hud_configure)
        {
-               if(isdemo() || intermission == 2)
+               if(isdemo() || intermission == 2 || scoreboard_active)
                {
                        HUD_Configure_Exit_Force();
                        return;
@@ -1250,7 +1262,7 @@ void HUD_Configure_Frame()
                if(autocvar__menu_alpha != _menu_alpha_prev)
                {
                        if(autocvar__menu_alpha == 0)
-                               menu_enabled = 0;
+                               hud_configure_menu_open = 0;
                        _menu_alpha_prev = autocvar__menu_alpha;
                }
 
@@ -1258,8 +1270,8 @@ void HUD_Configure_Frame()
        }
        else if(hud_configure_prev)
        {
-               if(menu_enabled)
-                       menu_enabled = 0;
+               if(hud_configure_menu_open)
+                       hud_configure_menu_open = 0;
                if(autocvar_hud_cursormode)
                        setcursormode(0);
                hud_dynamic_shake_factor = -1;
index 32c846076145def23fbe3bb769dfbbec2bb63473..6ab64f6aed2a7548f5b51dc36bbb012713c8c9e7 100644 (file)
@@ -8,6 +8,12 @@ int prevMouseClicked; // previous state
 float prevMouseClickedTime; // time during previous left mouse click, to check for doubleclicks
 vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks
 
+float hud_configure_prev;
+float hud_configure_checkcollisions;
+vector hud_configure_gridSize;
+vector hud_configure_realGridSize;
+float hud_configure_menu_open; // 1 showing the entire HUD, 2 showing only the clicked panel
+
 void HUD_Panel_ExportCfg(string cfgname);
 
 void HUD_Panel_Mouse();
diff --git a/qcsrc/client/hud/panel.qc b/qcsrc/client/hud/panel.qc
new file mode 100644 (file)
index 0000000..558f320
--- /dev/null
@@ -0,0 +1 @@
+#include "panel.qh"
index 5681474b84e75ea22e1894ddfd1aced1948f03b0..7a95752132c43f3764da1a067db10667ba87c4bd 100644 (file)
@@ -15,6 +15,7 @@
 #include <client/hud/panel/racetimer.qc>
 #include <client/hud/panel/radar.qc>
 #include <client/hud/panel/score.qc>
+#include <client/hud/panel/scoreboard.qc>
 #include <client/hud/panel/timer.qc>
 #include <client/hud/panel/vote.qc>
 #include <client/hud/panel/weapons.qc>
index 930a3fe02ba8ccc73879bf98039e5b3845e53a3e..c24b5c0002876d29d4beca731325ca828004c4e3 100644 (file)
@@ -15,6 +15,7 @@
 #include <client/hud/panel/racetimer.qh>
 #include <client/hud/panel/radar.qh>
 #include <client/hud/panel/score.qh>
+#include <client/hud/panel/scoreboard.qh>
 #include <client/hud/panel/timer.qh>
 #include <client/hud/panel/vote.qh>
 #include <client/hud/panel/weapons.qh>
index 5c45ff0f331a1544be55bf6b4f3a262d7f704022..e299d1416ea1485da0cd4dee153aa1afc835dd4f 100644 (file)
@@ -98,11 +98,13 @@ void HUD_Ammo()
        if(hud != HUD_NORMAL) return;
        if(!autocvar__hud_configure)
        {
-               if(!autocvar_hud_panel_ammo) return;
-               if(spectatee_status == -1) return;
+               if((!autocvar_hud_panel_ammo) || (spectatee_status == -1))
+                       return;
+               if(STAT(HEALTH) < 1 && autocvar_hud_panel_ammo_hide_ondeath)
+                       return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        draw_beginBoldFont();
 
@@ -114,7 +116,7 @@ void HUD_Ammo()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
index 6db88c68b39ee50fce1f8acce9c778465a3223ae..71919d2fb094d48f6c0967de3b4a53cd39ffc453 100644 (file)
@@ -1,2 +1,4 @@
 #pragma once
 #include "../panel.qh"
+
+void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color);
index afda329838c623f9d1e8b98712de71b82d8f5014..f67cbffbd04d176160d072b1e6bddcbfe3e045b0 100644 (file)
@@ -1,6 +1,7 @@
 #include "centerprint.qh"
 
-#include <client/scoreboard.qh>
+#include "scoreboard.qh"
+#include <common/notifications/all.qh>
 
 // CenterPrint (#16)
 
@@ -57,8 +58,7 @@ void centerprint_generic(int new_id, string strMessage, float duration, int coun
                        {
                                // fade out the current msg (duration and countdown_num are ignored)
                                centerprint_time[j] = min(5, autocvar_hud_panel_centerprint_fade_out);
-                               if (centerprint_expire_time[j] > time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time)
-                                       centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out);
+                               centerprint_expire_time[j] = -1; // don't use the variable time here!
                                return;
                        }
                        break; // found a msg with the same id, at position j
@@ -80,14 +80,14 @@ void centerprint_generic(int new_id, string strMessage, float duration, int coun
        if (duration < 0)
        {
                centerprint_time[j] = -1;
-               centerprint_expire_time[j] = time;
+               centerprint_expire_time[j] = -1; // don't use the variable time here!
        }
        else
        {
                if(duration == 0)
                        duration = max(1, autocvar_hud_panel_centerprint_time);
                centerprint_time[j] = duration;
-               centerprint_expire_time[j] = time + duration;
+               centerprint_expire_time[j] = -1; // don't use the variable time here!
        }
        centerprint_countdown_num[j] = countdown_num;
 }
@@ -128,7 +128,10 @@ void HUD_CenterPrint ()
        else
        {
                if(!hud_configure_prev)
+               {
                        reset_centerprint_messages();
+                       hud_configure_cp_generation_time = time; // show a message immediately
+               }
                if (time > hud_configure_cp_generation_time)
                {
                        if(highlightedPanel == HUD_PANEL(CENTERPRINT))
@@ -151,14 +154,7 @@ void HUD_CenterPrint ()
                }
        }
 
-       // this panel fades only when the menu does
-       float hud_fade_alpha_save = 0;
-       if(scoreboard_fade_alpha)
-       {
-               hud_fade_alpha_save = hud_fade_alpha;
-               hud_fade_alpha = 1 - autocvar__menu_alpha;
-       }
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        if ( HUD_Radar_Clickable() )
        {
@@ -168,17 +164,13 @@ void HUD_CenterPrint ()
                panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x);
                panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom);
        }
-       else if(scoreboard_fade_alpha)
+       else if(!autocvar__hud_configure && scoreboard_fade_alpha)
        {
-               hud_fade_alpha = hud_fade_alpha_save;
-
                // move the panel below the scoreboard
                if (scoreboard_bottom >= 0.96 * vid_conheight)
                        return;
                vector target_pos;
-
                target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x);
-
                if(target_pos.y > panel_pos.y)
                {
                        panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha);
@@ -190,7 +182,7 @@ void HUD_CenterPrint ()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if (!centerprint_showing)
                return;
@@ -225,6 +217,13 @@ void HUD_CenterPrint ()
        {
                if (j == CENTERPRINT_MAX_MSGS)
                        j = 0;
+               if (centerprint_expire_time[j] == -1)
+               {
+                       // here we are sure the time variable is not altered by CSQC_Ent_Update
+                       centerprint_expire_time[j] = time;
+                       if (centerprint_time[j] > 0)
+                               centerprint_expire_time[j] += centerprint_time[j];
+               }
                if (centerprint_expire_time[j] <= time)
                {
                        if (centerprint_countdown_num[j] && centerprint_time[j] > 0)
@@ -248,6 +247,9 @@ void HUD_CenterPrint ()
                else // Expiring soon, so fade it out.
                        a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
 
+               if(centerprint_msgID[j] == CPID_TIMEIN)
+                       a = 1;
+
                // while counting down show it anyway in order to hold the current message position
                if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0)  // Guaranteed invisible - don't show.
                        continue;
@@ -260,12 +262,14 @@ void HUD_CenterPrint ()
                // also fade it based on positioning
                if(autocvar_hud_panel_centerprint_fade_subsequent)
                {
-                       a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha
-                       a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message
+                       // pass one: all messages after the first have half alpha
+                       a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1);
+                       // pass two: after that, gradually lower alpha even more for each message
+                       a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1);
                }
                a *= panel_fg_alpha;
 
-               // finally set the size based on the new theAlpha from subsequent fading
+               // finally set the size based on the new alpha from subsequent fading
                sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize));
                drawfontscale = hud_scale * sz;
 
index 6db88c68b39ee50fce1f8acce9c778465a3223ae..1bec93efa644c684b35340f4973280742b259925 100644 (file)
@@ -1,2 +1,4 @@
 #pragma once
 #include "../panel.qh"
+
+void reset_centerprint_messages();
index a27e7b9070f109e2786edde44affdf1ff7f9193d..554f44e17ec759fa6da826b543672399405f8ee6 100644 (file)
@@ -1,5 +1,7 @@
 #include "chat.qh"
-/** Handle chat as a panel (#12) */
+
+// Chat (#12)
+
 void HUD_Chat()
 {
        if(!autocvar__hud_configure)
@@ -21,7 +23,7 @@ void HUD_Chat()
                }
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        if(intermission == 2)
        {
@@ -47,7 +49,7 @@ void HUD_Chat()
                        panel.current_panel_bg = strzone(panel_bg);
                        chat_panel_modified = true;
                }
-               panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75
+               panel_bg_alpha = max(0.75, panel_bg_alpha);
        }
 
        vector pos, mySize;
@@ -56,7 +58,7 @@ void HUD_Chat()
 
        // chat messages don't scale properly since they are displayed directly by the engine
        HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if(panel_bg_padding)
        {
@@ -75,17 +77,13 @@ void HUD_Chat()
 
        if(autocvar__hud_configure)
        {
-               vector chatsize;
-               chatsize = '1 1 0' * autocvar_con_chatsize;
-               cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such
-               float i, a;
-               for(i = 0; i < autocvar_con_chat; ++i)
+               vector chatsize = '1 1 0' * autocvar_con_chatsize;
+               cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over alpha and such
+               string str = textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors);
+               for(int i = 0; i < autocvar_con_chat; ++i)
                {
-                       if(i == autocvar_con_chat - 1)
-                               a = panel_fg_alpha;
-                       else
-                               a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45);
-                       drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL);
+                       // engine displays chat text at full alpha
+                       drawcolorcodedstring(pos, str, chatsize, 1, DRAWFLAG_NORMAL);
                        pos.y += chatsize.y;
                }
        }
index 773c751d82680fd826b00ece67514a3891134935..c8b7203eee9cfc240c09ac0556c3709aeaea7b32 100644 (file)
@@ -1,5 +1,6 @@
 #include "engineinfo.qh"
-// Engine info panel (#13)
+
+// Engine info (#13)
 
 float prevfps;
 float prevfps_time;
@@ -18,7 +19,7 @@ void HUD_EngineInfo()
                if(!autocvar_hud_panel_engineinfo) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -27,7 +28,7 @@ void HUD_EngineInfo()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
index bc4291a948648e8eb5f8cf843c683cdc17d7ba1c..4fad2c7c43c56342337764cc1fa323b2791a6248 100644 (file)
@@ -2,25 +2,28 @@
 
 #include <common/deathtypes/all.qh>
 
-/** Health/armor (#3) */
+// Health/armor (#3)
+
 void HUD_HealthArmor()
 {
        int armor, health, fuel;
        if(!autocvar__hud_configure)
        {
-               if(!autocvar_hud_panel_healtharmor) return;
+               if((!autocvar_hud_panel_healtharmor) || (spectatee_status == -1))
+                       return;
                if(hud != HUD_NORMAL) return;
-               if(spectatee_status == -1) return;
 
                health = STAT(HEALTH);
                if(health <= 0)
                {
+                       health = 0;
                        prev_health = -1;
-                       return;
+                       if(autocvar_hud_panel_healtharmor_hide_ondeath)
+                               return;
                }
                armor = STAT(ARMOR);
 
-               // code to check for spectatee_status changes is in Ent_ClientData()
+               // code to check for spectatee_status changes is in ENT_CLIENT_CLIENTDATA
                // prev_p_health and prev_health can be set to -1 there
 
                if (prev_p_health == -1)
@@ -54,7 +57,7 @@ void HUD_HealthArmor()
                fuel = 20;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        draw_beginBoldFont();
 
@@ -66,7 +69,7 @@ void HUD_HealthArmor()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
@@ -83,8 +86,7 @@ void HUD_HealthArmor()
                vector v;
                v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON.m_id);
 
-               float x;
-               x = floor(v.x + 1);
+               float hp = floor(v.x + 1);
 
         float maxtotal = maxhealth + maxarmor;
                string biggercount;
@@ -92,7 +94,7 @@ void HUD_HealthArmor()
                {
                        biggercount = "health";
                        if(autocvar_hud_panel_healtharmor_progressbar)
-                               HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+                               HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, hp/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
                        if(armor)
             if(autocvar_hud_panel_healtharmor_text)
                                drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL);
@@ -101,13 +103,13 @@ void HUD_HealthArmor()
                {
                        biggercount = "armor";
                        if(autocvar_hud_panel_healtharmor_progressbar)
-                               HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+                               HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, hp/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
                        if(health)
             if(autocvar_hud_panel_healtharmor_text)
                                drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
                }
         if(autocvar_hud_panel_healtharmor_text)
-                       DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1);
+                       DrawNumIcon(pos, mySize, hp, biggercount, 0, iconalign, HUD_Get_Num_Color(hp, maxtotal), 1);
 
                if(fuel)
                        HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
index a197963e75492145545a315b9de468585059cdce..2d6c952fc0f3764cfced6e99448be1a14628dd47 100644 (file)
@@ -3,14 +3,56 @@
 #include <common/ent_cs.qh>
 #include <common/mapinfo.qh>
 
-// Info messages panel (#14)
+// Info messages (#14)
 
-#define drawInfoMessage(s) MACRO_BEGIN {                                                                                                                                                       \
-       if(autocvar_hud_panel_infomessages_flip)                                                                                                                                                \
-               o.x = pos.x + mySize.x - stringwidth(s, true, fontsize);                                                                                                        \
-       drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL);                                                                                                               \
-       o.y += fontsize.y;                                                                                                                                                                                              \
+float autocvar_hud_panel_infomessages_group0 = 1;
+float autocvar_hud_panel_infomessages_group_fadetime = 0.4;
+float autocvar_hud_panel_infomessages_group_time = 6;
+const int IMG_COUNT = 1; // number of InfoMessage Groups
+float img_fade[IMG_COUNT];
+int img_cur_msg[IMG_COUNT];
+float img_time[IMG_COUNT];
+
+int img_select(int group_id)
+{
+       float fadetime = max(0.001, autocvar_hud_panel_infomessages_group_fadetime);
+       if(time > img_time[group_id])
+       {
+               img_fade[group_id] = max(0, img_fade[group_id] - frametime / fadetime);
+               if(!img_fade[group_id])
+               {
+                       ++img_cur_msg[group_id];
+                       img_time[group_id] = floor(time) + autocvar_hud_panel_infomessages_group_time;
+               }
+       }
+       else
+               img_fade[group_id] = min(1, img_fade[group_id] + frametime / fadetime);
+       return img_cur_msg[group_id];
+}
+
+float stringwidth_colors(string s, vector theSize);
+vector InfoMessages_drawstring(string s, vector pos, vector sz, float a, vector fontsize)
+{
+       getWrappedLine_remaining = s;
+       float offset = 0;
+       while(getWrappedLine_remaining)
+       {
+               s = getWrappedLine(sz.x - offset, fontsize, stringwidth_colors);
+               if(autocvar_hud_panel_infomessages_flip)
+                       offset = sz.x - stringwidth_colors(s, fontsize) - offset;
+               drawcolorcodedstring(pos + eX * offset, s, fontsize, a, DRAWFLAG_NORMAL);
+               pos.y += fontsize.y;
+               offset = fontsize.x;
+       }
+       pos.y += fontsize.y * 0.25;
+       return pos;
+}
+
+#define InfoMessage(s) MACRO_BEGIN { \
+       pos = InfoMessages_drawstring(s, pos, mySize, ((img_curr_group >= 0) ? panel_fg_alpha * img_fade[img_curr_group] : panel_fg_alpha), fontsize); \
+       img_curr_group = -1; \
 } MACRO_END
+
 void HUD_InfoMessages()
 {
        if(!autocvar__hud_configure)
@@ -18,7 +60,7 @@ void HUD_InfoMessages()
                if(!autocvar_hud_panel_infomessages) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -27,82 +69,67 @@ void HUD_InfoMessages()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
                mySize -= '2 2 0' * panel_bg_padding;
        }
 
-       // always force 5:1 aspect
-       vector newSize = '0 0 0';
-       if(mySize.x/mySize.y > 5)
-       {
-               newSize.x = 5 * mySize.y;
-               newSize.y = mySize.y;
-
-               pos.x = pos.x + (mySize.x - newSize.x) / 2;
-       }
-       else
-       {
-               newSize.y = 1/5 * mySize.x;
-               newSize.x = mySize.x;
-
-               pos.y = pos.y + (mySize.y - newSize.y) / 2;
-       }
-
-       mySize = newSize;
-       entity tm;
-       vector o;
-       o = pos;
-
-       vector fontsize;
-       fontsize = '0.20 0.20 0' * mySize.y;
-
-       float a;
-       a = panel_fg_alpha;
-
+       vector fontsize = '0.2 0.2 0' * mySize.y;
        string s;
+       int img_curr_group = -1;
        if(!autocvar__hud_configure)
        {
                if(spectatee_status)
                {
-                       a = 1;
                        if(spectatee_status == -1)
                                s = _("^1Observing");
                        else
                                s = sprintf(_("^1Spectating: ^7%s"), entcs_GetName(current_player));
-                       drawInfoMessage(s);
+                       InfoMessage(s);
 
-                       if(spectatee_status == -1)
-                               s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
-                       else
-                               s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
-                       drawInfoMessage(s);
-
-                       if(spectatee_status == -1)
-                               s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
-                       else
-                               s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2"));
-                       drawInfoMessage(s);
+                       if(autocvar_hud_panel_infomessages_group0)
+                       {
+                               img_curr_group = 0;
+                               switch(img_select(img_curr_group) % 3)
+                               {
+                                       default:
+                                       case 0:
+                                               if(spectatee_status == -1)
+                                                       s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey(_("primary fire"), "+fire"));
+                                               else
+                                                       s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey(_("next weapon"), "weapnext"), getcommandkey(_("previous weapon"), "weapprev"));
+                                               break;
+                                       case 1:
+                                               if(spectatee_status == -1)
+                                                       s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey(_("next weapon"), "weapnext"), getcommandkey(_("previous weapon"), "weapprev"));
+                                               else
+                                                       s = sprintf(_("^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode"), getcommandkey(_("secondary fire"), "+fire2"), getcommandkey(_("drop weapon"), "dropweapon"));
+                                               break;
+                                       case 2:
+                                               s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey(_("server info"), "+show_info"));
+                                               break;
+                               }
+                               InfoMessage(s);
+                       }
 
-                       s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
-                       drawInfoMessage(s);
+                       MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize);
 
-                       if(gametype == MAPINFO_TYPE_LMS)
+                       if(!warmup_stage && gametype == MAPINFO_TYPE_LMS)
                        {
                                entity sk;
                                sk = playerslots[player_localnum];
-                               if(sk.(scores[ps_primary]) >= 666)
+                               if(sk.(scores(ps_primary)) >= 666)
                                        s = _("^1Match has already begun");
-                               else if(sk.(scores[ps_primary]) > 0)
+                               else if(sk.(scores(ps_primary)) > 0)
                                        s = _("^1You have no more lives left");
                                else
-                                       s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+                                       s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump"));
                        }
                        else
-                               s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
-                       drawInfoMessage(s);
+                               s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump"));
+                       InfoMessage(s);
                }
 
                if (time < STAT(GAMESTARTTIME))
@@ -110,13 +137,13 @@ void HUD_InfoMessages()
                        //we need to ceil, otherwise the countdown would be off by .5 when using round()
                        float countdown = ceil(STAT(GAMESTARTTIME) - time);
                        s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
-                       drawInfoMessage(s);
+                       InfoMessage(s);
                }
 
                if(warmup_stage)
                {
                        s = _("^2Currently in ^1warmup^2 stage!");
-                       drawInfoMessage(s);
+                       InfoMessage(s);
                }
 
                string blinkcolor;
@@ -130,9 +157,9 @@ void HUD_InfoMessages()
                        if(ready_waiting_for_me)
                        {
                                if(warmup_stage)
-                                       s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+                                       s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey(_("ready"), "ready"), blinkcolor);
                                else
-                                       s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+                                       s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey(_("ready"), "ready"), blinkcolor);
                        }
                        else
                        {
@@ -141,18 +168,18 @@ void HUD_InfoMessages()
                                else
                                        s = _("^2Waiting for others to ready up...");
                        }
-                       drawInfoMessage(s);
+                       InfoMessage(s);
                }
                else if(warmup_stage && !spectatee_status)
                {
-                       s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
-                       drawInfoMessage(s);
+                       s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey(_("ready"), "ready"));
+                       InfoMessage(s);
                }
 
                if(teamplay && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
                {
                        float ts_min = 0, ts_max = 0;
-                       tm = teams.sort_next;
+                       entity tm = teams.sort_next;
                        if (tm)
                        {
                                for (; tm.sort_next; tm = tm.sort_next)
@@ -168,24 +195,36 @@ void HUD_InfoMessages()
                                {
                                        s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
                                        tm = GetTeam(myteam, false);
-                                       if (tm)
-                                       if (tm.team != NUM_SPECTATOR)
-                                       if (tm.team_size == ts_max)
-                                               s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
-                                       drawInfoMessage(s);
+                                       if (tm && tm.team != NUM_SPECTATOR && tm.team_size == ts_max)
+                                               s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey(_("team menu"), "menu_showteamselect"), blinkcolor));
+                                       InfoMessage(s);
                                }
                        }
                }
+
+               if(autocvar_cl_showspectators)
+               if(num_spectators)
+               //if(spectatee_status != -1)
+               {
+                       s = ((spectatee_status) ? _("^1Spectating this player:") : _("^1Spectating you:"));
+                       // InfoMessage(s)
+                       int limit = min(num_spectators, MAX_SPECTATORS);
+                       for(int i = 0; i < limit; ++i)
+                       {
+                               float slot = spectatorlist[i];
+                               if(i == 0)
+                                       s = strcat(s, " ^7", entcs_GetName(slot));
+                               else
+                                       s = strcat("^7", entcs_GetName(slot));
+                               InfoMessage(s);
+                       }
+               }
        }
        else
        {
-               s = _("^7Press ^3ESC ^7to show HUD options.");
-               drawInfoMessage(s);
-               s = _("^3Doubleclick ^7a panel for panel-specific options.");
-               drawInfoMessage(s);
-               s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
-               drawInfoMessage(s);
-               s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
-               drawInfoMessage(s);
+               InfoMessage(_("^7Press ^3ESC ^7to show HUD options."));
+               InfoMessage(_("^3Doubleclick ^7a panel for panel-specific options."));
+               InfoMessage(_("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and"));
+               InfoMessage(_("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments."));
        }
 }
index 933528a495f72f20d47b1f76dae0e9c50d05af4c..618f30b1080fa67ae0f4285ee7cc214313bcb0c5 100644 (file)
@@ -1,4 +1,5 @@
 #include "minigame.qh"
-// Minigame
+
+// Minigame (#17, #18, #19, #20)
 
 #include <common/minigames/cl_minigames_hud.qc>
index 54c835831dec0b27510d9d41953fd2ac8c265955..bf302fdfa3f3cff6f2fcc701e269821a4a8b1f7f 100644 (file)
@@ -4,7 +4,7 @@
 #include <common/ent_cs.qh>
 #include <server/mutators/mutator/gamemode_ctf.qh> // TODO: remove
 
-// Mod icons panel (#10)
+// Mod icons (#10)
 
 bool mod_active; // is there any active mod icon?
 
@@ -16,27 +16,11 @@ void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int
        vector color = '0 0 0';
        switch(i)
        {
-               case 0:
-                       stat = STAT(REDALIVE);
-                       pic = "player_red.tga";
-                       color = '1 0 0';
-                       break;
-               case 1:
-                       stat = STAT(BLUEALIVE);
-                       pic = "player_blue.tga";
-                       color = '0 0 1';
-                       break;
-               case 2:
-                       stat = STAT(YELLOWALIVE);
-                       pic = "player_yellow.tga";
-                       color = '1 1 0';
-                       break;
+               case 0: stat = STAT(REDALIVE); pic = "player_red"; color = '1 0 0'; break;
+               case 1: stat = STAT(BLUEALIVE); pic = "player_blue"; color = '0 0 1'; break;
+               case 2: stat = STAT(YELLOWALIVE); pic = "player_yellow"; color = '1 1 0'; break;
                default:
-               case 3:
-                       stat = STAT(PINKALIVE);
-                       pic = "player_pink.tga";
-                       color = '1 0 1';
-                       break;
+               case 3: stat = STAT(PINKALIVE); pic = "player_pink"; color = '1 0 1'; break;
        }
 
        if(mySize.x/mySize.y > aspect_ratio)
@@ -108,6 +92,7 @@ void HUD_Mod_CTF_Reset()
        redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
 }
 
+int autocvar__teams_available;
 void HUD_Mod_CTF(vector pos, vector mySize)
 {
        vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
@@ -117,10 +102,13 @@ void HUD_Mod_CTF(vector pos, vector mySize)
        int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
        float redflag_statuschange_elapsedtime = 0, blueflag_statuschange_elapsedtime = 0, yellowflag_statuschange_elapsedtime = 0, pinkflag_statuschange_elapsedtime = 0, neutralflag_statuschange_elapsedtime = 0; // time since the status changed
        bool ctf_oneflag; // one-flag CTF mode enabled/disabled
+       bool ctf_stalemate; // currently in stalemate
        int stat_items = STAT(CTF_FLAGSTATUS);
        float fs, fs2, fs3, size1, size2;
        vector e1, e2;
 
+       int nteams = autocvar__teams_available;
+
        redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
        blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
        yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
@@ -129,14 +117,16 @@ void HUD_Mod_CTF(vector pos, vector mySize)
 
        ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL);
 
+       ctf_stalemate = (stat_items & CTF_STALEMATE);
+
        mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag || (stat_items & CTF_SHIELDED));
 
        if (autocvar__hud_configure) {
                redflag = 1;
                blueflag = 2;
-               if (team_count >= 3)
+               if (nteams & BIT(2))
                        yellowflag = 2;
-               if (team_count >= 4)
+               if (nteams & BIT(3))
                        pinkflag = 3;
                ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
        }
@@ -198,18 +188,24 @@ void HUD_Mod_CTF(vector pos, vector mySize)
                                break; \
                } \
        } MACRO_END
-       X(red, myteam != NUM_TEAM_1);
-       X(blue, myteam != NUM_TEAM_2);
-       X(yellow, myteam != NUM_TEAM_3 && team_count >= 3);
-       X(pink, myteam != NUM_TEAM_4 && team_count >= 4);
+       X(red, myteam != NUM_TEAM_1 && (nteams & BIT(0)));
+       X(blue, myteam != NUM_TEAM_2 && (nteams & BIT(1)));
+       X(yellow, myteam != NUM_TEAM_3 && (nteams & BIT(2)));
+       X(pink, myteam != NUM_TEAM_4 && (nteams & BIT(3)));
        X(neutral, ctf_oneflag);
        #undef X
 
+       int tcount = 2;
+       if(nteams & BIT(2))
+               tcount = 3;
+       if(nteams & BIT(3))
+               tcount = 4;
+
        if (ctf_oneflag) {
                // hacky, but these aren't needed
                red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
                fs = fs2 = fs3 = 1;
-       } else switch (team_count) {
+       } else switch (tcount) {
                default:
                case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
                case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
@@ -264,6 +260,8 @@ void HUD_Mod_CTF(vector pos, vector mySize)
 
        #define X(team) MACRO_BEGIN { \
                f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \
+               if (team##_icon && ctf_stalemate) \
+                       drawpic_aspect_skin(team##flag_pos, "flag_stalemate", flag_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); \
                if (team##_icon_prevstatus && f < 1) \
                        drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \
                if (team##_icon) \
@@ -285,8 +283,9 @@ void HUD_Mod_KH(vector pos, vector mySize)
        mod_active = 1; // keyhunt should never hide the mod icons panel
 
        // Read current state
-
        int state = STAT(KH_KEYS);
+       if(!state) return;
+
        int i, key_state;
        int all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
        all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
@@ -317,9 +316,7 @@ void HUD_Mod_KH(vector pos, vector mySize)
        }
 
        // Calculate slot measurements
-
        vector slot_size;
-
        if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x)
        {
                // Quadratic arrangement
@@ -349,11 +346,10 @@ void HUD_Mod_KH(vector pos, vector mySize)
 
        // Make icons blink in case of RUN HERE
 
-       float blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1
-       float alpha;
-       alpha = 1;
-
+       float alpha = 1;
        if(carrying_keys)
+       {
+               float blink = 0.6 + sin(2 * M_PI * time) * 0.4; // Oscillate between 0.2 and 1
                switch(myteam)
                {
                        case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break;
@@ -361,6 +357,7 @@ void HUD_Mod_KH(vector pos, vector mySize)
                        case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break;
                        case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break;
                }
+       }
 
        // Draw icons
 
@@ -518,9 +515,9 @@ void HUD_Mod_Race(vector pos, vector mySize)
        entity me;
        me = playerslots[player_localnum];
        float score;
-       score = me.(scores[ps_primary]);
+       score = me.(scores(ps_primary));
 
-       if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD
+       if(!(scores_flags(ps_primary) & SFL_TIME) || teamplay) // race/cts record display on HUD
                return; // no records in the actual race
 
        // clientside personal record
@@ -640,29 +637,15 @@ void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, in
        vector color = '0 0 0';
        switch(i)
        {
-               case 0:
-                       stat = STAT(DOM_PPS_RED);
-                       pic = "dom_icon_red";
-                       color = '1 0 0';
-                       break;
-               case 1:
-                       stat = STAT(DOM_PPS_BLUE);
-                       pic = "dom_icon_blue";
-                       color = '0 0 1';
-                       break;
-               case 2:
-                       stat = STAT(DOM_PPS_YELLOW);
-                       pic = "dom_icon_yellow";
-                       color = '1 1 0';
-                       break;
+               case 0: stat = STAT(DOM_PPS_RED); pic = "dom_icon_red"; color = '1 0 0'; break;
+               case 1: stat = STAT(DOM_PPS_BLUE); pic = "dom_icon_blue"; color = '0 0 1'; break;
+               case 2: stat = STAT(DOM_PPS_YELLOW); pic = "dom_icon_yellow"; color = '1 1 0'; break;
                default:
-               case 3:
-                       stat = STAT(DOM_PPS_PINK);
-                       pic = "dom_icon_pink";
-                       color = '1 0 1';
-                       break;
+               case 3: stat = STAT(DOM_PPS_PINK); pic = "dom_icon_pink"; color = '1 0 1'; break;
        }
-       float pps_ratio = stat / STAT(DOM_TOTAL_PPS);
+       float pps_ratio = 0;
+       if(STAT(DOM_TOTAL_PPS))
+               pps_ratio = stat / STAT(DOM_TOTAL_PPS);
 
        if(mySize.x/mySize.y > aspect_ratio)
        {
@@ -729,18 +712,7 @@ void HUD_Mod_Dom(vector myPos, vector mySize)
 
 void HUD_ModIcons_SetFunc()
 {
-       switch(gametype)
-       {
-               case MAPINFO_TYPE_KEYHUNT:              HUD_ModIcons_GameType = HUD_Mod_KH; break;
-               case MAPINFO_TYPE_CTF:                  HUD_ModIcons_GameType = HUD_Mod_CTF; break;
-               case MAPINFO_TYPE_NEXBALL:              HUD_ModIcons_GameType = HUD_Mod_NexBall; break;
-               case MAPINFO_TYPE_CTS:
-               case MAPINFO_TYPE_RACE:         HUD_ModIcons_GameType = HUD_Mod_Race; break;
-               case MAPINFO_TYPE_CA:
-               case MAPINFO_TYPE_FREEZETAG:    HUD_ModIcons_GameType = HUD_Mod_CA; break;
-               case MAPINFO_TYPE_DOMINATION:   HUD_ModIcons_GameType = HUD_Mod_Dom; break;
-               case MAPINFO_TYPE_KEEPAWAY:     HUD_ModIcons_GameType = HUD_Mod_Keepaway; break;
-       }
+       HUD_ModIcons_GameType = gametype.m_modicons;
 }
 
 int mod_prev; // previous state of mod_active to check for a change
@@ -755,9 +727,6 @@ void HUD_ModIcons()
                if(!HUD_ModIcons_GameType) return;
        }
 
-       HUD_Panel_UpdateCvars();
-
-       draw_beginBoldFont();
 
        if(mod_active != mod_prev) {
                mod_change = time;
@@ -769,12 +738,19 @@ void HUD_ModIcons()
        else
                mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1);
 
+       //if(mod_alpha <= 0)
+       //      return;
+       panel_fade_alpha *= mod_alpha;
+       HUD_Panel_LoadCvars();
+
+       draw_beginBoldFont();
+
        if (autocvar_hud_panel_modicons_dynamichud)
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       if(mod_alpha)
-               HUD_Panel_DrawBg(mod_alpha);
+
+       HUD_Panel_DrawBg();
 
        if(panel_bg_padding)
        {
index a5e923825a989fc13e9b76b9c077de5b4ce97c31..a49d262a4f9075a9ff615d7435974c6a62e39f20 100644 (file)
@@ -1,5 +1,7 @@
 #include "notify.qh"
-// Notification area (#4)
+
+
+// Notifications (#4)
 
 void HUD_Notify_Push(string icon, string attacker, string victim)
 {
@@ -47,12 +49,13 @@ void HUD_Notify()
                if (!autocvar_hud_panel_notify)
                        return;
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
+
        if (autocvar_hud_panel_notify_dynamichud)
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if (!autocvar__hud_configure)
                if (notify_count == 0)
index 371a9f344bf2990bfda62a2a3730d771f6f4d60b..6befd1a30a4fe161ea57e386def9eb33f0e2bbfe 100644 (file)
@@ -1,9 +1,10 @@
 #include "physics.qh"
 
+#include <client/main.qh>
 #include <common/mapinfo.qh>
 #include <lib/csqcmodel/cl_player.qh>
 
-// Physics panel (#15)
+// Physics (#15)
 
 vector acc_prevspeed;
 float acc_prevtime, acc_avg, top_speed, top_speed_time;
@@ -17,7 +18,7 @@ void HUD_Physics()
                if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        draw_beginBoldFont();
 
@@ -25,7 +26,7 @@ void HUD_Physics()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                panel_pos += '1 1 0' * panel_bg_padding;
@@ -43,34 +44,8 @@ void HUD_Physics()
                text_scale = min(autocvar_hud_panel_physics_text_scale, 1);
 
        //compute speed
-       float speed, conversion_factor;
-       string unit;
-
-       switch(autocvar_hud_panel_physics_speed_unit)
-       {
-               default:
-               case 1:
-                       unit = _(" qu/s");
-                       conversion_factor = 1.0;
-                       break;
-               case 2:
-                       unit = _(" m/s");
-                       conversion_factor = 0.0254;
-                       break;
-               case 3:
-                       unit = _(" km/h");
-                       conversion_factor = 0.0254 * 3.6;
-                       break;
-               case 4:
-                       unit = _(" mph");
-                       conversion_factor = 0.0254 * 3.6 * 0.6213711922;
-                       break;
-               case 5:
-                       unit = _(" knots");
-                       conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
-                       break;
-       }
-
+       float speed, conversion_factor = GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
+       string unit = GetSpeedUnit(autocvar_hud_panel_physics_speed_unit);
        vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel);
 
        float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 );
index 223bf72ce8efae4a679f8c6405673deaf1aac283..076ce918f14723dcde2e755308a3f4379fb885e0 100644 (file)
@@ -1,6 +1,6 @@
 #include "powerups.qh"
 
-#include <common/items/all.qc>
+#include <common/items/_mod.qh>
 
 // Powerups (#2)
 
@@ -63,15 +63,16 @@ void HUD_Powerups()
 {
        int allItems = STAT(ITEMS);
        int allBuffs = STAT(BUFFS);
-       int strengthTime, shieldTime, superTime;
+       float strengthTime, shieldTime, superTime;
 
        // Initialize items
        if(!autocvar__hud_configure)
        {
-               if(!autocvar_hud_panel_powerups) return;
-               if(spectatee_status == -1) return;
-               if(STAT(HEALTH) <= 0) return;
-               if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return;
+               if((!autocvar_hud_panel_powerups) || (spectatee_status == -1))
+                       return;
+               if(STAT(HEALTH) <= 0 && autocvar_hud_panel_powerups_hide_ondeath)
+                       return;
+               //if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return;
 
                strengthTime = bound(0, STAT(STRENGTH_FINISHED) - time, 99);
                shieldTime = bound(0, STAT(INVINCIBLE_FINISHED) - time, 99);
@@ -108,12 +109,13 @@ void HUD_Powerups()
                return;
 
        // Draw panel background
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
+
        if (autocvar_hud_panel_powerups_dynamichud)
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        // Set drawing area
        vector pos = panel_pos;
index 6db88c68b39ee50fce1f8acce9c778465a3223ae..3235f8da674507aa494cf3e7e8c29aa132db6bc0 100644 (file)
@@ -1,2 +1,4 @@
 #pragma once
 #include "../panel.qh"
+
+void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime);
index d0a4f39fdfd8fc956ce202376af7f5b55f90ac17..abb9ccef9aa56910fae3fd52fb1b3e5699e512ab 100644 (file)
@@ -1,5 +1,8 @@
 #include "pressedkeys.qh"
-/** Draw pressed keys (#11) */
+
+
+// Pressed keys (#11)
+
 void HUD_PressedKeys()
 {
        if(!autocvar__hud_configure)
@@ -8,7 +11,7 @@ void HUD_PressedKeys()
                if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -17,7 +20,7 @@ void HUD_PressedKeys()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
index 08d97ffd2e5a64027eb730017bce4f2ad9b2d17a..e242ae895881a89d05baf81eb6da4c3197fdf4e7 100644 (file)
@@ -1,10 +1,11 @@
 #include "quickmenu.qh"
-// QuickMenu (#23)
 
 #include <common/ent_cs.qh>
-#include <client/hud/all.qh>
+#include <client/hud/_mod.qh>
 #include <client/mapvoting.qh>
 
+// QuickMenu (#23)
+
 // QUICKMENU_MAXLINES must be <= 10
 const int QUICKMENU_MAXLINES = 10;
 // visible entries are loaded from QuickMenu_Buffer into QuickMenu_Page_* arrays
@@ -58,6 +59,7 @@ void QuickMenu_Page_ClearEntry(int i)
        if (QuickMenu_Page_Command[i])
                strunzone(QuickMenu_Page_Command[i]);
        QuickMenu_Page_Command[i] = string_null;
+       QuickMenu_Page_Command_Type[i] = 0;
 }
 
 float QuickMenu_Page_Load(string target_submenu, float new_page);
@@ -155,7 +157,7 @@ bool QuickMenu_Open(string mode, string submenu)
        }
        else
        {
-               LOG_WARNINGF("Unrecognized mode %s\n", mode);
+               LOG_WARNF("Unrecognized mode %s", mode);
                return false;
        }
 
@@ -291,7 +293,7 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page)
                        // printf("^1 skipping %s\n", s);
                }
                if(QuickMenu_Buffer_Index == QuickMenu_Buffer_Size)
-                       LOG_WARNINGF("Couldn't find submenu \"%s\"\n", z_submenu);
+                       LOG_WARNF("Couldn't find submenu \"%s\"", z_submenu);
        }
 
        // only the last page can contain up to QUICKMENU_MAXLINES entries
@@ -430,11 +432,8 @@ bool QuickMenu_InputEvent(int bInputType, float nPrimary, float nSecondary)
        }
 
        // allow console bind to work
-       string con_keys;
-       float keys;
-       con_keys = findkeysforcommand("toggleconsole", 0);
-       keys = tokenize(con_keys); // findkeysforcommand returns data for this
-
+       string con_keys = findkeysforcommand("toggleconsole", 0);
+       int keys = tokenize(con_keys); // findkeysforcommand returns data for this
        bool hit_con_bind = false;
        int i;
        for (i = 0; i < keys; ++i)
@@ -497,15 +496,11 @@ void QuickMenu_Mouse()
                return;
        }
 
-       if(!autocvar_hud_cursormode)
-       {
-               mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
-
-               mousepos.x = bound(0, mousepos.x, vid_conwidth);
-               mousepos.y = bound(0, mousepos.y, vid_conheight);
-       }
+       if (!autocvar_hud_cursormode)
+               update_mousepos();
 
-       HUD_Panel_UpdateCvars();
+       panel = HUD_PANEL(QUICKMENU);
+       HUD_Panel_LoadCvars();
 
        if(panel_bg_padding)
        {
@@ -619,10 +614,10 @@ void HUD_QuickMenu()
                }
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if(panel_bg_padding)
        {
@@ -660,7 +655,7 @@ void HUD_QuickMenu()
                                else
                                        tokenize_console(substring(QuickMenu_Page_Command[i], 0, end));
 
-                               //if(argv(1) && argv(0) == "toggle") // already checked
+                               if(argv(1) && argv(0) == "toggle")
                                {
                                        // "enable feature xxx" "toggle xxx" (or "toggle xxx 1 0")
                                        // "disable feature xxx" "toggle xxx 0 1"
@@ -868,9 +863,9 @@ void QuickMenu_Default(string target_submenu)
 
        if(target_submenu != "" && !target_submenu_found)
        {
-               LOG_WARNINGF("Couldn't find submenu \"%s\"\n", target_submenu);
+               LOG_WARNF("Couldn't find submenu \"%s\"", target_submenu);
                if(prvm_language != "en")
-                       LOG_WARNINGF("^3Warning: submenu must be in English\n", target_submenu);
+                       LOG_WARNF("^3Warning: submenu must be in English", target_submenu);
                QuickMenu_Buffer_Size = 0;
        }
 }
index dd8bbdfec9375597df95e0f755309b21454af9e7..02b631b3000932cbd2ee69f812e5cc8446f2aed1 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <common/mapinfo.qh>
 
-/** Race timer (#8) */
+// Race timer (#6)
 
 // return the string of the onscreen race timer
 string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname)
@@ -83,7 +83,7 @@ void HUD_RaceTimer ()
                if(spectatee_status == -1) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        vector pos, mySize;
        pos = panel_pos;
@@ -93,7 +93,7 @@ void HUD_RaceTimer ()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
index 1642e41891a8d75eed766cbe826bad92636feaa7..3bc537c8f8b5e42760e5ca614ac4e02dadc4ce08 100644 (file)
@@ -26,6 +26,13 @@ void HUD_Radar_Show_Maximized(bool doshow,float clickable)
                        if(autocvar_hud_cursormode)
                                setcursormode(1);
                        hud_panel_radar_mouse = 1;
+
+                       // we must unset the player's buttons, as they aren't released elsewhere
+                       localcmd("-fire\n");
+                       localcmd("-fire2\n");
+                       localcmd("-use\n");
+                       localcmd("-hook\n");
+                       localcmd("-jump\n");
                }
        }
        else if ( hud_panel_radar_mouse )
@@ -78,11 +85,9 @@ float HUD_Radar_InputEvent(int bInputType, float nPrimary, float nSecondary)
        else
        {
                // allow console/use binds to work without hiding the map
-               string con_keys;
-               float keys;
-               float i;
-               con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ;
-               keys = tokenize(con_keys); // findkeysforcommand returns data for this
+               string con_keys = strcat(findkeysforcommand("toggleconsole", 0), " ", findkeysforcommand("+use", 0)) ;
+               int keys = tokenize(con_keys); // findkeysforcommand returns data for this
+               int i;
                for (i = 0; i < keys; ++i)
                {
                        if(nPrimary == stof(argv(i)))
@@ -132,15 +137,11 @@ void HUD_Radar_Mouse()
                return;
        }
 
-       if(!autocvar_hud_cursormode)
-       {
-               mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
-
-               mousepos_x = bound(0, mousepos_x, vid_conwidth);
-               mousepos_y = bound(0, mousepos_y, vid_conheight);
-       }
+       if (!autocvar_hud_cursormode)
+               update_mousepos();
 
-       HUD_Panel_UpdateCvars();
+       panel = HUD_PANEL(RADAR);
+       HUD_Panel_LoadCvars();
 
 
        panel_size = autocvar_hud_panel_radar_maximized_size;
@@ -192,7 +193,7 @@ void HUD_Radar()
        if ( hud_panel_radar_temp_hidden )
                return;
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        float f = 0;
 
@@ -279,7 +280,7 @@ void HUD_Radar()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
@@ -311,10 +312,10 @@ void HUD_Radar()
        else
        {
                vector c0, c1, c2, c3, span;
-               c0 = rotate(mi_min, teamradar_angle * DEG2RAD);
-               c1 = rotate(mi_max, teamradar_angle * DEG2RAD);
-               c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
-               c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
+               c0 = Rotate(mi_min, teamradar_angle * DEG2RAD);
+               c1 = Rotate(mi_max, teamradar_angle * DEG2RAD);
+               c2 = Rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
+               c3 = Rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
                span = '0 0 0';
                span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x);
                span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y);
@@ -346,12 +347,12 @@ void HUD_Radar()
 
        draw_teamradar_background(hud_panel_radar_foreground_alpha);
 
-       FOREACH_ENTITY_CLASS("radarlink", true, draw_teamradar_link(it.origin, it.velocity, it.team));
+       IL_EACH(g_radarlinks, true, draw_teamradar_link(it.origin, it.velocity, it.team));
 
-       FOREACH_ENTITY_FLAGS(teamradar_icon, 0xFFFFFF, {
+       IL_EACH(g_radaricons, it.teamradar_icon, {
                if ( hud_panel_radar_mouse )
-               if ( it.health > 0 )
-               if ( it.team == myteam+1 || gametype == MAPINFO_TYPE_RACE )
+               if ( it.health >= 0 )
+               if ( it.team == myteam + 1 || gametype == MAPINFO_TYPE_RACE || !teamplay )
                {
                        vector coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(it.origin));
                        if(vdist((mousepos - coord), <, 8))
index b07b84ed33f8001c34779cea4a7bddd9d4db5813..f3d55a433e8db1792ddc098c8bd71e7f4b0186f8 100644 (file)
@@ -1,12 +1,11 @@
 #include "score.qh"
 
-#include <client/scoreboard.qh>
+#include "scoreboard.qh"
 #include <common/ent_cs.qh>
 #include <common/mapinfo.qh>
 
 // Score (#7)
 
-void HUD_UpdatePlayerTeams();
 void HUD_Score_Rankings(vector pos, vector mySize, entity me)
 {
        float score;
@@ -71,8 +70,8 @@ void HUD_Score_Rankings(vector pos, vector mySize, entity me)
                return;
        }
 
-       if (!scoreboard_fade_alpha) // the scoreboard too calls HUD_UpdatePlayerTeams
-               HUD_UpdatePlayerTeams();
+       if (!scoreboard_fade_alpha) // the scoreboard too calls Scoreboard_UpdatePlayerTeams
+               Scoreboard_UpdatePlayerTeams();
        if (team_count)
        {
                // show team scores in the first line
@@ -80,11 +79,12 @@ void HUD_Score_Rankings(vector pos, vector mySize, entity me)
                for(tm = teams.sort_next; tm; tm = tm.sort_next) {
                        if(tm.team == NUM_SPECTATOR)
                                continue;
-                       if(!tm.team && teamplay)
+                       if(!tm.team)
                                continue;
+
                        if (tm.team == myteam)
                                drawfill(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
-                       drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores[ts_primary])), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
+                       drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores(ts_primary))), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
                        ++i;
                }
                first_pl = 1;
@@ -124,7 +124,7 @@ void HUD_Score_Rankings(vector pos, vector mySize, entity me)
                        score_color = Team_ColorRGB(pl.team) * 0.8;
                s = textShortenToWidth(entcs_GetName(pl.sv_entnum), name_size, fontsize, stringwidth_colors);
                drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
-               drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores[ps_primary])), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+               drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores(ps_primary))), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
                pos.y += fontsize.y;
                ++i;
        }
@@ -139,7 +139,7 @@ void HUD_Score()
                if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -148,7 +148,7 @@ void HUD_Score()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
@@ -162,23 +162,23 @@ void HUD_Score()
 
        me = playerslots[current_player];
 
-       if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
+       if((scores_flags(ps_primary) & SFL_TIME) && !teamplay) { // race/cts record display on HUD
                string timer, distrtimer;
 
                pl = players.sort_next;
                if(pl == me)
                        pl = pl.sort_next;
-               if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
-                       if(pl.scores[ps_primary] == 0)
+               if(scores_flags(ps_primary) & SFL_ZERO_IS_WORST)
+                       if(pl.scores(ps_primary) == 0)
                                pl = NULL;
 
-               score = me.(scores[ps_primary]);
+               score = me.(scores(ps_primary));
                timer = TIME_ENCODED_TOSTRING(score);
 
                draw_beginBoldFont();
-               if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
+               if (pl && ((!(scores_flags(ps_primary) & SFL_ZERO_IS_WORST)) || score)) {
                        // distribution display
-                       distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
+                       distribution = me.(scores(ps_primary)) - pl.(scores(ps_primary));
 
                        distrtimer = ftos_decimals(fabs(distribution/pow(10, TIME_DECIMALS)), TIME_DECIMALS);
 
@@ -211,11 +211,11 @@ void HUD_Score()
                if(autocvar__hud_configure)
                        distribution = 42;
                else if(pl)
-                       distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
+                       distribution = me.(scores(ps_primary)) - pl.(scores(ps_primary));
                else
                        distribution = 0;
 
-               score = me.(scores[ps_primary]);
+               score = me.(scores(ps_primary));
                if(autocvar__hud_configure)
                        score = 123;
 
@@ -281,9 +281,10 @@ void HUD_Score()
                for(tm = teams.sort_next; tm; tm = tm.sort_next) {
                        if(tm.team == NUM_SPECTATOR)
                                continue;
-                       if(!tm.team && teamplay)
+                       if(!tm.team)
                                continue;
-                       score = tm.(teamscores[ts_primary]);
+
+                       score = tm.(teamscores(ts_primary));
                        if(autocvar__hud_configure)
                                score = 123;
 
diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc
new file mode 100644 (file)
index 0000000..35de219
--- /dev/null
@@ -0,0 +1,1702 @@
+#include "scoreboard.qh"
+
+#include "quickmenu.qh"
+#include <common/ent_cs.qh>
+#include <common/constants.qh>
+#include <common/net_linked.qh>
+#include <common/mapinfo.qh>
+#include <common/minigames/cl_minigames.qh>
+#include <common/stats.qh>
+#include <common/teams.qh>
+
+// Scoreboard (#24)
+
+const int MAX_SBT_FIELDS = MAX_SCORE;
+
+PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1];
+float sbt_field_size[MAX_SBT_FIELDS + 1];
+string sbt_field_title[MAX_SBT_FIELDS + 1];
+int sbt_num_fields;
+
+string autocvar_hud_fontsize;
+string hud_fontsize_str;
+float max_namesize;
+
+float sbt_bg_alpha;
+float sbt_fg_alpha;
+float sbt_fg_alpha_self;
+bool sbt_highlight;
+float sbt_highlight_alpha;
+float sbt_highlight_alpha_self;
+
+// provide basic panel cvars to old clients
+// TODO remove them after a future release (0.8.2+)
+noref string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000";
+noref string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000";
+noref string autocvar_hud_panel_scoreboard_bg = "border_default";
+noref string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5";
+noref string autocvar_hud_panel_scoreboard_bg_color_team = "";
+noref string autocvar_hud_panel_scoreboard_bg_alpha = "0.7";
+noref string autocvar_hud_panel_scoreboard_bg_border = "";
+noref string autocvar_hud_panel_scoreboard_bg_padding = "";
+
+float autocvar_hud_panel_scoreboard_fadeinspeed = 10;
+float autocvar_hud_panel_scoreboard_fadeoutspeed = 5;
+float autocvar_hud_panel_scoreboard_respawntime_decimals = 1;
+float autocvar_hud_panel_scoreboard_table_bg_alpha = 0;
+float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25;
+float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9;
+float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1;
+bool autocvar_hud_panel_scoreboard_table_highlight = true;
+float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2;
+float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
+float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
+float autocvar_hud_panel_scoreboard_namesize = 15;
+
+bool autocvar_hud_panel_scoreboard_accuracy = true;
+bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false;
+bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
+
+bool autocvar_hud_panel_scoreboard_dynamichud = false;
+
+float autocvar_hud_panel_scoreboard_maxheight = 0.6;
+bool autocvar_hud_panel_scoreboard_others_showscore = true;
+bool autocvar_hud_panel_scoreboard_spectators_showping = true;
+bool autocvar_hud_panel_scoreboard_spectators_aligned = false;
+float autocvar_hud_panel_scoreboard_minwidth = 0.4;
+
+
+void drawstringright(vector, string, vector, vector, float, float);
+void drawstringcenter(vector, string, vector, vector, float, float);
+
+// wrapper to put all possible scores titles through gettext
+string TranslateScoresLabel(string l)
+{
+       switch(l)
+       {
+               case "bckills": return CTX(_("SCO^bckills"));
+               case "bctime": return CTX(_("SCO^bctime"));
+               case "caps": return CTX(_("SCO^caps"));
+               case "captime": return CTX(_("SCO^captime"));
+               case "deaths": return CTX(_("SCO^deaths"));
+               case "destroyed": return CTX(_("SCO^destroyed"));
+               case "dmg": return CTX(_("SCO^damage"));
+               case "dmgtaken": return CTX(_("SCO^dmgtaken"));
+               case "drops": return CTX(_("SCO^drops"));
+               case "faults": return CTX(_("SCO^faults"));
+               case "fckills": return CTX(_("SCO^fckills"));
+               case "goals": return CTX(_("SCO^goals"));
+               case "kckills": return CTX(_("SCO^kckills"));
+               case "kdratio": return CTX(_("SCO^kdratio"));
+               case "kd": return CTX(_("SCO^k/d"));
+               case "kdr": return CTX(_("SCO^kdr"));
+               case "kills": return CTX(_("SCO^kills"));
+               case "laps": return CTX(_("SCO^laps"));
+               case "lives": return CTX(_("SCO^lives"));
+               case "losses": return CTX(_("SCO^losses"));
+               case "name": return CTX(_("SCO^name"));
+               case "sum": return CTX(_("SCO^sum"));
+               case "nick": return CTX(_("SCO^nick"));
+               case "objectives": return CTX(_("SCO^objectives"));
+               case "pickups": return CTX(_("SCO^pickups"));
+               case "ping": return CTX(_("SCO^ping"));
+               case "pl": return CTX(_("SCO^pl"));
+               case "pushes": return CTX(_("SCO^pushes"));
+               case "rank": return CTX(_("SCO^rank"));
+               case "returns": return CTX(_("SCO^returns"));
+               case "revivals": return CTX(_("SCO^revivals"));
+               case "rounds": return CTX(_("SCO^rounds won"));
+               case "score": return CTX(_("SCO^score"));
+               case "suicides": return CTX(_("SCO^suicides"));
+               case "takes": return CTX(_("SCO^takes"));
+               case "ticks": return CTX(_("SCO^ticks"));
+               default: return l;
+       }
+}
+
+void Scoreboard_InitScores()
+{
+       int i, f;
+
+       ps_primary = ps_secondary = NULL;
+       ts_primary = ts_secondary = -1;
+       FOREACH(Scores, true, {
+               f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
+               if(f == SFL_SORT_PRIO_PRIMARY)
+                       ps_primary = it;
+               if(f == SFL_SORT_PRIO_SECONDARY)
+                       ps_secondary = it;
+       });
+       if(ps_secondary == NULL)
+               ps_secondary = ps_primary;
+
+       for(i = 0; i < MAX_TEAMSCORE; ++i)
+       {
+               f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
+               if(f == SFL_SORT_PRIO_PRIMARY)
+                       ts_primary = i;
+               if(f == SFL_SORT_PRIO_SECONDARY)
+                       ts_secondary = i;
+       }
+       if(ts_secondary == -1)
+               ts_secondary = ts_primary;
+
+       Cmd_Scoreboard_SetFields(0);
+}
+
+float SetTeam(entity pl, float Team);
+//float lastpnum;
+void Scoreboard_UpdatePlayerTeams()
+{
+       float Team;
+       entity pl, tmp;
+       //int num = 0;
+       for(pl = players.sort_next; pl; pl = pl.sort_next)
+       {
+               //num += 1;
+               Team = entcs_GetScoreTeam(pl.sv_entnum);
+               if(SetTeam(pl, Team))
+               {
+                       tmp = pl.sort_prev;
+                       Scoreboard_UpdatePlayerPos(pl);
+                       if(tmp)
+                               pl = tmp;
+                       else
+                               pl = players.sort_next;
+               }
+       }
+       /*
+       if(num != lastpnum)
+               print(strcat("PNUM: ", ftos(num), "\n"));
+       lastpnum = num;
+       */
+}
+
+int Scoreboard_CompareScore(int vl, int vr, int f)
+{
+    TC(int, vl); TC(int, vr); TC(int, f);
+       if(f & SFL_ZERO_IS_WORST)
+       {
+               if(vl == 0 && vr != 0)
+                       return 1;
+               if(vl != 0 && vr == 0)
+                       return 0;
+       }
+       if(vl > vr)
+               return IS_INCREASING(f);
+       if(vl < vr)
+               return IS_DECREASING(f);
+       return -1;
+}
+
+float Scoreboard_ComparePlayerScores(entity left, entity right)
+{
+       float vl, vr, r;
+       vl = entcs_GetTeam(left.sv_entnum);
+       vr = entcs_GetTeam(right.sv_entnum);
+
+       if(!left.gotscores)
+               vl = NUM_SPECTATOR;
+       if(!right.gotscores)
+               vr = NUM_SPECTATOR;
+
+       if(vl > vr)
+               return true;
+       if(vl < vr)
+               return false;
+
+       if(vl == NUM_SPECTATOR)
+       {
+               // FIRST the one with scores (spectators), THEN the ones without (downloaders)
+               // no other sorting
+               if(!left.gotscores && right.gotscores)
+                       return true;
+               return false;
+       }
+
+       r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
+       if (r >= 0)
+               return r;
+
+       r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
+       if (r >= 0)
+               return r;
+
+       FOREACH(Scores, true, {
+               r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
+               if (r >= 0) return r;
+       });
+
+       if (left.sv_entnum < right.sv_entnum)
+               return true;
+
+       return false;
+}
+
+void Scoreboard_UpdatePlayerPos(entity player)
+{
+       entity ent;
+       for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
+       {
+               SORT_SWAP(player, ent);
+       }
+       for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
+       {
+               SORT_SWAP(ent, player);
+       }
+}
+
+float Scoreboard_CompareTeamScores(entity left, entity right)
+{
+       int i, r;
+
+       if(left.team == NUM_SPECTATOR)
+               return 1;
+       if(right.team == NUM_SPECTATOR)
+               return 0;
+
+       r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
+       if (r >= 0)
+               return r;
+
+       r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
+       if (r >= 0)
+               return r;
+
+       for(i = 0; i < MAX_TEAMSCORE; ++i)
+       {
+               r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
+               if (r >= 0)
+                       return r;
+       }
+
+       if (left.team < right.team)
+               return true;
+
+       return false;
+}
+
+void Scoreboard_UpdateTeamPos(entity Team)
+{
+       entity ent;
+       for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
+       {
+               SORT_SWAP(Team, ent);
+       }
+       for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
+       {
+               SORT_SWAP(ent, Team);
+       }
+}
+
+void Cmd_Scoreboard_Help()
+{
+       LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
+       LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
+       LOG_INFO(_("Usage:\n"));
+       LOG_INFO(_("^2scoreboard_columns_set default\n"));
+       LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
+       LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
+       LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n"));
+       LOG_INFO("\n");
+
+       LOG_INFO(_("^3name^7 or ^3nick^7             Name of a player\n"));
+       LOG_INFO(_("^3ping^7                     Ping time\n"));
+       LOG_INFO(_("^3pl^7                       Packet loss\n"));
+       LOG_INFO(_("^3elo^7                      Player ELO\n"));
+       LOG_INFO(_("^3kills^7                    Number of kills\n"));
+       LOG_INFO(_("^3deaths^7                   Number of deaths\n"));
+       LOG_INFO(_("^3suicides^7                 Number of suicides\n"));
+       LOG_INFO(_("^3frags^7                    kills - suicides\n"));
+       LOG_INFO(_("^3kd^7                       The kill-death ratio\n"));
+       LOG_INFO(_("^3dmg^7                      The total damage done\n"));
+       LOG_INFO(_("^3dmgtaken^7                 The total damage taken\n"));
+       LOG_INFO(_("^3sum^7                      frags - deaths\n"));
+       LOG_INFO(_("^3caps^7                     How often a flag (CTF) or a key (KeyHunt) was captured\n"));
+       LOG_INFO(_("^3pickups^7                  How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
+       LOG_INFO(_("^3captime^7                  Time of fastest cap (CTF)\n"));
+       LOG_INFO(_("^3fckills^7                  Number of flag carrier kills\n"));
+       LOG_INFO(_("^3returns^7                  Number of flag returns\n"));
+       LOG_INFO(_("^3drops^7                    Number of flag drops\n"));
+       LOG_INFO(_("^3lives^7                    Number of lives (LMS)\n"));
+       LOG_INFO(_("^3rank^7                     Player rank\n"));
+       LOG_INFO(_("^3pushes^7                   Number of players pushed into void\n"));
+       LOG_INFO(_("^3destroyed^7                Number of keys destroyed by pushing them into void\n"));
+       LOG_INFO(_("^3kckills^7                  Number of keys carrier kills\n"));
+       LOG_INFO(_("^3losses^7                   Number of times a key was lost\n"));
+       LOG_INFO(_("^3laps^7                     Number of laps finished (race/cts)\n"));
+       LOG_INFO(_("^3time^7                     Total time raced (race/cts)\n"));
+       LOG_INFO(_("^3fastest^7                  Time of fastest lap (race/cts)\n"));
+       LOG_INFO(_("^3ticks^7                    Number of ticks (DOM)\n"));
+       LOG_INFO(_("^3takes^7                    Number of domination points taken (DOM)\n"));
+       LOG_INFO(_("^3bckills^7                  Number of ball carrier kills\n"));
+       LOG_INFO(_("^3bctime^7                   Total amount of time holding the ball in Keepaway\n"));
+       LOG_INFO(_("^3score^7                    Total score\n"));
+       LOG_INFO("\n");
+
+       LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
+               "of game types, then a slash, to make the field show up only in these\n"
+               "or in all but these game types. You can also specify 'all' as a\n"
+               "field to show all fields available for the current game mode.\n\n"));
+
+       LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
+               "include/exclude ALL teams/noteams game modes.\n\n"));
+
+       LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
+       LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
+               "right of the vertical bar aligned to the right.\n"));
+       LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
+               "other gamemodes except DM.\n"));
+}
+
+// NOTE: adding a gametype with ? to not warn for an optional field
+// make sure it's excluded in a previous exclusive rule, if any
+// otherwise the previous exclusive rule warns anyway
+// e.g. -teams,rc,cts,lms/kills ?+rc/kills
+#define SCOREBOARD_DEFAULT_COLUMNS \
+"ping pl name |" \
+" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
+" -teams,lms/deaths +ft,tdm/deaths" \
+" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
+" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
+" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
+" +lms/lives +lms/rank" \
+" +kh/caps +kh/pushes +kh/destroyed" \
+" ?+rc/laps ?+rc/time +rc,cts/fastest" \
+" +as/objectives +nb/faults +nb/goals" \
+" +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
+" -lms,rc,cts,inv,nb/score"
+
+void Cmd_Scoreboard_SetFields(int argc)
+{
+    TC(int, argc);
+       int i, slash;
+       string str, pattern;
+       bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
+       int missing;
+
+       if(!gametype)
+               return; // do nothing, we don't know gametype and scores yet
+
+       // sbt_fields uses strunzone on the titles!
+       if(!sbt_field_title[0])
+               for(i = 0; i < MAX_SBT_FIELDS; ++i)
+                       sbt_field_title[i] = strzone("(null)");
+
+       // TODO: re enable with gametype dependant cvars?
+       if(argc < 3) // no arguments provided
+               argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
+
+       if(argc < 3)
+               argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
+
+       if(argc == 3)
+       {
+               if(argv(2) == "default")
+                       argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
+               else if(argv(2) == "all")
+               {
+                       string s;
+                       s = "ping pl name |";
+                       FOREACH(Scores, true, {
+                               if(it != ps_primary)
+                               if(it != ps_secondary)
+                               if(scores_label(it) != "")
+                                       s = strcat(s, " ", scores_label(it));
+                       });
+                       if(ps_secondary != ps_primary)
+                               s = strcat(s, " ", scores_label(ps_secondary));
+                       s = strcat(s, " ", scores_label(ps_primary));
+                       argc = tokenizebyseparator(strcat("0 1 ", s), " ");
+               }
+       }
+
+
+       sbt_num_fields = 0;
+
+       hud_fontsize = HUD_GetFontsize("hud_fontsize");
+
+       for(i = 1; i < argc - 1; ++i)
+       {
+               float nocomplain;
+               str = argv(i+1);
+
+               nocomplain = false;
+               if(substring(str, 0, 1) == "?")
+               {
+                       nocomplain = true;
+                       str = substring(str, 1, strlen(str) - 1);
+               }
+
+               slash = strstrofs(str, "/", 0);
+               if(slash >= 0)
+               {
+                       pattern = substring(str, 0, slash);
+                       str = substring(str, slash + 1, strlen(str) - (slash + 1));
+
+                       if (!isGametypeInFilter(gametype, teamplay, false, pattern))
+                               continue;
+               }
+
+               strunzone(sbt_field_title[sbt_num_fields]);
+               sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(str));
+               sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
+               str = strtolower(str);
+
+               PlayerScoreField j;
+               switch(str)
+               {
+                       case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
+                       case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
+                       case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
+                       case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
+                       case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
+                       case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
+                       case "elo": sbt_field[sbt_num_fields] = SP_ELO; break;
+                       case "dmg": case "damage": sbt_field[sbt_num_fields] = SP_DMG; break;
+                       case "dmgtaken": case "damagetaken": sbt_field[sbt_num_fields] = SP_DMGTAKEN; break;
+                       default:
+                       {
+                               FOREACH(Scores, true, {
+                                       if (str == strtolower(scores_label(it))) {
+                                               j = it;
+                                               goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
+                                       }
+                               });
+
+LABEL(notfound)
+                               if(str == "frags")
+                                       j = SP_FRAGS;
+                               else
+                               {
+                                       if(!nocomplain)
+                                               LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
+                                       continue;
+                               }
+LABEL(found)
+                               sbt_field[sbt_num_fields] = j;
+                               if(j == ps_primary)
+                                       have_primary = true;
+                               if(j == ps_secondary)
+                                       have_secondary = true;
+
+                       }
+               }
+               ++sbt_num_fields;
+               if(sbt_num_fields >= MAX_SBT_FIELDS)
+                       break;
+       }
+
+       if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
+               have_primary = true;
+       if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
+               have_secondary = true;
+       if(ps_primary == ps_secondary)
+               have_secondary = true;
+       missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
+
+       if(sbt_num_fields + missing < MAX_SBT_FIELDS)
+       {
+               if(!have_name)
+               {
+                       strunzone(sbt_field_title[sbt_num_fields]);
+                       for(i = sbt_num_fields; i > 0; --i)
+                       {
+                               sbt_field_title[i] = sbt_field_title[i-1];
+                               sbt_field_size[i] = sbt_field_size[i-1];
+                               sbt_field[i] = sbt_field[i-1];
+                       }
+                       sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
+                       sbt_field[0] = SP_NAME;
+                       ++sbt_num_fields;
+                       LOG_INFO("fixed missing field 'name'\n");
+
+                       if(!have_separator)
+                       {
+                               strunzone(sbt_field_title[sbt_num_fields]);
+                               for(i = sbt_num_fields; i > 1; --i)
+                               {
+                                       sbt_field_title[i] = sbt_field_title[i-1];
+                                       sbt_field_size[i] = sbt_field_size[i-1];
+                                       sbt_field[i] = sbt_field[i-1];
+                               }
+                               sbt_field_title[1] = strzone("|");
+                               sbt_field[1] = SP_SEPARATOR;
+                               sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
+                               ++sbt_num_fields;
+                               LOG_INFO("fixed missing field '|'\n");
+                       }
+               }
+               else if(!have_separator)
+               {
+                       strunzone(sbt_field_title[sbt_num_fields]);
+                       sbt_field_title[sbt_num_fields] = strzone("|");
+                       sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
+                       sbt_field[sbt_num_fields] = SP_SEPARATOR;
+                       ++sbt_num_fields;
+                       LOG_INFO("fixed missing field '|'\n");
+               }
+               if(!have_secondary)
+               {
+                       strunzone(sbt_field_title[sbt_num_fields]);
+                       sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary)));
+                       sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
+                       sbt_field[sbt_num_fields] = ps_secondary;
+                       ++sbt_num_fields;
+                       LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary));
+               }
+               if(!have_primary)
+               {
+                       strunzone(sbt_field_title[sbt_num_fields]);
+                       sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary)));
+                       sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
+                       sbt_field[sbt_num_fields] = ps_primary;
+                       ++sbt_num_fields;
+                       LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary));
+               }
+       }
+
+       sbt_field[sbt_num_fields] = SP_END;
+}
+
+// MOVEUP::
+vector sbt_field_rgb;
+string sbt_field_icon0;
+string sbt_field_icon1;
+string sbt_field_icon2;
+vector sbt_field_icon0_rgb;
+vector sbt_field_icon1_rgb;
+vector sbt_field_icon2_rgb;
+string Scoreboard_GetField(entity pl, PlayerScoreField field)
+{
+       float tmp, num, denom;
+       int f;
+       string str;
+       sbt_field_rgb = '1 1 1';
+       sbt_field_icon0 = "";
+       sbt_field_icon1 = "";
+       sbt_field_icon2 = "";
+       sbt_field_icon0_rgb = '1 1 1';
+       sbt_field_icon1_rgb = '1 1 1';
+       sbt_field_icon2_rgb = '1 1 1';
+       switch(field)
+       {
+               case SP_PING:
+                       if (!pl.gotscores)
+                               return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
+                       //str = getplayerkeyvalue(pl.sv_entnum, "ping");
+                       f = pl.ping;
+                       if(f == 0)
+                               return _("N/A");
+                       tmp = max(0, min(220, f-80)) / 220;
+                       sbt_field_rgb = '1 1 1' - '0 1 1' * tmp;
+                       return ftos(f);
+
+               case SP_PL:
+                       if (!pl.gotscores)
+                               return _("N/A");
+                       f = pl.ping_packetloss;
+                       tmp = pl.ping_movementloss;
+                       if(f == 0 && tmp == 0)
+                               return "";
+                       str = ftos(ceil(f * 100));
+                       if(tmp != 0)
+                               str = strcat(str, "~", ftos(ceil(tmp * 100)));
+                       tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
+                       sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
+                       return str;
+
+               case SP_NAME:
+                       if(ready_waiting && pl.ready)
+                       {
+                               sbt_field_icon0 = "gfx/scoreboard/player_ready";
+                       }
+                       else if(!teamplay)
+                       {
+                               f = entcs_GetClientColors(pl.sv_entnum);
+                               {
+                                       sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
+                                       sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
+                                       sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
+                                       sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
+                                       sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
+                               }
+                       }
+                       return entcs_GetName(pl.sv_entnum);
+
+               case SP_FRAGS:
+                       f = pl.(scores(SP_KILLS));
+                       f -= pl.(scores(SP_SUICIDES));
+                       return ftos(f);
+
+               case SP_KDRATIO:
+                       num = pl.(scores(SP_KILLS));
+                       denom = pl.(scores(SP_DEATHS));
+
+                       if(denom == 0) {
+                               sbt_field_rgb = '0 1 0';
+                               str = sprintf("%d", num);
+                       } else if(num <= 0) {
+                               sbt_field_rgb = '1 0 0';
+                               str = sprintf("%.1f", num/denom);
+                       } else
+                               str = sprintf("%.1f", num/denom);
+                       return str;
+
+               case SP_SUM:
+                       f = pl.(scores(SP_KILLS));
+                       f -= pl.(scores(SP_DEATHS));
+
+                       if(f > 0) {
+                               sbt_field_rgb = '0 1 0';
+                       } else if(f == 0) {
+                               sbt_field_rgb = '1 1 1';
+                       } else {
+                               sbt_field_rgb = '1 0 0';
+                       }
+                       return ftos(f);
+
+               case SP_ELO:
+               {
+                       float elo = pl.(scores(SP_ELO));
+                       switch (elo) {
+                               case -1: return "...";
+                               case -2: return _("N/A");
+                               default: return ftos(elo);
+                       }
+               }
+
+               case SP_DMG: case SP_DMGTAKEN:
+                       return sprintf("%.1f k", pl.(scores(field)) / 1000);
+
+               default: case SP_SCORE:
+                       tmp = pl.(scores(field));
+                       f = scores_flags(field);
+                       if(field == ps_primary)
+                               sbt_field_rgb = '1 1 0';
+                       else if(field == ps_secondary)
+                               sbt_field_rgb = '0 1 1';
+                       else
+                               sbt_field_rgb = '1 1 1';
+                       return ScoreString(f, tmp);
+       }
+       //return "error";
+}
+
+float sbt_fixcolumnwidth_len;
+float sbt_fixcolumnwidth_iconlen;
+float sbt_fixcolumnwidth_marginlen;
+
+string Scoreboard_FixColumnWidth(int i, string str)
+{
+    TC(int, i);
+       float f;
+       vector sz;
+
+       sbt_fixcolumnwidth_iconlen = 0;
+
+       if(sbt_field_icon0 != "")
+       {
+               sz = draw_getimagesize(sbt_field_icon0);
+               f = sz.x / sz.y;
+               if(sbt_fixcolumnwidth_iconlen < f)
+                       sbt_fixcolumnwidth_iconlen = f;
+       }
+
+       if(sbt_field_icon1 != "")
+       {
+               sz = draw_getimagesize(sbt_field_icon1);
+               f = sz.x / sz.y;
+               if(sbt_fixcolumnwidth_iconlen < f)
+                       sbt_fixcolumnwidth_iconlen = f;
+       }
+
+       if(sbt_field_icon2 != "")
+       {
+               sz = draw_getimagesize(sbt_field_icon2);
+               f = sz.x / sz.y;
+               if(sbt_fixcolumnwidth_iconlen < f)
+                       sbt_fixcolumnwidth_iconlen = f;
+       }
+
+       sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
+
+       if(sbt_fixcolumnwidth_iconlen != 0)
+               sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
+       else
+               sbt_fixcolumnwidth_marginlen = 0;
+
+       if(sbt_field[i] == SP_NAME) // name gets all remaining space
+       {
+               int j;
+               float remaining_space = 0;
+               for(j = 0; j < sbt_num_fields; ++j)
+                       if(j != i)
+                               if (sbt_field[i] != SP_SEPARATOR)
+                                       remaining_space += sbt_field_size[j] + hud_fontsize.x;
+               sbt_field_size[i] = panel_size.x - remaining_space;
+
+               if (sbt_fixcolumnwidth_iconlen != 0)
+                       remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
+               float namesize = panel_size.x - remaining_space;
+               str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
+               sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
+
+               max_namesize = vid_conwidth - remaining_space;
+       }
+       else
+               sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
+
+       f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
+       if(sbt_field_size[i] < f)
+               sbt_field_size[i] = f;
+
+       return str;
+}
+
+void Scoreboard_initFieldSizes()
+{
+       for(int i = 0; i < sbt_num_fields; ++i)
+       {
+               sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
+               Scoreboard_FixColumnWidth(i, "");
+       }
+}
+
+vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players)
+{
+       int i;
+       vector column_dim = eY * panel_size.y;
+       if(other_players)
+               column_dim.y -= 1.25 * hud_fontsize.y;
+       vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
+       pos.x += hud_fontsize.x * 0.5;
+       for(i = 0; i < sbt_num_fields; ++i)
+       {
+               if(sbt_field[i] == SP_SEPARATOR)
+                       break;
+               column_dim.x = sbt_field_size[i] + hud_fontsize.x;
+               if (sbt_highlight)
+                       if (i % 2)
+                               drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
+               drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
+               pos.x += column_dim.x;
+       }
+       if(sbt_field[i] == SP_SEPARATOR)
+       {
+               pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
+               for(i = sbt_num_fields - 1; i > 0; --i)
+               {
+                       if(sbt_field[i] == SP_SEPARATOR)
+                               break;
+
+                       pos.x -= sbt_field_size[i];
+
+                       if (sbt_highlight)
+                               if (!(i % 2))
+                               {
+                                       column_dim.x = sbt_field_size[i] + hud_fontsize.x;
+                                       drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
+                               }
+
+                       text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
+                       drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
+                       pos.x -= hud_fontsize.x;
+               }
+       }
+
+       pos.x = panel_pos.x;
+       pos.y += 1.25 * hud_fontsize.y;
+       return pos;
+}
+
+void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
+{
+    TC(bool, is_self); TC(int, pl_number);
+       string str;
+       bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
+
+       vector h_pos = item_pos;
+       vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25;
+       // alternated rows highlighting
+       if(is_self)
+               drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
+       else if((sbt_highlight) && (!(pl_number % 2)))
+               drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
+
+       float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
+
+       vector pos = item_pos;
+       pos.x += hud_fontsize.x * 0.5;
+       pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
+       vector tmp = '0 0 0';
+       int i;
+       PlayerScoreField field;
+       for(i = 0; i < sbt_num_fields; ++i)
+       {
+               field = sbt_field[i];
+               if(field == SP_SEPARATOR)
+                       break;
+
+               if(is_spec && field != SP_NAME && field != SP_PING) {
+                       pos.x += sbt_field_size[i] + hud_fontsize.x;
+                       continue;
+               }
+               str = Scoreboard_GetField(pl, field);
+               str = Scoreboard_FixColumnWidth(i, str);
+
+               pos.x += sbt_field_size[i] + hud_fontsize.x;
+
+               if(field == SP_NAME) {
+                       tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
+                       drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
+               } else {
+                       tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
+                       drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
+               }
+
+               tmp.x = sbt_field_size[i] + hud_fontsize.x;
+               if(sbt_field_icon0 != "")
+                       drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
+               if(sbt_field_icon1 != "")
+                       drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
+               if(sbt_field_icon2 != "")
+                       drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
+       }
+
+       if(sbt_field[i] == SP_SEPARATOR)
+       {
+               pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
+               for(i = sbt_num_fields-1; i > 0; --i)
+               {
+                       field = sbt_field[i];
+                       if(field == SP_SEPARATOR)
+                               break;
+
+                       if(is_spec && field != SP_NAME && field != SP_PING) {
+                               pos.x -= sbt_field_size[i] + hud_fontsize.x;
+                               continue;
+                       }
+
+                       str = Scoreboard_GetField(pl, field);
+                       str = Scoreboard_FixColumnWidth(i, str);
+
+                       if(field == SP_NAME) {
+                               tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
+                               drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
+                       } else {
+                               tmp.x = sbt_fixcolumnwidth_len;
+                               drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
+                       }
+
+                       tmp.x = sbt_field_size[i];
+                       if(sbt_field_icon0 != "")
+                               drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
+                       if(sbt_field_icon1 != "")
+                               drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
+                       if(sbt_field_icon2 != "")
+                               drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
+                       pos.x -= sbt_field_size[i] + hud_fontsize.x;
+               }
+       }
+
+       if(pl.eliminated)
+               drawfill(h_pos, h_size, '0 0 0', 0.5 * panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
+{
+       int i = 0;
+       vector h_pos = item_pos;
+       vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25;
+
+       bool complete = (this_team == NUM_SPECTATOR);
+
+       if(!complete)
+       if((sbt_highlight) && (!(pl_number % 2)))
+               drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
+
+       vector pos = item_pos;
+       pos.x += hud_fontsize.x * 0.5;
+       pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
+
+       float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
+       if(!complete)
+               width_limit -= stringwidth("...", false, hud_fontsize);
+       float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
+       static float max_name_width = 0;
+       string field = "";
+       float fieldsize = 0;
+       float min_fieldsize = 0;
+       float fieldpadding = hud_fontsize.x * 0.25;
+       if(this_team == NUM_SPECTATOR)
+       {
+               if(autocvar_hud_panel_scoreboard_spectators_showping)
+                       min_fieldsize = stringwidth("999", false, hud_fontsize);
+       }
+       else if(autocvar_hud_panel_scoreboard_others_showscore)
+               min_fieldsize = stringwidth("99", false, hud_fontsize);
+       for(i = 0; pl; pl = pl.sort_next)
+       {
+               if(pl.team != this_team)
+                       continue;
+               if(pl == ignored_pl)
+                       continue;
+
+               field = "";
+               if(this_team == NUM_SPECTATOR)
+               {
+                       if(autocvar_hud_panel_scoreboard_spectators_showping)
+                               field = Scoreboard_GetField(pl, SP_PING);
+               }
+               else if(autocvar_hud_panel_scoreboard_others_showscore)
+                       field = Scoreboard_GetField(pl, SP_SCORE);
+
+               string str = textShortenToWidth(entcs_GetName(pl.sv_entnum), namesize, hud_fontsize, stringwidth_colors);
+               float column_width = stringwidth(str, true, hud_fontsize);
+               if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
+               {
+                       if(column_width > max_name_width)
+                               max_name_width = column_width;
+                       column_width = max_name_width;
+               }
+               if(field != "")
+               {
+                       fieldsize = stringwidth(field, false, hud_fontsize);
+                       column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
+               }
+
+               if(pos.x + column_width > width_limit)
+               {
+                       ++i;
+                       if(!complete)
+                       {
+                               drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
+                               break;
+                       }
+                       else
+                       {
+                               pos.x = item_pos.x + hud_fontsize.x * 0.5;
+                               pos.y += hud_fontsize.y * 1.25;
+                       }
+               }
+
+               vector name_pos = pos;
+               if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
+                       name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
+               drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
+               if(field != "")
+               {
+                       h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
+                       h_size.y = hud_fontsize.y;
+                       vector field_pos = pos;
+                       if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned))
+                               field_pos.x += column_width - h_size.x;
+                       if(sbt_highlight)
+                               drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
+                       field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
+                       drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
+               }
+               pos.x += column_width;
+               pos.x += hud_fontsize.x;
+       }
+       return eX * item_pos.x + eY * (item_pos.y + i * hud_fontsize.y * 1.25);
+}
+
+vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
+{
+       int max_players = 999;
+       if(autocvar_hud_panel_scoreboard_maxheight > 0)
+       {
+               float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
+               if(teamplay)
+               {
+                       height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
+                       height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
+                       height /= team_count;
+               }
+               else
+                       height -= panel_bg_padding * 2; // - padding
+               max_players = floor(height / (hud_fontsize.y * 1.25));
+               if(max_players <= 1)
+                       max_players = 1;
+               if(max_players == tm.team_size)
+                       max_players = 999;
+       }
+
+       entity pl;
+       entity me = playerslots[current_player];
+       panel_pos = pos;
+       panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
+       panel_size.y += panel_bg_padding * 2;
+       HUD_Panel_DrawBg();
+
+       vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
+       if(panel.current_panel_bg != "0")
+               end_pos.y += panel_bg_border * 2;
+
+       if(panel_bg_padding)
+       {
+               panel_pos += '1 1 0' * panel_bg_padding;
+               panel_size -= '2 2 0' * panel_bg_padding;
+       }
+
+       pos = panel_pos;
+       vector tmp = eX * panel_size.x + eY * 1.25 * hud_fontsize.y;
+
+       // rounded header
+       if (sbt_bg_alpha)
+               drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
+
+       pos.y += 1.25 * hud_fontsize.y;
+
+       // table background
+       tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
+       if (sbt_bg_alpha)
+               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
+
+
+       // print header row and highlight columns
+       pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size));
+
+       // fill the table and draw the rows
+       bool is_self = false;
+       bool self_shown = false;
+       int i = 0;
+       for(pl = players.sort_next; pl; pl = pl.sort_next)
+       {
+               if(pl.team != tm.team)
+                       continue;
+               if(i == max_players - 2 && pl != me)
+               {
+                       if(!self_shown && me.team == tm.team)
+                       {
+                               Scoreboard_DrawItem(pos, rgb, me, true, i);
+                               self_shown = true;
+                               pos.y += 1.25 * hud_fontsize.y;
+                               ++i;
+                       }
+               }
+               if(i >= max_players - 1)
+               {
+                       pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
+                       break;
+               }
+               is_self = (pl.sv_entnum == current_player);
+               Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
+               if(is_self)
+                       self_shown = true;
+               pos.y += 1.25 * hud_fontsize.y;
+               ++i;
+       }
+
+       panel_size.x += panel_bg_padding * 2; // restore initial width
+       return end_pos;
+}
+
+bool Scoreboard_WouldDraw()
+{
+       if (MUTATOR_CALLHOOK(DrawScoreboard))
+               return false;
+       else if (QuickMenu_IsOpened())
+               return false;
+       else if (HUD_Radar_Clickable())
+               return false;
+       else if (scoreboard_showscores)
+               return true;
+       else if (intermission == 1)
+               return true;
+       else if (intermission == 2)
+               return false;
+       else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
+               return true;
+       else if (scoreboard_showscores_force)
+               return true;
+       return false;
+}
+
+float average_accuracy;
+vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
+{
+       WepSet weapons_stat = WepSet_GetFromStat();
+       WepSet weapons_inmap = WepSet_GetFromStat_InMap();
+       int disownedcnt = 0;
+       int nHidden = 0;
+       FOREACH(Weapons, it != WEP_Null, {
+               int weapon_stats = weapon_accuracy[i - WEP_FIRST];
+
+               WepSet set = it.m_wepset;
+               if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
+               {
+                       if (((it.spawnflags & WEP_FLAG_HIDDEN) || (it.spawnflags & WEP_FLAG_MUTATORBLOCKED)))
+                               ++nHidden;
+                       else
+                               ++disownedcnt;
+               }
+       });
+
+       int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt - nHidden;
+       if (weapon_cnt <= 0) return pos;
+
+       int rows = 1;
+       if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - nHidden - 1) * 0.5))
+               rows = 2;
+       int columnns = ceil(weapon_cnt / rows);
+
+       float weapon_height = 29;
+       float height = hud_fontsize.y + weapon_height;
+
+       drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       pos.y += 1.25 * hud_fontsize.y;
+       if(panel.current_panel_bg != "0")
+               pos.y += panel_bg_border;
+
+       panel_pos = pos;
+       panel_size.y = height * rows;
+       panel_size.y += panel_bg_padding * 2;
+       HUD_Panel_DrawBg();
+
+       vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
+       if(panel.current_panel_bg != "0")
+               end_pos.y += panel_bg_border * 2;
+
+       if(panel_bg_padding)
+       {
+               panel_pos += '1 1 0' * panel_bg_padding;
+               panel_size -= '2 2 0' * panel_bg_padding;
+       }
+
+       pos = panel_pos;
+       vector tmp = panel_size;
+
+       float weapon_width = tmp.x / columnns / rows;
+
+       if (sbt_bg_alpha)
+               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
+
+       if(sbt_highlight)
+       {
+               // column highlighting
+               for (int i = 0; i < columnns; ++i)
+                       if ((i % 2) == 0)
+                               drawfill(pos + eX * weapon_width * rows * i, eY * height * rows + eX * weapon_width * rows, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
+
+               // row highlighting
+               for (int i = 0; i < rows; ++i)
+                       drawfill(pos + eY * weapon_height + eY * height * i, eX * tmp.x + eY * hud_fontsize.y, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
+       }
+
+       average_accuracy = 0;
+       int weapons_with_stats = 0;
+       if (rows == 2)
+               pos.x += weapon_width / 2;
+
+       if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
+               rgb = '1 1 1';
+       else
+               Accuracy_LoadColors();
+
+       float oldposx = pos.x;
+       vector tmpos = pos;
+
+       int column = 0;
+       FOREACH(Weapons, it != WEP_Null, {
+               int weapon_stats = weapon_accuracy[i - WEP_FIRST];
+
+               WepSet set = it.m_wepset;
+               if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
+                       continue;
+
+               float weapon_alpha;
+               if (weapon_stats >= 0)
+                       weapon_alpha = sbt_fg_alpha;
+               else
+                       weapon_alpha = 0.2 * sbt_fg_alpha;
+
+               // weapon icon
+               drawpic_aspect_skin(tmpos, it.model2, eX * weapon_width + eY * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
+               // the accuracy
+               if (weapon_stats >= 0) {
+                       weapons_with_stats += 1;
+                       average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
+
+                       string s;
+                       s = sprintf("%d%%", weapon_stats * 100);
+
+                       float padding;
+                       padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
+
+                       if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
+                               rgb = Accuracy_GetColor(weapon_stats);
+
+                       drawstring(tmpos + eX * padding + eY * weapon_height, s, hud_fontsize, rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
+               }
+               tmpos.x += weapon_width * rows;
+               pos.x += weapon_width * rows;
+               if (rows == 2 && column == columnns - 1) {
+                       tmpos.x = oldposx;
+                       tmpos.y += height;
+                       pos.y += height;
+               }
+               ++column;
+       });
+
+       if (weapons_with_stats)
+               average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
+
+       panel_size.x += panel_bg_padding * 2; // restore initial width
+       return end_pos;
+}
+
+vector MapStats_DrawKeyValue(vector pos, string key, string value) {
+       float px = pos.x;
+       pos.x += hud_fontsize.x * 0.25;
+       drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
+       pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
+       drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
+       pos.x = px;
+       pos.y += hud_fontsize.y;
+
+       return pos;
+}
+
+vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
+       float stat_secrets_found, stat_secrets_total;
+       float stat_monsters_killed, stat_monsters_total;
+       float rows = 0;
+       string val;
+
+       // get monster stats
+       stat_monsters_killed = STAT(MONSTERS_KILLED);
+       stat_monsters_total = STAT(MONSTERS_TOTAL);
+
+       // get secrets stats
+       stat_secrets_found = STAT(SECRETS_FOUND);
+       stat_secrets_total = STAT(SECRETS_TOTAL);
+
+       // get number of rows
+       if(stat_secrets_total)
+               rows += 1;
+       if(stat_monsters_total)
+               rows += 1;
+
+       // if no rows, return
+       if (!rows)
+               return pos;
+
+       //  draw table header
+       drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       pos.y += 1.25 * hud_fontsize.y;
+       if(panel.current_panel_bg != "0")
+               pos.y += panel_bg_border;
+
+       panel_pos = pos;
+       panel_size.y = hud_fontsize.y * rows;
+       panel_size.y += panel_bg_padding * 2;
+       HUD_Panel_DrawBg();
+
+       vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
+       if(panel.current_panel_bg != "0")
+               end_pos.y += panel_bg_border * 2;
+
+       if(panel_bg_padding)
+       {
+               panel_pos += '1 1 0' * panel_bg_padding;
+               panel_size -= '2 2 0' * panel_bg_padding;
+       }
+
+       pos = panel_pos;
+       vector tmp = panel_size;
+
+       if (sbt_bg_alpha)
+               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
+
+       // draw monsters
+       if(stat_monsters_total)
+       {
+               val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
+               pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
+       }
+
+       // draw secrets
+       if(stat_secrets_total)
+       {
+               val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
+               pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
+       }
+
+       panel_size.x += panel_bg_padding * 2; // restore initial width
+       return end_pos;
+}
+
+
+vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_size)
+{
+       int i;
+       RANKINGS_RECEIVED_CNT = 0;
+       for (i=RANKINGS_CNT-1; i>=0; --i)
+               if (grecordtime[i])
+                       ++RANKINGS_RECEIVED_CNT;
+
+       if (RANKINGS_RECEIVED_CNT == 0)
+               return pos;
+
+       vector hl_rgb = rgb + '0.5 0.5 0.5';
+
+       pos.y += hud_fontsize.y;
+       drawstring(pos + eX * panel_bg_padding, _("Rankings"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       pos.y += 1.25 * hud_fontsize.y;
+       if(panel.current_panel_bg != "0")
+               pos.y += panel_bg_border;
+
+       panel_pos = pos;
+
+       float namesize = 0;
+       for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
+       {
+               float f = stringwidth(grecordholder[i], true, hud_fontsize);
+               if(f > namesize)
+                       namesize = f;
+       }
+       bool cut = false;
+       if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
+       {
+               namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
+               cut = true;
+       }
+
+       float ranksize = 3 * hud_fontsize.x;
+       float timesize = 5 * hud_fontsize.x;
+       vector columnsize = eX * (ranksize + timesize + namesize + hud_fontsize.x) + eY * 1.25 * hud_fontsize.y;
+       int columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
+       columns = min(columns, RANKINGS_RECEIVED_CNT);
+
+       // expand name column to fill the entire row
+       float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * columns) / columns;
+       namesize += available_space;
+       columnsize.x += available_space;
+
+       panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y;
+       panel_size.y += panel_bg_padding * 2;
+
+       HUD_Panel_DrawBg();
+
+       vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
+       if(panel.current_panel_bg != "0")
+               end_pos.y += panel_bg_border * 2;
+
+       if(panel_bg_padding)
+       {
+               panel_pos += '1 1 0' * panel_bg_padding;
+               panel_size -= '2 2 0' * panel_bg_padding;
+       }
+
+       pos = panel_pos;
+
+       if (sbt_bg_alpha)
+               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
+
+       vector text_ofs = eX * 0.5 * hud_fontsize.x + eY * (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
+       string str = "";
+       int column = 0, j = 0;
+       for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
+       {
+               float t;
+               t = grecordtime[i];
+               if (t == 0)
+                       continue;
+
+               if(grecordholder[i] == entcs_GetName(player_localnum))
+                       drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
+               else if(!((j + column) & 1) && sbt_highlight)
+                       drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
+
+               str = count_ordinal(i+1);
+               drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
+               drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
+               str = grecordholder[i];
+               if(cut)
+                       str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
+               drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
+
+               pos.y += 1.25 * hud_fontsize.y;
+               j++;
+               if(j >= ceil(RANKINGS_RECEIVED_CNT / columns))
+               {
+                       column++;
+                       j = 0;
+                       pos.x += panel_size.x / columns;
+                       pos.y = panel_pos.y;
+               }
+       }
+
+       panel_size.x += panel_bg_padding * 2; // restore initial width
+       return end_pos;
+}
+
+void Scoreboard_Draw()
+{
+       if(!autocvar__hud_configure)
+       {
+               if(!hud_draw_maximized) return;
+
+               // frametime checks allow to toggle the scoreboard even when the game is paused
+               if(scoreboard_active) {
+                       if(hud_configure_menu_open == 1)
+                               scoreboard_fade_alpha = 1;
+                       float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
+                       if (scoreboard_fadeinspeed && frametime)
+                               scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
+                       else
+                               scoreboard_fade_alpha = 1;
+                       if(hud_fontsize_str != autocvar_hud_fontsize)
+                       {
+                               hud_fontsize = HUD_GetFontsize("hud_fontsize");
+                               Scoreboard_initFieldSizes();
+                               if(hud_fontsize_str)
+                                       strunzone(hud_fontsize_str);
+                               hud_fontsize_str = strzone(autocvar_hud_fontsize);
+                       }
+               }
+               else {
+                       float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
+                       if (scoreboard_fadeoutspeed && frametime)
+                               scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
+                       else
+                               scoreboard_fade_alpha = 0;
+               }
+
+               if (!scoreboard_fade_alpha)
+                       return;
+       }
+       else
+               scoreboard_fade_alpha = 0;
+
+       if (autocvar_hud_panel_scoreboard_dynamichud)
+               HUD_Scale_Enable();
+       else
+               HUD_Scale_Disable();
+
+       if(scoreboard_fade_alpha <= 0)
+               return;
+       panel_fade_alpha *= scoreboard_fade_alpha;
+       HUD_Panel_LoadCvars();
+
+       sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
+       sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
+       sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
+       sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
+       sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
+       sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
+
+       // don't overlap with con_notify
+       if(!autocvar__hud_configure)
+               panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
+
+       float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
+       float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
+       panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width);
+       panel_size.x = fixed_scoreboard_width;
+
+       Scoreboard_UpdatePlayerTeams();
+
+       vector pos = panel_pos;
+       entity pl, tm;
+       string str;
+
+       // Heading
+       vector sb_heading_fontsize;
+       sb_heading_fontsize = hud_fontsize * 2;
+       draw_beginBoldFont();
+       drawstring(pos + eX * panel_bg_padding, _("Scoreboard"), sb_heading_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       draw_endBoldFont();
+
+       pos.y += sb_heading_fontsize.y;
+       if(panel.current_panel_bg != "0")
+               pos.y += panel_bg_border;
+
+       // Draw the scoreboard
+       float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
+       if(scale <= 0)
+               scale = 0.25;
+       vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
+
+       if(teamplay)
+       {
+               vector panel_bg_color_save = panel_bg_color;
+               vector team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
+               if(panel.current_panel_bg != "0")
+                       team_score_baseoffset.x -= panel_bg_border;
+               for(tm = teams.sort_next; tm; tm = tm.sort_next)
+               {
+                       if(tm.team == NUM_SPECTATOR)
+                               continue;
+                       if(!tm.team)
+                               continue;
+
+                       draw_beginBoldFont();
+                       vector rgb = Team_ColorRGB(tm.team);
+                       str = ftos(tm.(teamscores(ts_primary)));
+                       drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
+
+                       if(ts_primary != ts_secondary)
+                       {
+                               str = ftos(tm.(teamscores(ts_secondary)));
+                               drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
+                       }
+                       draw_endBoldFont();
+                       if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
+                               panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
+                       else if(panel_bg_color_team > 0)
+                               panel_bg_color = rgb * panel_bg_color_team;
+                       else
+                               panel_bg_color = rgb;
+                       pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
+               }
+               panel_bg_color = panel_bg_color_save;
+       }
+       else
+       {
+               for(tm = teams.sort_next; tm; tm = tm.sort_next)
+                       if(tm.team != NUM_SPECTATOR)
+                               break;
+               // display it anyway
+               pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
+       }
+
+       if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
+               if(race_speedaward) {
+                       drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+                       pos.y += 1.25 * hud_fontsize.y;
+               }
+               if(race_speedaward_alltimebest) {
+                       drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+                       pos.y += 1.25 * hud_fontsize.y;
+               }
+               pos = Scoreboard_Rankings_Draw(pos, playerslots[player_localnum], panel_bg_color, bg_size);
+       }
+       else if (autocvar_hud_panel_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL)
+               pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
+
+       pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
+
+       // List spectators
+       for(pl = players.sort_next; pl; pl = pl.sort_next)
+       {
+               if(pl.team == NUM_SPECTATOR)
+               {
+                       for(tm = teams.sort_next; tm; tm = tm.sort_next)
+                               if(tm.team == NUM_SPECTATOR)
+                                       break;
+                       str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
+                       draw_beginBoldFont();
+                       drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+                       draw_endBoldFont();
+                       pos.y += 1.25 * hud_fontsize.y;
+
+                       pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
+                       pos.y += 1.25 * hud_fontsize.y;
+
+                       break;
+               }
+       }
+
+       // Print info string
+       float tl, fl, ll;
+       str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
+       tl = STAT(TIMELIMIT);
+       fl = STAT(FRAGLIMIT);
+       ll = STAT(LEADLIMIT);
+       if(gametype == MAPINFO_TYPE_LMS)
+       {
+               if(tl > 0)
+                       str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
+       }
+       else
+       {
+               if(tl > 0)
+                       str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
+               if(fl > 0)
+               {
+                       if(tl > 0)
+                               str = strcat(str, _(" or"));
+                       if(teamplay)
+                       {
+                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl),
+                                       (teamscores_label(ts_primary) == "score")   ? CTX(_("SCO^points")) :
+                                       (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
+                                       TranslateScoresLabel(teamscores_label(ts_primary))));
+                       }
+                       else
+                       {
+                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl),
+                                       (scores_label(ps_primary) == "score")   ? CTX(_("SCO^points")) :
+                                       (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
+                                       TranslateScoresLabel(scores_label(ps_primary))));
+                       }
+               }
+               if(ll > 0)
+               {
+                       if(tl > 0 || fl > 0)
+                               str = strcat(str, _(" or"));
+                       if(teamplay)
+                       {
+                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll),
+                                       (teamscores_label(ts_primary) == "score")   ? CTX(_("SCO^points")) :
+                                       (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
+                                       TranslateScoresLabel(teamscores_label(ts_primary))));
+                       }
+                       else
+                       {
+                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll),
+                                       (scores_label(ps_primary) == "score")   ? CTX(_("SCO^points")) :
+                                       (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
+                                       TranslateScoresLabel(scores_label(ps_primary))));
+                       }
+               }
+       }
+
+       pos.y += 1.2 * hud_fontsize.y;
+       drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+
+       // print information about respawn status
+       float respawn_time = STAT(RESPAWN_TIME);
+       if(!intermission)
+       if(respawn_time)
+       {
+               if(respawn_time < 0)
+               {
+                       // a negative number means we are awaiting respawn, time value is still the same
+                       respawn_time *= -1; // remove mark now that we checked it
+
+                       if(respawn_time < time) // it happens for a few frames when server is respawning the player
+                               str = ""; // draw an empty string to not change suddenly scoreboard_bottom
+                       else
+                               str = sprintf(_("^1Respawning in ^3%s^1..."),
+                                       (autocvar_hud_panel_scoreboard_respawntime_decimals ?
+                                               count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
+                                               :
+                                               count_seconds(ceil(respawn_time - time))
+                                       )
+                               );
+               }
+               else if(time < respawn_time)
+               {
+                       str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
+                               (autocvar_hud_panel_scoreboard_respawntime_decimals ?
+                                       count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
+                                       :
+                                       count_seconds(ceil(respawn_time - time))
+                               )
+                       );
+               }
+               else if(time >= respawn_time)
+                       str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
+
+               pos.y += 1.2 * hud_fontsize.y;
+               drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+       }
+
+       scoreboard_bottom = pos.y + 2 * hud_fontsize.y;
+}
diff --git a/qcsrc/client/hud/panel/scoreboard.qh b/qcsrc/client/hud/panel/scoreboard.qh
new file mode 100644 (file)
index 0000000..a560b74
--- /dev/null
@@ -0,0 +1,13 @@
+#pragma once
+#include "../panel.qh"
+
+bool scoreboard_active;
+float scoreboard_fade_alpha;
+
+void Cmd_Scoreboard_SetFields(float argc);
+void Scoreboard_Draw();
+void Scoreboard_InitScores();
+void Scoreboard_UpdatePlayerTeams();
+void Scoreboard_UpdatePlayerPos(entity pl);
+void Scoreboard_UpdateTeamPos(entity Team);
+bool Scoreboard_WouldDraw();
index 5a7194a45849ee7967f97e2a9694fccaf3b97c0c..45f46b4087ad35da1c4cff70aabe9799a40463f6 100644 (file)
@@ -1,4 +1,7 @@
 #include "timer.qh"
+
+// Timer (#5)
+
 void HUD_Timer()
 {
        if(!autocvar__hud_configure)
@@ -6,7 +9,7 @@ void HUD_Timer()
                if(!autocvar_hud_panel_timer) return;
        }
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        draw_beginBoldFont();
 
@@ -18,7 +21,7 @@ void HUD_Timer()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        if(panel_bg_padding)
        {
                pos += '1 1 0' * panel_bg_padding;
@@ -26,7 +29,7 @@ void HUD_Timer()
        }
 
        string timer;
-       float timelimit, elapsedTime, timeleft, minutesLeft;
+       float timelimit, timeleft, minutesLeft;
 
        timelimit = STAT(TIMELIMIT);
 
@@ -47,21 +50,20 @@ void HUD_Timer()
        }
 
        vector timer_color;
-       if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit
+       if(intermission_time || minutesLeft >= 5 || warmup_stage || timelimit == 0)
                timer_color = '1 1 1'; //white
        else if(minutesLeft >= 1)
                timer_color = '1 1 0'; //yellow
        else
                timer_color = '1 0 0'; //red
 
-       if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) {
-               if (time < STAT(GAMESTARTTIME)) {
-                       //while restart is still active, show 00:00
-                       timer = seconds_tostring(0);
-               } else {
-                       elapsedTime = floor(time - STAT(GAMESTARTTIME));
-                       timer = seconds_tostring(elapsedTime);
-               }
+       if (intermission_time) {
+               timer = seconds_tostring(max(0, floor(intermission_time - STAT(GAMESTARTTIME))));
+       } else if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) {
+               if (time < STAT(GAMESTARTTIME))
+                       timer = seconds_tostring(0); //while restart is still active, show 00:00
+               else
+                       timer = seconds_tostring(floor(time - STAT(GAMESTARTTIME)));
        } else {
                if(warmup_stage)
                        timer = seconds_tostring(warmup_timeleft);
index 89c784a1083be4e0ece863ca00419549520c05de..1c2ea03bc8ec3a11708c7921a976855b6e4ee9fb 100644 (file)
@@ -2,11 +2,20 @@
 
 #include <common/mapinfo.qh>
 
-/** Vote window (#9) */
+// Vote (#9)
+
 void HUD_Vote()
 {
        if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS)))
        {
+               // this dialog gets overriden by the uid2name menu dialog, if it exists
+               // TODO remove this client side uid2name dialog in the next release
+               if(!autocvar__menu_alpha)
+                       uid2name_dialog = 0;
+
+               if (!uid2name_dialog)
+                       localcmd("menu_cmd directmenu Uid2Name\n");
+
                vote_active = 1;
                if (autocvar__hud_configure)
                {
@@ -24,14 +33,12 @@ void HUD_Vote()
        if(!autocvar__hud_configure)
        {
                if(!autocvar_hud_panel_vote) return;
-
-               panel_fg_alpha = autocvar_hud_panel_fg_alpha;
-               panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha;
-
-               if(panel_bg_alpha_str == "") {
-                       panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha);
-               }
-               panel_bg_alpha = stof(panel_bg_alpha_str);
+               /*
+               if(cvar("hud_panel_vote_test")) {
+                       if(vote_called_vote) strunzone(vote_called_vote); vote_called_vote = strzone("^1test the vote panel");
+                       vote_active = true; vote_yescount = 3; vote_nocount = 2; vote_needed = 4;
+               } else vote_active = false;
+               */
        }
        else
        {
@@ -52,10 +59,18 @@ void HUD_Vote()
        else
                vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1);
 
-       if(!vote_alpha)
+       a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1);
+       if(a <= 0)
                return;
-
-       HUD_Panel_UpdateCvars();
+       //panel_fade_alpha *= a;
+       // nothing can hide this panel, not even the menu
+       float hud_fade_alpha_save = hud_fade_alpha;
+       if(uid2name_dialog && autocvar__menu_alpha)
+               hud_fade_alpha = 0;
+       else
+               hud_fade_alpha = a;
+       HUD_Panel_LoadCvars();
+       hud_fade_alpha = hud_fade_alpha_save;
 
        if(uid2name_dialog)
        {
@@ -63,18 +78,15 @@ void HUD_Vote()
                panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight;
        }
 
-    // these must be below above block
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
 
-       a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1);
        if (autocvar_hud_panel_vote_dynamichud)
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(a);
-       a = panel_fg_alpha * a;
+       HUD_Panel_DrawBg();
 
        if(panel_bg_padding)
        {
@@ -103,42 +115,44 @@ void HUD_Vote()
        s = _("A vote has been called for:");
        if(uid2name_dialog)
                s = _("Allow servers to store and display your name?");
-       drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+       drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
        s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors);
        if(autocvar__hud_configure)
                s = _("^1Configure the HUD");
-       drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL);
+       drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
 
        // print the yes/no counts
-    s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount);
-       drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL);
-    s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount);
-       drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL);
+       s = sprintf("^2%s ^7(%d)", getcommandkey_forcename(_("Yes"), "vyes"), vote_yescount);
+       drawcolorcodedstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
+       s = sprintf("^1%s ^7(%d)", getcommandkey_forcename(_("No"), "vno"), vote_nocount);
+       drawcolorcodedstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
 
+       pos.y += (5/8) * mySize.y;
+       vector tmp_size = eX * mySize.x + eY * (3/8) * mySize.y;
        // draw the progress bar backgrounds
-       drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+       drawpic_skin(pos, "voteprogress_back", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 
        // draw the highlights
        if(vote_highlighted == 1) {
                drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y);
-               drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+               drawpic_skin(pos, "voteprogress_voted", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
        }
        else if(vote_highlighted == -1) {
                drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y);
-               drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+               drawpic_skin(pos, "voteprogress_voted", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
        }
 
        // draw the progress bars
        if(vote_yescount && vote_needed)
        {
                drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y);
-               drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+               drawpic_skin(pos, "voteprogress_prog", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
        }
 
        if(vote_nocount && vote_needed)
        {
                drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y);
-               drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+               drawpic_skin(pos, "voteprogress_prog", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
        }
 
        drawresetcliparea();
index 984cb4f50519097481f08ee8d701babcf058e160..db25d532a016f938e31a04045ac54e1886f03617 100644 (file)
@@ -1,5 +1,7 @@
 #include "weapons.qh"
-// Weapon icons (#0)
+
+
+// Weapons (#0)
 
 entity weaponorder[Weapons_MAX];
 void weaponorder_swap(int i, int j, entity pass)
@@ -23,7 +25,7 @@ int weaponorder_cmp(int i, int j, entity pass)
        int nHidden = 0; \
        FOREACH(Weapons, it != WEP_Null, { \
                if (weapons_stat & WepSet_FromWeapon(it)) continue; \
-               if (it.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1; \
+               if (it.spawnflags & WEP_FLAG_HIDDEN || it.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1; \
        }); \
        vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1) - nHidden, panel_size, aspect); \
        columns = table_size.x; \
@@ -56,6 +58,8 @@ void HUD_Weapons()
        float when = max(1, autocvar_hud_panel_weapons_complainbubble_time);
        float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime);
 
+       bool infinite_ammo = (STAT(ITEMS) & IT_UNLIMITED_WEAPON_AMMO);
+
        vector weapon_pos, weapon_size = '0 0 0';
        vector color;
 
@@ -68,6 +72,8 @@ void HUD_Weapons()
        {
                if((!autocvar_hud_panel_weapons) || (spectatee_status == -1))
                        return;
+               if(STAT(HEALTH) <= 0 && autocvar_hud_panel_weapons_hide_ondeath)
+                       return;
                if(timeout && time >= weapontime + timeout + timeout_effect_length)
                if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin)))
                {
@@ -77,7 +83,7 @@ void HUD_Weapons()
        }
 
        // update generic hud functions
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        // figure out weapon order (how the weapons are sorted) // TODO make this configurable
        if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0])
@@ -147,8 +153,8 @@ void HUD_Weapons()
        {
                if(autocvar__hud_configure)
                {
-                       if(menu_enabled != 2)
-                               HUD_Panel_DrawBg(1); // also draw the bg of the entire panel
+                       if(hud_configure_menu_open != 2)
+                               HUD_Panel_DrawBg(); // also draw the bg of the entire panel
                }
 
                // do we own this weapon?
@@ -310,7 +316,7 @@ void HUD_Weapons()
                HUD_Scale_Enable();
        else
                HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if(center.x == -1)
                return; // panel has gone off screen
@@ -330,7 +336,7 @@ void HUD_Weapons()
        }
 
        // calculate position/size for visual bar displaying ammount of ammo status
-       if (autocvar_hud_panel_weapons_ammo)
+       if (!infinite_ammo && autocvar_hud_panel_weapons_ammo)
        {
                ammo_color = stov(autocvar_hud_panel_weapons_ammo_color);
                ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha;
@@ -356,7 +362,6 @@ void HUD_Weapons()
        vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1);
        vector noncurrent_size = weapon_size * bound(0.01, autocvar_hud_panel_weapons_noncurrent_scale, 1);
        float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1);
-       bool isCurrent;
        static vector weapon_pos_current = '-1 0 0';
        if(weapon_pos_current.x == -1)
                weapon_pos_current = panel_pos;
@@ -368,7 +373,9 @@ void HUD_Weapons()
                switch_speed = frametime * autocvar_hud_panel_weapons_selection_speed;
        vector radius_size = weapon_size * (autocvar_hud_panel_weapons_selection_radius + 1);
 
-       if(!panel_switchweapon)
+       if(switchweapon == WEP_Null)
+               panel_switchweapon = NULL;
+       else if(!panel_switchweapon)
                panel_switchweapon = switchweapon;
 
        // draw background behind currently selected weapon
@@ -401,8 +408,7 @@ void HUD_Weapons()
                weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y);
 
                // update position of the currently selected weapon
-               isCurrent = (it == panel_switchweapon);
-               if(isCurrent)
+               if(it == panel_switchweapon)
                {
                        if(weapon_pos_current.y > weapon_pos.y)
                                weapon_pos_current.y = max(weapon_pos.y, weapon_pos_current.y - switch_speed * (weapon_pos_current.y - weapon_pos.y));
@@ -466,7 +472,7 @@ void HUD_Weapons()
                        }
 
                        // draw ammo status bar
-                       if(autocvar_hud_panel_weapons_ammo && (it.ammo_field != ammo_none))
+                       if(!infinite_ammo && autocvar_hud_panel_weapons_ammo && (it.ammo_field != ammo_none))
                        {
                                float ammo_full;
                                a = getstati(GetAmmoStat(it.ammo_field)); // how much ammo do we have?
index c62a72e6e9303cfa41ae3d888ad5d0e6bed175aa..36ca217240f0ba0e8a6966fcb25b06829df43491 100644 (file)
@@ -1,20 +1,21 @@
 #include "main.qh"
 
 #include <common/effects/qc/all.qh>
-#include "hud/all.qh"
+#include "hud/_mod.qh"
 #include "mapvoting.qh"
 #include "mutators/events.qh"
+#include "hud/panel/scoreboard.qh"
 #include "hud/panel/quickmenu.qh"
-#include "scoreboard.qh"
 #include "shownames.qh"
 #include <common/t_items.qh>
 #include "wall.qh"
 #include "weapons/projectile.qh"
 #include <common/deathtypes/all.qh>
-#include <common/items/all.qh>
+#include <common/items/_mod.qh>
 #include <common/mapinfo.qh>
 #include <common/minigames/cl_minigames.qh>
 #include <common/minigames/cl_minigames_hud.qh>
+#include <common/net_linked.qh>
 #include <common/net_notice.qh>
 #include <common/triggers/include.qh>
 #include <common/vehicles/all.qh>
@@ -108,7 +109,6 @@ void CSQC_Init()
        binddb = db_create();
        tempdb = db_create();
        ClientProgsDB = db_load("client.db");
-       compressShortVector_init();
 
        draw_endBoldFont();
 
@@ -131,13 +131,7 @@ void CSQC_Init()
 
        registercvar("cl_spawn_near_teammate", "1");
 
-       gametype = 0;
-
-       // hud_fields uses strunzone on the titles!
-       for(int i = 0; i < MAX_HUD_FIELDS; ++i)
-               hud_title[i] = strzone("(null)");
-
-       Cmd_HUD_SetFields(0);
+       gametype = NULL;
 
        postinit = false;
 
@@ -185,8 +179,8 @@ void Shutdown()
 {
        WarpZone_Shutdown();
 
-       remove(teams);
-       remove(players);
+       delete(teams);
+       delete(players);
        db_close(binddb);
        db_close(tempdb);
        if(autocvar_cl_db_saveasdump)
@@ -235,7 +229,7 @@ float SetTeam(entity o, int Team)
                        default:
                                if(GetTeam(Team, false) == NULL)
                                {
-                                       LOG_TRACEF("trying to switch to unsupported team %d\n", Team);
+                                       LOG_TRACEF("trying to switch to unsupported team %d", Team);
                                        Team = NUM_SPECTATOR;
                                }
                                break;
@@ -251,7 +245,7 @@ float SetTeam(entity o, int Team)
                        default:
                                if(GetTeam(Team, false) == NULL)
                                {
-                                       LOG_TRACEF("trying to switch to unsupported team %d\n", Team);
+                                       LOG_TRACEF("trying to switch to unsupported team %d", Team);
                                        Team = NUM_SPECTATOR;
                                }
                                break;
@@ -323,9 +317,9 @@ void Playerchecker_Think(entity this)
                                e.ping_movementloss = 0;
                                //e.gotscores = 0; // we might already have the scores...
                                int t = entcs_GetScoreTeam(i);
-                               if (t) SetTeam(e, t); // will not hurt; later updates come with HUD_UpdatePlayerTeams
+                               if (t) SetTeam(e, t); // will not hurt; later updates come with Scoreboard_UpdatePlayerTeams
                                RegisterPlayer(e);
-                               HUD_UpdatePlayerPos(e);
+                               Scoreboard_UpdatePlayerPos(e);
                        }
                }
        }
@@ -385,23 +379,21 @@ void Ent_RemovePlayerScore(entity this)
        if(this.owner) {
                SetTeam(this.owner, -1);
                this.owner.gotscores = 0;
-               for(int i = 0; i < MAX_SCORE; ++i) {
-                       this.owner.(scores[i]) = 0; // clear all scores
-               }
+               FOREACH(Scores, true, {
+                       this.owner.(scores(it)) = 0; // clear all scores
+               });
        }
 }
 
 NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
 {
        make_pure(this);
-       int i, n;
-       bool isNew;
        entity o;
 
        // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN
        // (no I've never heard of M-x replace-string, sed, or anything like that)
-       isNew = !this.owner; // workaround for DP bug
-       n = ReadByte()-1;
+       bool isNew = !this.owner; // workaround for DP bug
+       int n = ReadByte()-1;
 
 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
        if(!isNew && n != this.sv_entnum)
@@ -429,27 +421,23 @@ NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
        //playerchecker will do this for us later, if it has not already done so
 
     int sf, lf;
-#if MAX_SCORE <= 8
-       sf = ReadByte();
-       lf = ReadByte();
-#else
        sf = ReadShort();
        lf = ReadShort();
-#endif
-    int p;
-       for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)
-               if(sf & p)
+       FOREACH(Scores, true, {
+        int p = 1 << (i % 16);
+               if (sf & p)
                {
-                       if(lf & p)
-                               o.(scores[i]) = ReadInt24_t();
+                       if (lf & p)
+                               o.(scores(it)) = ReadInt24_t();
                        else
-                               o.(scores[i]) = ReadChar();
+                               o.(scores(it)) = ReadChar();
                }
+    });
 
        return = true;
 
        if(o.sort_prev)
-               HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet!
+               Scoreboard_UpdatePlayerPos(o); // if not registered, we cannot do this yet!
 
        this.entremove = Ent_RemovePlayerScore;
 }
@@ -476,14 +464,14 @@ NET_HANDLE(ENT_CLIENT_TEAMSCORES, bool isnew)
                if(sf & p)
                {
                        if(lf & p)
-                               o.(teamscores[i]) = ReadInt24_t();
+                               o.(teamscores(i)) = ReadInt24_t();
                        else
-                               o.(teamscores[i]) = ReadChar();
+                               o.(teamscores(i)) = ReadChar();
                }
 
        return = true;
 
-       HUD_UpdateTeamPos(o);
+       Scoreboard_UpdateTeamPos(o);
 }
 
 NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
@@ -516,6 +504,22 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
        else
                angles_held_status = 0;
 
+       if(f & 16)
+       {
+               num_spectators = ReadByte();
+
+               float i, slot;
+
+               for(i = 0; i < MAX_SPECTATORS; ++i)
+                       spectatorlist[i] = 0; // reset list first
+
+               for(i = 0; i < num_spectators; ++i)
+               {
+                       slot = ReadByte();
+                       spectatorlist[i] = slot - 1;
+               }
+       }
+
        return = true;
 
        if(newspectatee_status != spectatee_status)
@@ -684,6 +688,8 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
        spn_origin.y = ReadCoord();
        spn_origin.z = ReadCoord();
 
+       this.team = (teamnum + 1);
+
        //if(is_new)
        //{
                this.origin = spn_origin;
@@ -697,12 +703,13 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        precache_model(this.mdl);
                        setmodel(this, this.mdl);
                        this.drawmask = MASK_NORMAL;
-                       //this.movetype = MOVETYPE_NOCLIP;
+                       //this.move_movetype = MOVETYPE_NOCLIP;
                        //this.draw = Spawn_Draw;
+                       IL_PUSH(g_drawables, this);
                }*/
                if(autocvar_cl_spawn_point_particles)
                {
-                       if((serverflags & SERVERFLAG_TEAMPLAY))
+                       if(teamplay)
                        {
                                switch(teamnum)
                                {
@@ -716,6 +723,7 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
 
                        this.draw = Spawn_Draw;
+                       if (is_new) IL_PUSH(g_drawables, this);
                        setpredraw(this, Spawn_PreDraw);
                        this.fade_start = autocvar_cl_spawn_point_dist_min;
                        this.fade_end = autocvar_cl_spawn_point_dist_max;
@@ -780,8 +788,8 @@ NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new)
 
 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.
 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.
-void CSQC_Ent_Update(bool isnew)
-{ENGINE_EVENT();
+void CSQC_Ent_Update(entity this, bool isnew)
+{
        this.sourceLoc = __FILE__ ":" STR(__LINE__);
        int t = ReadByte();
 
@@ -825,12 +833,13 @@ void CSQC_Ent_Update(bool isnew)
                if (autocvar_developer_csqcentities)
             LOG_INFOF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
                done = it.m_read(this, NULL, isnew);
+               MUTATOR_CALLHOOK(Ent_Update, this, isnew);
                break;
        });
        time = savetime;
        if (!done)
        {
-               LOG_FATALF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
+               LOG_FATALF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
        }
 }
 
@@ -860,16 +869,16 @@ void Ent_Remove(entity this)
        // TODO possibly set more stuff to defaults
 }
 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(this) as well.
-void CSQC_Ent_Remove()
-{ENGINE_EVENT();
+void CSQC_Ent_Remove(entity this)
+{
        if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype);
        if (wasfreed(this))
        {
-               LOG_WARNING("CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
+               LOG_WARN("CSQC_Ent_Remove called for already removed entity. Packet loss?");
                return;
        }
        if (this.enttype) Ent_Remove(this);
-       remove(this);
+       delete(this);
 }
 
 void Gamemode_Init()
@@ -935,22 +944,22 @@ void Gamemode_Init();
 NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew)
 {
        make_pure(this);
-       gametype = ReadInt24_t();
+       gametype = ReadRegistered(Gametypes);
+       teamplay = _MapInfo_GetTeamPlayBool(gametype);
        HUD_ModIcons_SetFunc();
-       for (int i = 0; i < MAX_SCORE; ++i)
-       {
-               if (scores_label[i]) strunzone(scores_label[i]);
-               scores_label[i] = strzone(ReadString());
-               scores_flags[i] = ReadByte();
-       }
+       FOREACH(Scores, true, {
+               if (scores_label(it)) strunzone(scores_label(it));
+               scores_label(it) = strzone(ReadString());
+               scores_flags(it) = ReadByte();
+       });
        for (int i = 0; i < MAX_TEAMSCORE; ++i)
        {
-               if (teamscores_label[i]) strunzone(teamscores_label[i]);
-               teamscores_label[i] = strzone(ReadString());
-               teamscores_flags[i] = ReadByte();
+               if (teamscores_label(i)) strunzone(teamscores_label(i));
+               teamscores_label(i) = strzone(ReadString());
+               teamscores_flags(i) = ReadByte();
        }
        return = true;
-       HUD_InitScores();
+       Scoreboard_InitScores();
        Gamemode_Init();
 }
 
@@ -983,6 +992,42 @@ NET_HANDLE(ENT_CLIENT_INIT, bool isnew)
        if (!postinit) PostInit();
 }
 
+float GetSpeedUnitFactor(int speed_unit)
+{
+       switch(speed_unit)
+       {
+               default:
+               case 1:
+                       return 1.0;
+               case 2:
+                       return 0.0254;
+               case 3:
+                       return 0.0254 * 3.6;
+               case 4:
+                       return 0.0254 * 3.6 * 0.6213711922;
+               case 5:
+                       return 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
+       }
+}
+
+string GetSpeedUnit(int speed_unit)
+{
+       switch(speed_unit)
+       {
+               default:
+               case 1:
+                       return _(" qu/s");
+               case 2:
+                       return _(" m/s");
+               case 3:
+                       return _(" km/h");
+               case 4:
+                       return _(" mph");
+               case 5:
+                       return _(" knots");
+       }
+}
+
 NET_HANDLE(TE_CSQC_RACE, bool isNew)
 {
        int b = ReadByte();
@@ -1071,16 +1116,22 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew)
                        race_server_record = ReadInt24_t();
                        break;
                case RACE_NET_SPEED_AWARD:
-                       race_speedaward = ReadInt24_t();
+                       race_speedaward = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
                        if(race_speedaward_holder)
                                strunzone(race_speedaward_holder);
                        race_speedaward_holder = strzone(ReadString());
+                       if(race_speedaward_unit)
+                               strunzone(race_speedaward_unit);
+                       race_speedaward_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit));
                        break;
                case RACE_NET_SPEED_AWARD_BEST:
-                       race_speedaward_alltimebest = ReadInt24_t();
+                       race_speedaward_alltimebest = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
                        if(race_speedaward_alltimebest_holder)
                                strunzone(race_speedaward_alltimebest_holder);
                        race_speedaward_alltimebest_holder = strzone(ReadString());
+                       if(race_speedaward_alltimebest_unit)
+                               strunzone(race_speedaward_alltimebest_unit);
+                       race_speedaward_alltimebest_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit));
                        break;
                case RACE_NET_SERVER_RANKINGS:
                        float prevpos, del;
@@ -1175,13 +1226,13 @@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew)
        }
 }
 
-string getcommandkey(string text, string command)
+string _getcommandkey(string cmd_name, string command, bool forcename)
 {
        string keys;
        float n, j, k, l = 0;
 
        if (!autocvar_hud_showbinds)
-               return text;
+               return cmd_name;
 
        keys = db_get(binddb, command);
        if (keys == "")
@@ -1215,12 +1266,12 @@ string getcommandkey(string text, string command)
 
        if (keys == "NO_KEY") {
                if (autocvar_hud_showbinds > 1)
-                       return sprintf(_("%s (not bound)"), text);
+                       return sprintf(_("%s (not bound)"), cmd_name);
                else
-                       return text;
+                       return cmd_name;
        }
-       else if (autocvar_hud_showbinds > 1)
-               return sprintf("%s (%s)", text, keys);
+       else if (autocvar_hud_showbinds > 1 || forcename)
+               return sprintf("%s (%s)", cmd_name, keys);
        else
                return keys;
 }
index 8601d26b589338b2dc8b18a342e0a68abc230396..54ed9e2c39aafd90498b2ad39633fe3846540a89 100644 (file)
@@ -1,13 +1,7 @@
 #pragma once
 
 #include <common/constants.qh>
-#include <common/weapons/all.qh>
-
-// --------------------------------------------------------------------------
-// MENU Functionality
-
-// --------------------------------------------------------------------------
-// Onslaught
+#include <common/weapons/_all.qh>
 
 // Map coordinate base calculations need these
 vector mi_center;
@@ -15,18 +9,8 @@ vector mi_scale;
 // Minimap
 string minimapname;
 
-// --------------------------------------------------------------------------
-// General stuff
-
 float postinit;
-float gametype;
-
-//float sorted_players;
-//float sorted_teams;
-
-// Defs
-//.float ctf_state;
-//.float health;
+entity gametype;
 
 float FONT_USER = 8;
 
@@ -37,45 +21,11 @@ void draw_cursor(vector pos, vector ofs, string img, vector col, float a);
 void draw_cursor_normal(vector pos, vector col, float a);
 void LoadMenuSkinValues();
 
-// --------------------------------------------------------------------------
-// Scoreboard stuff
-
-const int MAX_HUD_FIELDS = 16;
-
-const int SP_END = -1;
-
-const int SP_PING = -2;
-const int SP_NAME = -3;
-const int SP_KDRATIO = -4;
-const int SP_CLRATIO = -5;
-const int SP_PL = -6;
-const int SP_FRAGS = -7;
-const int SP_SUM = -8;
-
-const int SP_SEPARATOR = -100;
-
-float hud_field[MAX_HUD_FIELDS + 1];
-float hud_size[MAX_HUD_FIELDS + 1];
-string hud_title[MAX_HUD_FIELDS + 1];
-int hud_num_fields;
-
-string scores_label[MAX_SCORE];
-int scores_flags[MAX_SCORE];
-string teamscores_label[MAX_SCORE];
-int teamscores_flags[MAX_SCORE];
-.int scores[MAX_SCORE];
-.float teamscores[MAX_TEAMSCORE];
-
-#define IS_INCREASING(x) ( (x)&SFL_LOWER_IS_BETTER )
-#define IS_DECREASING(x) ( !((x)&SFL_LOWER_IS_BETTER) )
-
-
 vector hud_fontsize;
 
 float RANKINGS_RECEIVED_CNT;
 string grecordholder[RANKINGS_CNT];
 float grecordtime[RANKINGS_CNT];
-//float csqc_flags;
 
 entity playerslots[255]; // 255 is engine limit on maxclients
 entity teamslots[17];    // 17 teams (including "spectator team")
@@ -85,11 +35,20 @@ entity teamslots[17];    // 17 teams (including "spectator team")
 .float eliminated;
 
 .void(entity) draw;
+IntrusiveList g_drawables;
+STATIC_INIT(g_drawables) { g_drawables = IL_NEW(); }
 .void(entity) draw2d;
+IntrusiveList g_drawables_2d;
+STATIC_INIT(g_drawables_2d) { g_drawables_2d = IL_NEW(); }
 .void(entity) entremove;
 float drawframetime;
 vector view_origin, view_forward, view_right, view_up;
 
+IntrusiveList g_radarlinks;
+STATIC_INIT(g_radarlinks) { g_radarlinks = IL_NEW(); }
+IntrusiveList g_radaricons;
+STATIC_INIT(g_radaricons) { g_radaricons = IL_NEW(); }
+
 bool button_zoom;
 bool spectatorbutton_zoom;
 bool button_attack2;
@@ -103,7 +62,9 @@ float warmup_stage;
 
 void Fog_Force();
 
-string getcommandkey(string text, string command);
+string _getcommandkey(string text, string command, bool forcename);
+#define getcommandkey(cmd_name, command) _getcommandkey(cmd_name, command, false)
+#define getcommandkey_forcename(cmd_name, command) _getcommandkey(cmd_name, command, true)
 
 string vote_called_vote;
 float ready_waiting;
@@ -138,7 +99,15 @@ const int HOOK_END =      2;
 
 float g_trueaim_minrange;
 
-float hud;
+int hud;
 float view_quality;
+
+int num_spectators;
+const int MAX_SPECTATORS = 7;
+int spectatorlist[MAX_SPECTATORS];
+
 int framecount;
 .float health;
+
+float GetSpeedUnitFactor(int speed_unit);
+string GetSpeedUnit(int speed_unit);
index bfc25e99c134116524b39fe0934773484e1627f7..7b07b680057196bb325bda90f4d4dd03fd8866a3 100644 (file)
@@ -1,7 +1,7 @@
 #include "mapvoting.qh"
 
-#include "hud/all.qh"
-#include "scoreboard.qh"
+#include "hud/_mod.qh"
+#include "hud/panel/scoreboard.qh"
 
 #include <common/mapinfo.qh>
 
@@ -24,7 +24,6 @@ float mv_timeout;
 float mv_top2_time;
 float mv_top2_alpha;
 
-vector mv_mousepos;
 int mv_selection;
 int mv_columns;
 int mv_mouse_selection;
@@ -84,14 +83,15 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g
                alpha = mv_top2_alpha; // Fade away if not one of the top 2 choice
        else
                alpha = 1; // Normal, full alpha
+       alpha *= panel_fg_alpha;
 
        // Bounding box details
        float rect_margin = hud_fontsize.y / 2;
 
-       pos.x += rect_margin + autocvar_scoreboard_border_thickness;
-       pos.y += rect_margin + autocvar_scoreboard_border_thickness;
-       maxh -= 2 * (rect_margin + autocvar_scoreboard_border_thickness);
-       tsize -= 2 * (rect_margin + autocvar_scoreboard_border_thickness);
+       pos.x += rect_margin + autocvar_hud_panel_mapvote_highlight_border;
+       pos.y += rect_margin + autocvar_hud_panel_mapvote_highlight_border;
+       maxh -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
+       tsize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
 
        vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
        vector rect_size = '1 1 0';
@@ -101,15 +101,15 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g
        // Highlight selected item
        if(id == mv_selection && (mv_flags[id] & GTV_AVAILABLE))
        {
-               drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
+               drawfill(rect_pos, rect_size, '1 1 1', 0.1 * panel_fg_alpha, DRAWFLAG_NORMAL);
        }
 
        // Highlight current vote
        vector rgb = MapVote_RGB(id);
        if(id == mv_ownvote)
        {
-               drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL);
-               drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
+               drawfill(rect_pos, rect_size, rgb, 0.1 * alpha, DRAWFLAG_NORMAL);
+               drawborderlines(autocvar_hud_panel_mapvote_highlight_border, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
        }
 
        vector offset = pos;
@@ -177,11 +177,11 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g
                drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
                next = last;
                last = last.chain;
-               remove(next);
+               delete(next);
        }
 
        // Cleanup
-       remove(title);
+       delete(title);
 }
 
 void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float _count, int id)
@@ -193,10 +193,10 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin
 
        float rect_margin = hud_fontsize.y / 2;
 
-       pos.x += rect_margin + autocvar_scoreboard_border_thickness;
-       pos.y += rect_margin + autocvar_scoreboard_border_thickness;
-       isize -= 2 * (rect_margin + autocvar_scoreboard_border_thickness);
-       tsize -= 2 * (rect_margin + autocvar_scoreboard_border_thickness);
+       pos.x += rect_margin + autocvar_hud_panel_mapvote_highlight_border;
+       pos.y += rect_margin + autocvar_hud_panel_mapvote_highlight_border;
+       isize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
+       tsize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
 
        vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
        vector rect_size = '1 1 0';
@@ -230,17 +230,18 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin
                theAlpha = mv_top2_alpha;
        else
                theAlpha = 1;
+       theAlpha *= panel_fg_alpha;
 
        // Highlight selected item
        if(id == mv_selection && (mv_flags[id] & GTV_AVAILABLE))
-               drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
+               drawfill(rect_pos, rect_size, '1 1 1', 0.1 * panel_fg_alpha, DRAWFLAG_NORMAL);
 
        // Highlight current vote
        vector rgb = MapVote_RGB(id);
        if(id == mv_ownvote)
        {
-               drawfill(rect_pos, rect_size, rgb, 0.1*theAlpha, DRAWFLAG_NORMAL);
-               drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, theAlpha, DRAWFLAG_NORMAL);
+               drawfill(rect_pos, rect_size, rgb, 0.1 * theAlpha, DRAWFLAG_NORMAL);
+               drawborderlines(autocvar_hud_panel_mapvote_highlight_border, rect_pos, rect_size, rgb, theAlpha, DRAWFLAG_NORMAL);
        }
 
        drawstring(text_pos, label, hud_fontsize, rgb, theAlpha, DRAWFLAG_NORMAL);
@@ -272,7 +273,7 @@ void MapVote_DrawAbstain(vector pos, float isize, float tsize, float _count, int
        text_size = stringwidth(label, false, hud_fontsize);
 
        pos.x -= text_size*0.5;
-       drawstring(pos, label, hud_fontsize, rgb, 1, DRAWFLAG_NORMAL);
+       drawstring(pos, label, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
 }
 
 vector MapVote_GridVec(vector gridspec, int i, int m)
@@ -295,10 +296,10 @@ float MapVote_Selection(vector topleft, vector cellsize, float rows, float colum
        for (r = 0; r < rows; ++r)
                for (c = 0; c < columns; ++c)
                {
-                       if (mv_mousepos.x >= topleft.x + cellsize.x *  c &&
-                               mv_mousepos.x <= topleft.x + cellsize.x * (c + 1) &&
-                               mv_mousepos.y >= topleft.y + cellsize.y *  r &&
-                               mv_mousepos.y <= topleft.y + cellsize.y * (r + 1))
+                       if (mousepos.x >= topleft.x + cellsize.x *  c &&
+                               mousepos.x <= topleft.x + cellsize.x * (c + 1) &&
+                               mousepos.y >= topleft.y + cellsize.y *  r &&
+                               mousepos.y <= topleft.y + cellsize.y * (r + 1))
                        {
                                mv_mouse_selection = r * columns + c;
                                break;
@@ -332,15 +333,14 @@ void MapVote_Draw()
        if(!mv_active)
                return;
 
+       HUD_Panel_LoadCvars();
+
        if (!autocvar_hud_cursormode)
        {
-               vector mpos = mv_mousepos + getmousepos();
-               mpos.x = bound(0, mpos.x, vid_conwidth);
-               mpos.y = bound(0, mpos.y, vid_conheight);
-
-               if ( mpos.x != mv_mousepos.x || mpos.y != mv_mousepos.y )
+               vector mpos = mousepos;
+               update_mousepos();
+               if (mpos.x != mousepos.x || mpos.y != mousepos.y)
                        mv_selection_keyboard = 0;
-               mv_mousepos = mpos;
        }
 
        center = (vid_conwidth - 1)/2;
@@ -364,14 +364,14 @@ void MapVote_Draw()
 
        map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map"));
        pos.x = center - stringwidth(map, false, hud_fontsize * 2) * 0.5;
-       drawstring(pos, map, hud_fontsize * 2, '1 1 1', 1, DRAWFLAG_NORMAL);
+       drawstring(pos, map, hud_fontsize * 2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
        pos.y += hud_fontsize.y * 2;
 
        if( mapvote_chosenmap != "" )
        {
                pos.y += hud_fontsize.y * 0.25;
                pos.x = center - stringwidth(mapvote_chosenmap, false, hud_fontsize * 1.5) * 0.5;
-               drawstring(pos, mapvote_chosenmap, hud_fontsize * 1.5, '1 1 1', 1, DRAWFLAG_NORMAL);
+               drawstring(pos, mapvote_chosenmap, hud_fontsize * 1.5, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
                pos.y += hud_fontsize.y * 1.5;
        }
        pos.y += hud_fontsize.y * 0.5;
@@ -381,12 +381,10 @@ void MapVote_Draw()
        i = ceil(max(0, mv_timeout - time));
        map = sprintf(_("%d seconds left"), i);
        pos.x = center - stringwidth(map, false, hud_fontsize * 1.5) * 0.5;
-       drawstring(pos, map, hud_fontsize * 1.5, '0 1 0', 1, DRAWFLAG_NORMAL);
+       drawstring(pos, map, hud_fontsize * 1.5, '0 1 0', panel_fg_alpha, DRAWFLAG_NORMAL);
        pos.y += hud_fontsize.y * 1.5;
        pos.y += hud_fontsize.y * 0.5;
 
-       HUD_Panel_UpdateCvars();
-
        // base for multi-column stuff...
        pos.y += hud_fontsize.y;
        pos.x = xmin;
@@ -440,7 +438,7 @@ void MapVote_Draw()
        panel_pos.y = pos.y;
        panel_size.x = xmax - xmin;
        panel_size.y = ymax - ymin;
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if(panel_bg_padding)
        {
@@ -486,7 +484,7 @@ void MapVote_Draw()
                MapVote_DrawAbstain(pos, dist.x, xmax - xmin, tmp, i);
        }
 
-       draw_cursor_normal(mv_mousepos, '1 1 1', 1 - autocvar__menu_alpha);
+       draw_cursor_normal(mousepos, '1 1 1', panel_fg_alpha);
 }
 
 void Cmd_MapVote_MapDownload(int argc)
@@ -610,12 +608,7 @@ void GameTypeVote_ReadOption(int i)
        mv_maps[i] = gt;
        mv_flags[i] = ReadByte();
 
-       string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, gt);
-       if(precache_pic(mv_picpath) == "")
-               mv_picpath = strcat("gfx/menu/default/gametype_", gt);
-       string pic = strzone(mv_picpath);
-       mv_pics[i] = pic;
-       mv_preview[i] = PreviewExists(pic);
+       string basetype = "";
 
        if ( mv_flags[i] & GTV_CUSTOM )
        {
@@ -624,20 +617,38 @@ void GameTypeVote_ReadOption(int i)
                        name = gt;
                mv_pk3[i] = strzone(name);
                mv_desc[i] = strzone(ReadString());
+               basetype = strzone(ReadString());
        }
        else
        {
-               int type = MapInfo_Type_FromString(gt);
+               Gametype type = MapInfo_Type_FromString(gt);
                mv_pk3[i] = strzone(MapInfo_Type_ToText(type));
                mv_desc[i] = MapInfo_Type_Description(type);
        }
+
+       string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, gt);
+       if(precache_pic(mv_picpath) == "")
+       {
+               mv_picpath = strcat("gfx/menu/default/gametype_", gt);
+               if(precache_pic(mv_picpath) == "")
+               {
+                       mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, basetype);
+                       if(precache_pic(mv_picpath) == "")
+                       {
+                               mv_picpath = strcat("gfx/menu/default/gametype_", basetype);
+                       }
+               }
+       }
+       string pic = strzone(mv_picpath);
+       mv_pics[i] = pic;
+       mv_preview[i] = PreviewExists(pic);
 }
 
 void MapVote_Init()
 {
        mv_active = 1;
-       if(autocvar_hud_cursormode) { setcursormode(1); }
-       else { mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; }
+       if(autocvar_hud_cursormode) setcursormode(1);
+       else mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
        mv_selection = -1;
        mv_selection_keyboard = 0;
 
@@ -773,8 +784,8 @@ float MapVote_InputEvent(int bInputType, float nPrimary, float nSecondary)
 
        if(bInputType == 3)
        {
-               mv_mousepos.x = nPrimary;
-               mv_mousepos.y = nSecondary;
+               mousepos.x = nPrimary;
+               mousepos.y = nSecondary;
                mv_selection_keyboard = 0;
                return true;
        }
index ebc107a0b2584196023b052fdb82c02470ed16f5..2f95102a990bf84571b8010cff9b55c674991dca 100644 (file)
@@ -10,3 +10,4 @@ float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary);
 void Net_MapVote_Picture();
 
 float mv_active;
+float xmin, xmax, ymin, ymax;
index d3b3e46d73526d230e87fe3da69a82f366833f18..a0d7b8789c2273ad34ed1cbe9750a2eb795e205c 100644 (file)
@@ -1,8 +1,8 @@
 #include "miscfunctions.qh"
 
-#include "hud/all.qh"
+#include "hud/_mod.qh"
 
-#include <common/command/generic.qh>
+#include <common/command/_mod.qh>
 
 #include <common/teams.qh>
 
@@ -68,11 +68,11 @@ void RemovePlayer(entity player)
 void MoveToLast(entity e)
 {
        AuditLists();
-       other = e.sort_next;
-       while(other)
+       entity ent = e.sort_next;
+       while(ent)
        {
-               SORT_SWAP(other, e);
-               other = e.sort_next;
+               SORT_SWAP(ent, e);
+               ent = e.sort_next;
        }
        AuditLists();
 }
@@ -350,6 +350,13 @@ void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, f
        drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz.y, theAlpha, drawflag, fadelerp);
 }
 
+void update_mousepos()
+{
+       mousepos += getmousepos() * autocvar_menu_mouse_speed;
+       mousepos.x = bound(0, mousepos.x, vid_conwidth);
+       mousepos.y = bound(0, mousepos.y, vid_conheight);
+}
+
 // this draws the triangles of a model DIRECTLY. Don't expect high performance, really...
 float PolyDrawModelSurface(entity e, float i_s)
 {
@@ -382,15 +389,15 @@ void PolyDrawModel(entity e)
 
 void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag)
 {
-       float x, y, q, d;
+       float d;
        vector ringsize, v, t;
        ringsize = radi * '1 1 0';
 
-       x = cos(f * 2 * M_PI);
-       y = sin(f * 2 * M_PI);
-       q = fabs(x) + fabs(y);
-       x /= q;
-       y /= q;
+       float co = cos(f * 2 * M_PI);
+       float si = sin(f * 2 * M_PI);
+       float q = fabs(co) + fabs(si);
+       co /= q;
+       si /= q;
 
        if(f >= 1)
        {
@@ -523,8 +530,8 @@ void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector
        if(d > 0)
        {
                        v = centre;                     t = '0.5 0.5 0';
-                       v.x += x * 0.5 * ringsize.x;    t += x * '0.5 0.5 0';
-                       v.y += y * 0.5 * ringsize.y;    t += y * '0.5 -0.5 0';
+                       v.x += co * 0.5 * ringsize.x;   t += co * '0.5 0.5 0';
+                       v.y += si * 0.5 * ringsize.y;   t += si * '0.5 -0.5 0';
                        R_PolygonVertex(v, t, rgb, a);
                R_EndPolygon();
        }
index 1f8790481f1a8d88a0193d9971d8b65b2dcbad4b..62de4565e53bff2549a9b361f063ab0791c7004b 100644 (file)
@@ -31,7 +31,7 @@ vector HUD_GetFontsize(string cvarname);
 
 float PreviewExists(string name);
 
-vector rotate(vector v, float a);
+vector Rotate(vector v, float a);
 
 
 #define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : ((s).health <= 0))
@@ -187,6 +187,8 @@ void drawcolorcodedstring_expanding(vector position, string text, vector theScal
 
 void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, float theAlpha, float drawflag, float fadelerp);
 
+void update_mousepos();
+
 // this draws the triangles of a model DIRECTLY. Don't expect high performance, really...
 float PolyDrawModelSurface(entity e, float i_s);
 void PolyDrawModel(entity e);
index 98fb4815c1ce28cc699a429537ea75c2643b4487..3dfd4f78974b38371a435762abe7eaa35f611c85 100644 (file)
@@ -1 +1,2 @@
 // generated file; do not modify
+#include <client/mutators/events.qc>
index 98fb4815c1ce28cc699a429537ea75c2643b4487..b54ee489cf0909a46d34a92a313124cd27d80619 100644 (file)
@@ -1 +1,2 @@
 // generated file; do not modify
+#include <client/mutators/events.qh>
diff --git a/qcsrc/client/mutators/events.qc b/qcsrc/client/mutators/events.qc
new file mode 100644 (file)
index 0000000..c2dbb70
--- /dev/null
@@ -0,0 +1 @@
+#include "events.qh"
index 9a2e8b1373be23d9d79db6e53fe9326384beb8b3..edd178199d5c17b0d3eafb191748afc890cb600f 100644 (file)
@@ -88,6 +88,15 @@ MUTATOR_HOOKABLE(GetModelParams, EV_GetModelParams);
        /**/
 MUTATOR_HOOKABLE(WantEventchase, EV_WantEventchase);
 
+/** allow customizing 3rd person mode effect */
+#define EV_CustomizeEventchase(i, o) \
+       /** entity id */ i(entity, MUTATOR_ARGV_0_entity) \
+       /* current_view_origin_override */ o(vector, MUTATOR_ARGV_0_vector) \
+       /* view_offset_override */ o(vector, MUTATOR_ARGV_1_vector) \
+       /* chase_distance_override */ o(float, MUTATOR_ARGV_0_float) \
+       /**/
+MUTATOR_HOOKABLE(CustomizeEventchase, EV_CustomizeEventchase);
+
 #define EV_AnnouncerOption(i, o) \
        /** announcer string */  i(string, MUTATOR_ARGV_0_string) \
        /** announcer string */ o(string, MUTATOR_ARGV_0_string) \
@@ -124,3 +133,34 @@ MUTATOR_HOOKABLE(Weapon_ImpactEffect, EV_Weapon_ImpactEffect);
        /** argc (also, argv() can be used) */ i(int, MUTATOR_ARGV_0_int) \
        /**/
 MUTATOR_HOOKABLE(HUD_Command, EV_HUD_Command);
+
+/** Draw the grapple hook, allows changing hook texture and colour */
+#define EV_DrawGrapplingHook(i, o) \
+       /** hook */                     i(entity, MUTATOR_ARGV_0_entity) \
+       /** texture */                  i(string, MUTATOR_ARGV_1_string) \
+       /***/                                   o(string, MUTATOR_ARGV_1_string) \
+       /** colour */                   i(vector, MUTATOR_ARGV_2_vector) \
+       /***/                                   o(vector, MUTATOR_ARGV_2_vector) \
+       /** team */                     i(float, MUTATOR_ARGV_3_float) \
+       /**/
+MUTATOR_HOOKABLE(DrawGrapplingHook, EV_DrawGrapplingHook);
+
+/** Called when an entity is updated (either by SVQC networking or PVS) */
+#define EV_Ent_Update(i, o) \
+       /** entity id */                i(entity, MUTATOR_ARGV_0_entity) \
+       /** is new to client */ i(bool, MUTATOR_ARGV_1_bool) \
+       /**/
+MUTATOR_HOOKABLE(Ent_Update, EV_Ent_Update);
+
+/** Return true to not draw crosshair */
+MUTATOR_HOOKABLE(DrawCrosshair, EV_NO_ARGS);
+
+/** Return true to not draw scoreboard */
+MUTATOR_HOOKABLE(DrawScoreboard, EV_NO_ARGS);
+
+/** Called when drawing info messages, allows adding new info messages */
+#define EV_DrawInfoMessages(i, o) \
+       /** pos */                          i(vector, MUTATOR_ARGV_0_vector) \
+       /** mySize */                   i(vector, MUTATOR_ARGV_1_vector) \
+       /**/
+MUTATOR_HOOKABLE(DrawInfoMessages, EV_DrawInfoMessages);
index c8abdc6f57b6cf7d62f20ae5c6288aae3397549f..66827c8f30e3bdc2bfeb9508ce64a5a69bf86bea 100644 (file)
@@ -1,10 +1,12 @@
 #include "player_skeleton.qh"
 
+#include <common/physics/movetypes/movetypes.qh>
 #include "mutators/events.qh"
 #include "../lib/csqcmodel/cl_player.qh"
 #include "../lib/warpzone/anglestransform.qh"
 
 .vector v_angle;
+.float v_angle_save_x;
 
 class(Skeleton) .float skeleton_info_modelindex;
 class(Skeleton) .float skeleton_info_skin;
@@ -50,7 +52,7 @@ void skeleton_loadinfo(entity e)
                }
        }
        else
-               LOG_TRACE("No model parameters for ", e.model, "\n");
+               LOG_TRACE("No model parameters for ", e.model);
        //dprint(e.model, " uses ", ftos(e.bone_upperbody), " ", ftos(e.fixbone), "\n");
        get_model_parameters(string_null, 0);
        e.skeleton_info_modelindex = e.modelindex;
@@ -179,13 +181,22 @@ void skeleton_from_frames(entity e, bool is_dead)
        if(!is_dead)
        {
                if(e == csqcplayer)
-                       e.v_angle_x = input_angles_x;
+               {
+                       if(e.move_movetype == MOVETYPE_NONE)
+                       {
+                               if(!e.v_angle_save_x)
+                                       e.v_angle_save_x = input_angles.x;
+                               e.v_angle_x = e.v_angle_save_x;
+                       }
+                       else
+                               e.v_angle_x = input_angles.x;
+               }
                int i;
                for(i = 0; i < MAX_AIM_BONES; ++i)
                {
                        if(e.(bone_aim[i]))
                        {
-                               vector aim = '1 0 0' * bound(-90, e.v_angle.x, 90) * e.(bone_aimweight[i]);
+                               vector aim = '1 0 0' * bound(-90, e.v_angle_x, 90) * e.(bone_aimweight[i]);
                                vector org = skel_get_boneabs(s, e.(bone_aim[i]));
                                vector ang_cur = fixedvectoangles2(v_forward, v_up);
                                vector ang = AnglesTransform_Multiply(aim, ang_cur);
index a736d32622cb6b8f470f2334e7bea06c504eb1a0..738831a5d020daf1d093806332ea290c6b8bce79 100644 (file)
@@ -1,25 +1,11 @@
 #include <lib/_all.inc>
-#include "_all.qh"
 
-#include "../client/_mod.inc"
-#include "commands/_mod.inc"
-#include "hud/_mod.inc"
-#include "mutators/_mod.inc"
-#include "weapons/_mod.inc"
+#if XONOTIC
+#include <client/_all.inc>
 
-#include <common/_all.inc>
-#include <common/effects/qc/all.qc>
-
-#include <lib/csqcmodel/cl_model.qc>
-#include <lib/csqcmodel/cl_player.qc>
-#include <lib/csqcmodel/interpolate.qc>
-
-#include <lib/warpzone/anglestransform.qc>
-#include <lib/warpzone/common.qc>
-#include <lib/warpzone/client.qc>
-#include <lib/warpzone/server.qc>
-#include <lib/warpzone/util_server.qc>
+#include <ecs/_mod.inc>
+#endif
 
-#if BUILD_MOD
-#include "../../mod/client/progs.inc"
+#ifdef BUILD_MOD
+#include <mod/client/progs.inc>
 #endif
diff --git a/qcsrc/client/scoreboard.qc b/qcsrc/client/scoreboard.qc
deleted file mode 100644 (file)
index 2fb239b..0000000
+++ /dev/null
@@ -1,1515 +0,0 @@
-#include "scoreboard.qh"
-
-#include "hud/panel/quickmenu.qh"
-#include "hud/all.qh"
-
-#include <common/ent_cs.qh>
-#include <common/constants.qh>
-#include <common/mapinfo.qh>
-#include <common/minigames/cl_minigames.qh>
-#include <common/stats.qh>
-#include <common/teams.qh>
-
-float scoreboard_alpha_bg;
-float scoreboard_alpha_fg;
-float scoreboard_highlight;
-float scoreboard_highlight_alpha;
-float scoreboard_highlight_alpha_self;
-float scoreboard_alpha_name;
-float scoreboard_alpha_name_self;
-
-void drawstringright(vector, string, vector, vector, float, float);
-void drawstringcenter(vector, string, vector, vector, float, float);
-
-const float SCOREBOARD_OFFSET = 50;
-
-// wrapper to put all possible scores titles through gettext
-string TranslateScoresLabel(string l)
-{
-       switch(l)
-       {
-               case "bckills": return CTX(_("SCO^bckills"));
-               case "bctime": return CTX(_("SCO^bctime"));
-               case "caps": return CTX(_("SCO^caps"));
-               case "captime": return CTX(_("SCO^captime"));
-               case "deaths": return CTX(_("SCO^deaths"));
-               case "destroyed": return CTX(_("SCO^destroyed"));
-               case "dmg": return CTX(_("SCO^dmg"));
-               case "dmgtaken": return CTX(_("SCO^dmgtaken"));
-               case "drops": return CTX(_("SCO^drops"));
-               case "faults": return CTX(_("SCO^faults"));
-               case "fckills": return CTX(_("SCO^fckills"));
-               case "goals": return CTX(_("SCO^goals"));
-               case "kckills": return CTX(_("SCO^kckills"));
-               case "kdratio": return CTX(_("SCO^kdratio"));
-               case "k/d": return CTX(_("SCO^k/d"));
-               case "kd": return CTX(_("SCO^kd"));
-               case "kdr": return CTX(_("SCO^kdr"));
-               case "kills": return CTX(_("SCO^kills"));
-               case "laps": return CTX(_("SCO^laps"));
-               case "lives": return CTX(_("SCO^lives"));
-               case "losses": return CTX(_("SCO^losses"));
-               case "name": return CTX(_("SCO^name"));
-               case "sum": return CTX(_("SCO^sum"));
-               case "nick": return CTX(_("SCO^nick"));
-               case "objectives": return CTX(_("SCO^objectives"));
-               case "pickups": return CTX(_("SCO^pickups"));
-               case "ping": return CTX(_("SCO^ping"));
-               case "pl": return CTX(_("SCO^pl"));
-               case "pushes": return CTX(_("SCO^pushes"));
-               case "rank": return CTX(_("SCO^rank"));
-               case "returns": return CTX(_("SCO^returns"));
-               case "revivals": return CTX(_("SCO^revivals"));
-               case "score": return CTX(_("SCO^score"));
-               case "suicides": return CTX(_("SCO^suicides"));
-               case "takes": return CTX(_("SCO^takes"));
-               case "ticks": return CTX(_("SCO^ticks"));
-               default: return l;
-       }
-}
-
-void HUD_InitScores()
-{
-       int i, f;
-
-       ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
-       for(i = 0; i < MAX_SCORE; ++i)
-       {
-               f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
-               if(f == SFL_SORT_PRIO_PRIMARY)
-                       ps_primary = i;
-               if(f == SFL_SORT_PRIO_SECONDARY)
-                       ps_secondary = i;
-       }
-       if(ps_secondary == -1)
-               ps_secondary = ps_primary;
-
-       for(i = 0; i < MAX_TEAMSCORE; ++i)
-       {
-               f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
-               if(f == SFL_SORT_PRIO_PRIMARY)
-                       ts_primary = i;
-               if(f == SFL_SORT_PRIO_SECONDARY)
-                       ts_secondary = i;
-       }
-       if(ts_secondary == -1)
-               ts_secondary = ts_primary;
-
-       Cmd_HUD_SetFields(0);
-}
-
-float SetTeam(entity pl, float Team);
-//float lastpnum;
-void HUD_UpdatePlayerTeams()
-{
-       float Team;
-       entity pl, tmp;
-       float num;
-
-       num = 0;
-       for(pl = players.sort_next; pl; pl = pl.sort_next)
-       {
-               num += 1;
-               Team = entcs_GetScoreTeam(pl.sv_entnum);
-               if(SetTeam(pl, Team))
-               {
-                       tmp = pl.sort_prev;
-                       HUD_UpdatePlayerPos(pl);
-                       if(tmp)
-                               pl = tmp;
-                       else
-                               pl = players.sort_next;
-               }
-       }
-       /*
-       if(num != lastpnum)
-               print(strcat("PNUM: ", ftos(num), "\n"));
-       lastpnum = num;
-       */
-}
-
-int HUD_CompareScore(int vl, int vr, int f)
-{
-    TC(int, vl); TC(int, vr); TC(int, f);
-       if(f & SFL_ZERO_IS_WORST)
-       {
-               if(vl == 0 && vr != 0)
-                       return 1;
-               if(vl != 0 && vr == 0)
-                       return 0;
-       }
-       if(vl > vr)
-               return IS_INCREASING(f);
-       if(vl < vr)
-               return IS_DECREASING(f);
-       return -1;
-}
-
-float HUD_ComparePlayerScores(entity left, entity right)
-{
-       float vl, vr, r;
-       vl = entcs_GetTeam(left.sv_entnum);
-       vr = entcs_GetTeam(right.sv_entnum);
-
-       if(!left.gotscores)
-               vl = NUM_SPECTATOR;
-       if(!right.gotscores)
-               vr = NUM_SPECTATOR;
-
-       if(vl > vr)
-               return true;
-       if(vl < vr)
-               return false;
-
-       if(vl == NUM_SPECTATOR)
-       {
-               // FIRST the one with scores (spectators), THEN the ones without (downloaders)
-               // no other sorting
-               if(!left.gotscores && right.gotscores)
-                       return true;
-               return false;
-       }
-
-       r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
-       if (r >= 0)
-               return r;
-
-       r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
-       if (r >= 0)
-               return r;
-
-       int i;
-       for(i = 0; i < MAX_SCORE; ++i)
-       {
-               r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
-               if (r >= 0)
-                       return r;
-       }
-
-       if (left.sv_entnum < right.sv_entnum)
-               return true;
-
-       return false;
-}
-
-void HUD_UpdatePlayerPos(entity player)
-{
-       for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
-       {
-               SORT_SWAP(player, other);
-       }
-       for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
-       {
-               SORT_SWAP(other, player);
-       }
-}
-
-float HUD_CompareTeamScores(entity left, entity right)
-{
-       int i, r;
-
-       if(left.team == NUM_SPECTATOR)
-               return 1;
-       if(right.team == NUM_SPECTATOR)
-               return 0;
-
-       r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
-       if (r >= 0)
-               return r;
-
-       r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
-       if (r >= 0)
-               return r;
-
-       for(i = 0; i < MAX_SCORE; ++i)
-       {
-               r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
-               if (r >= 0)
-                       return r;
-       }
-
-       if (left.team < right.team)
-               return true;
-
-       return false;
-}
-
-void HUD_UpdateTeamPos(entity Team)
-{
-       for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
-       {
-               SORT_SWAP(Team, other);
-       }
-       for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
-       {
-               SORT_SWAP(other, Team);
-       }
-}
-
-void Cmd_HUD_Help()
-{
-       LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
-       LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
-       LOG_INFO(_("Usage:\n"));
-       LOG_INFO(_("^2scoreboard_columns_set default\n"));
-       LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
-       LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
-       LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
-
-       LOG_INFO(_("^3name^7 or ^3nick^7             Name of a player\n"));
-       LOG_INFO(_("^3ping^7                     Ping time\n"));
-       LOG_INFO(_("^3pl^7                       Packet loss\n"));
-       LOG_INFO(_("^3kills^7                    Number of kills\n"));
-       LOG_INFO(_("^3deaths^7                   Number of deaths\n"));
-       LOG_INFO(_("^3suicides^7                 Number of suicides\n"));
-       LOG_INFO(_("^3frags^7                    kills - suicides\n"));
-       LOG_INFO(_("^3kd^7                       The kill-death ratio\n"));
-       LOG_INFO(_("^3dmg^7                      The total damage done\n"));
-       LOG_INFO(_("^3dmgtaken^7                 The total damage taken\n"));
-       LOG_INFO(_("^3sum^7                      frags - deaths\n"));
-       LOG_INFO(_("^3caps^7                     How often a flag (CTF) or a key (KeyHunt) was captured\n"));
-       LOG_INFO(_("^3pickups^7                  How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
-       LOG_INFO(_("^3captime^7                  Time of fastest cap (CTF)\n"));
-       LOG_INFO(_("^3fckills^7                  Number of flag carrier kills\n"));
-       LOG_INFO(_("^3returns^7                  Number of flag returns\n"));
-       LOG_INFO(_("^3drops^7                    Number of flag drops\n"));
-       LOG_INFO(_("^3lives^7                    Number of lives (LMS)\n"));
-       LOG_INFO(_("^3rank^7                     Player rank\n"));
-       LOG_INFO(_("^3pushes^7                   Number of players pushed into void\n"));
-       LOG_INFO(_("^3destroyed^7                Number of keys destroyed by pushing them into void\n"));
-       LOG_INFO(_("^3kckills^7                  Number of keys carrier kills\n"));
-       LOG_INFO(_("^3losses^7                   Number of times a key was lost\n"));
-       LOG_INFO(_("^3laps^7                     Number of laps finished (race/cts)\n"));
-       LOG_INFO(_("^3time^7                     Total time raced (race/cts)\n"));
-       LOG_INFO(_("^3fastest^7                  Time of fastest lap (race/cts)\n"));
-       LOG_INFO(_("^3ticks^7                    Number of ticks (DOM)\n"));
-       LOG_INFO(_("^3takes^7                    Number of domination points taken (DOM)\n"));
-       LOG_INFO(_("^3bckills^7                  Number of ball carrier kills\n"));
-       LOG_INFO(_("^3bctime^7                   Total amount of time holding the ball in Keepaway\n"));
-       LOG_INFO(_("^3score^7                    Total score\n\n"));
-
-       LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
-               "of game types, then a slash, to make the field show up only in these\n"
-               "or in all but these game types. You can also specify 'all' as a\n"
-               "field to show all fields available for the current game mode.\n\n"));
-
-       LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
-               "include/exclude ALL teams/noteams game modes.\n\n"));
-
-       LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
-       LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
-               "right of the vertical bar aligned to the right.\n"));
-       LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
-               "other gamemodes except DM.\n"));
-}
-
-// NOTE: adding a gametype with ? to not warn for an optional field
-// make sure it's excluded in a previous exclusive rule, if any
-// otherwise the previous exclusive rule warns anyway
-// e.g. -teams,rc,cts,lms/kills ?+rc/kills
-#define SCOREBOARD_DEFAULT_COLUMNS \
-"ping pl name |" \
-" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
-" -teams,lms/deaths +ft,tdm/deaths" \
-" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
-" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
-" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
-" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
-" +lms/lives +lms/rank" \
-" +kh/caps +kh/pushes +kh/destroyed" \
-" ?+rc/laps ?+rc/time +rc,cts/fastest" \
-" +as/objectives +nb/faults +nb/goals" \
-" +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
-" -lms,rc,cts,inv,nb/score"
-
-void Cmd_HUD_SetFields(int argc)
-{
-    TC(int, argc);
-       int i, j, slash;
-       string str, pattern;
-       float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
-       float missing;
-
-       if(!gametype)
-       {
-               // set up a temporary scoreboard layout
-               // no layout can be properly set up until score_info data haven't been received
-               argc = tokenizebyseparator("0 1 ping pl name | score", " ");
-               ps_primary = 0;
-               scores_label[ps_primary] = strzone("score");
-               scores_flags[ps_primary] = SFL_ALLOW_HIDE;
-       }
-
-       // TODO: re enable with gametype dependant cvars?
-       if(argc < 3) // no arguments provided
-               argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
-
-       if(argc < 3)
-               argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
-
-       if(argc == 3)
-       {
-               if(argv(2) == "default")
-                       argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
-               else if(argv(2) == "all")
-               {
-                       string s;
-                       s = "ping pl name |";
-                       for(i = 0; i < MAX_SCORE; ++i)
-                       {
-                               if(i != ps_primary)
-                               if(i != ps_secondary)
-                               if(scores_label[i] != "")
-                                       s = strcat(s, " ", scores_label[i]);
-                       }
-                       if(ps_secondary != ps_primary)
-                               s = strcat(s, " ", scores_label[ps_secondary]);
-                       s = strcat(s, " ", scores_label[ps_primary]);
-                       argc = tokenizebyseparator(strcat("0 1 ", s), " ");
-               }
-       }
-
-
-       hud_num_fields = 0;
-
-       hud_fontsize = HUD_GetFontsize("hud_fontsize");
-
-       for(i = 1; i < argc - 1; ++i)
-       {
-               float nocomplain;
-               str = argv(i+1);
-
-               nocomplain = false;
-               if(substring(str, 0, 1) == "?")
-               {
-                       nocomplain = true;
-                       str = substring(str, 1, strlen(str) - 1);
-               }
-
-               slash = strstrofs(str, "/", 0);
-               if(slash >= 0)
-               {
-                       pattern = substring(str, 0, slash);
-                       str = substring(str, slash + 1, strlen(str) - (slash + 1));
-
-                       if (!isGametypeInFilter(gametype, teamplay, false, pattern))
-                               continue;
-               }
-
-               strunzone(hud_title[hud_num_fields]);
-               hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
-               hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
-               str = strtolower(str);
-
-               switch(str)
-               {
-                       case "ping": hud_field[hud_num_fields] = SP_PING; break;
-                       case "pl": hud_field[hud_num_fields] = SP_PL; break;
-                       case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
-                       case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
-                       case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
-                       case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
-                       case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
-                       case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
-                       default:
-                       {
-                               for(j = 0; j < MAX_SCORE; ++j)
-                                       if(str == strtolower(scores_label[j]))
-                                               goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
-
-LABEL(notfound)
-                               if(str == "frags")
-                                       j = SP_FRAGS;
-                               else
-                               {
-                                       if(!nocomplain)
-                                               LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
-                                       continue;
-                               }
-LABEL(found)
-                               hud_field[hud_num_fields] = j;
-                               if(j == ps_primary)
-                                       have_primary = 1;
-                               if(j == ps_secondary)
-                                       have_secondary = 1;
-
-                       }
-               }
-               ++hud_num_fields;
-               if(hud_num_fields >= MAX_HUD_FIELDS)
-                       break;
-       }
-
-       if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
-               have_primary = 1;
-       if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
-               have_secondary = 1;
-       if(ps_primary == ps_secondary)
-               have_secondary = 1;
-       missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
-
-       if(hud_num_fields+missing < MAX_HUD_FIELDS)
-       {
-               if(!have_name)
-               {
-                       strunzone(hud_title[hud_num_fields]);
-                       for(i = hud_num_fields; i > 0; --i)
-                       {
-                               hud_title[i] = hud_title[i-1];
-                               hud_size[i] = hud_size[i-1];
-                               hud_field[i] = hud_field[i-1];
-                       }
-                       hud_title[0] = strzone(TranslateScoresLabel("name"));
-                       hud_field[0] = SP_NAME;
-                       ++hud_num_fields;
-                       LOG_INFO("fixed missing field 'name'\n");
-
-                       if(!have_separator)
-                       {
-                               strunzone(hud_title[hud_num_fields]);
-                               for(i = hud_num_fields; i > 1; --i)
-                               {
-                                       hud_title[i] = hud_title[i-1];
-                                       hud_size[i] = hud_size[i-1];
-                                       hud_field[i] = hud_field[i-1];
-                               }
-                               hud_title[1] = strzone("|");
-                               hud_field[1] = SP_SEPARATOR;
-                               hud_size[1] = stringwidth("|", false, hud_fontsize);
-                               ++hud_num_fields;
-                               LOG_INFO("fixed missing field '|'\n");
-                       }
-               }
-               else if(!have_separator)
-               {
-                       strunzone(hud_title[hud_num_fields]);
-                       hud_title[hud_num_fields] = strzone("|");
-                       hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
-                       hud_field[hud_num_fields] = SP_SEPARATOR;
-                       ++hud_num_fields;
-                       LOG_INFO("fixed missing field '|'\n");
-               }
-               if(!have_secondary)
-               {
-                       strunzone(hud_title[hud_num_fields]);
-                       hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
-                       hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
-                       hud_field[hud_num_fields] = ps_secondary;
-                       ++hud_num_fields;
-                       LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
-               }
-               if(!have_primary)
-               {
-                       strunzone(hud_title[hud_num_fields]);
-                       hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
-                       hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
-                       hud_field[hud_num_fields] = ps_primary;
-                       ++hud_num_fields;
-                       LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
-               }
-       }
-
-       hud_field[hud_num_fields] = SP_END;
-}
-
-// MOVEUP::
-vector hud_field_rgb;
-string hud_field_icon0;
-string hud_field_icon1;
-string hud_field_icon2;
-vector hud_field_icon0_rgb;
-vector hud_field_icon1_rgb;
-vector hud_field_icon2_rgb;
-float hud_field_icon0_alpha;
-float hud_field_icon1_alpha;
-float hud_field_icon2_alpha;
-string HUD_GetField(entity pl, int field)
-{
-    TC(int, field);
-       float tmp, num, denom;
-       int f;
-       string str;
-       hud_field_rgb = '1 1 1';
-       hud_field_icon0 = "";
-       hud_field_icon1 = "";
-       hud_field_icon2 = "";
-       hud_field_icon0_rgb = '1 1 1';
-       hud_field_icon1_rgb = '1 1 1';
-       hud_field_icon2_rgb = '1 1 1';
-       hud_field_icon0_alpha = 1;
-       hud_field_icon1_alpha = 1;
-       hud_field_icon2_alpha = 1;
-       switch(field)
-       {
-               case SP_PING:
-                       if (!pl.gotscores)
-                               return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
-                       //str = getplayerkeyvalue(pl.sv_entnum, "ping");
-                       f = pl.ping;
-                       if(f == 0)
-                               return _("N/A");
-                       tmp = max(0, min(220, f-80)) / 220;
-                       hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
-                       return ftos(f);
-
-               case SP_PL:
-                       if (!pl.gotscores)
-                               return _("N/A");
-                       f = pl.ping_packetloss;
-                       tmp = pl.ping_movementloss;
-                       if(f == 0 && tmp == 0)
-                               return "";
-                       str = ftos(ceil(f * 100));
-                       if(tmp != 0)
-                               str = strcat(str, "~", ftos(ceil(tmp * 100)));
-                       tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
-                       hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
-                       return str;
-
-               case SP_NAME:
-                       if(ready_waiting && pl.ready)
-                       {
-                               hud_field_icon0 = "gfx/scoreboard/player_ready";
-                       }
-                       else if(!teamplay)
-                       {
-                               f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
-                               {
-                                       hud_field_icon0 = "gfx/scoreboard/playercolor_base";
-                                       hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
-                                       hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
-                                       hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
-                                       hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
-                               }
-                       }
-                       return entcs_GetName(pl.sv_entnum);
-
-               case SP_FRAGS:
-                       f = pl.(scores[SP_KILLS]);
-                       f -= pl.(scores[SP_SUICIDES]);
-                       return ftos(f);
-
-               case SP_KDRATIO:
-                       num = pl.(scores[SP_KILLS]);
-                       denom = pl.(scores[SP_DEATHS]);
-
-                       if(denom == 0) {
-                               hud_field_rgb = '0 1 0';
-                               str = sprintf("%d", num);
-                       } else if(num <= 0) {
-                               hud_field_rgb = '1 0 0';
-                               str = sprintf("%.1f", num/denom);
-                       } else
-                               str = sprintf("%.1f", num/denom);
-                       return str;
-
-               case SP_SUM:
-                       f = pl.(scores[SP_KILLS]);
-                       f -= pl.(scores[SP_DEATHS]);
-
-                       if(f > 0) {
-                               hud_field_rgb = '0 1 0';
-                       } else if(f == 0) {
-                               hud_field_rgb = '1 1 1';
-                       } else {
-                               hud_field_rgb = '1 0 0';
-                       }
-                       return ftos(f);
-
-               case SP_DMG:
-                       num = pl.(scores[SP_DMG]);
-                       denom = 1000;
-
-                       str = sprintf("%.1f k", num/denom);
-                       return str;
-
-               case SP_DMGTAKEN:
-                       num = pl.(scores[SP_DMGTAKEN]);
-                       denom = 1000;
-
-                       str = sprintf("%.1f k", num/denom);
-                       return str;
-
-               default:
-                       tmp = pl.(scores[field]);
-                       f = scores_flags[field];
-                       if(field == ps_primary)
-                               hud_field_rgb = '1 1 0';
-                       else if(field == ps_secondary)
-                               hud_field_rgb = '0 1 1';
-                       else
-                               hud_field_rgb = '1 1 1';
-                       return ScoreString(f, tmp);
-       }
-       //return "error";
-}
-
-float hud_fixscoreboardcolumnwidth_len;
-float hud_fixscoreboardcolumnwidth_iconlen;
-float hud_fixscoreboardcolumnwidth_marginlen;
-
-string HUD_FixScoreboardColumnWidth(int i, string str)
-{
-    TC(int, i);
-       float field, f;
-       vector sz;
-       field = hud_field[i];
-
-       hud_fixscoreboardcolumnwidth_iconlen = 0;
-
-       if(hud_field_icon0 != "")
-       {
-               sz = draw_getimagesize(hud_field_icon0);
-               f = sz.x / sz.y;
-               if(hud_fixscoreboardcolumnwidth_iconlen < f)
-                       hud_fixscoreboardcolumnwidth_iconlen = f;
-       }
-
-       if(hud_field_icon1 != "")
-       {
-               sz = draw_getimagesize(hud_field_icon1);
-               f = sz.x / sz.y;
-               if(hud_fixscoreboardcolumnwidth_iconlen < f)
-                       hud_fixscoreboardcolumnwidth_iconlen = f;
-       }
-
-       if(hud_field_icon2 != "")
-       {
-               sz = draw_getimagesize(hud_field_icon2);
-               f = sz.x / sz.y;
-               if(hud_fixscoreboardcolumnwidth_iconlen < f)
-                       hud_fixscoreboardcolumnwidth_iconlen = f;
-       }
-
-       hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
-
-       if(hud_fixscoreboardcolumnwidth_iconlen != 0)
-               hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
-       else
-               hud_fixscoreboardcolumnwidth_marginlen = 0;
-
-       if(field == SP_NAME) // name gets all remaining space
-       {
-               int j;
-               float namesize;
-               namesize = sbwidth;// / hud_fontsize_x;
-               for(j = 0; j < hud_num_fields; ++j)
-                       if(j != i)
-                               if (hud_field[i] != SP_SEPARATOR)
-                                       namesize -= hud_size[j] + hud_fontsize.x;
-               namesize += hud_fontsize.x;
-               hud_size[i] = namesize;
-
-               if (hud_fixscoreboardcolumnwidth_iconlen != 0)
-                       namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
-               str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
-               hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
-       }
-       else
-               hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
-
-       f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
-       if(hud_size[i] < f)
-               hud_size[i] = f;
-
-       return str;
-}
-
-void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
-{
-    TC(bool, is_self); TC(int, pl_number);
-       vector tmp, rgb;
-       rgb = Team_ColorRGB(pl.team);
-       string str;
-       int field;
-       float is_spec;
-       is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
-
-       if((rgb == '1 1 1') && (!is_spec)) {
-               rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
-               rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
-               rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
-
-       vector h_pos = pos - '1 1 0';
-       vector h_size = item_size + '2 0 0';
-       // alternated rows highlighting
-       if(is_self)
-               drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
-       else if((scoreboard_highlight) && (!(pl_number % 2)))
-               drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
-
-       tmp.x = item_size.x;
-       tmp.y = 0;
-       tmp.z = 0;
-       int i;
-       for(i = 0; i < hud_num_fields; ++i)
-       {
-               field = hud_field[i];
-               if(field == SP_SEPARATOR)
-                       break;
-
-               if(is_spec && field != SP_NAME && field != SP_PING) {
-                       pos.x += hud_size[i] + hud_fontsize.x;
-                       continue;
-               }
-               str = HUD_GetField(pl, field);
-               str = HUD_FixScoreboardColumnWidth(i, str);
-
-               pos.x += hud_size[i] + hud_fontsize.x;
-
-               if(field == SP_NAME) {
-                       tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
-                       if (is_self)
-                               drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                       else
-                               drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
-               } else {
-                       tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
-                       if (is_self)
-                               drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                       else
-                               drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
-               }
-
-               tmp.x = hud_size[i] + hud_fontsize.x;
-               if(hud_field_icon0 != "")
-                       if (is_self)
-                               drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                       else
-                               drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
-               if(hud_field_icon1 != "")
-                       if (is_self)
-                               drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                       else
-                               drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
-               if(hud_field_icon2 != "")
-                       if (is_self)
-                               drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                       else
-                               drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
-       }
-
-       if(hud_field[i] == SP_SEPARATOR)
-       {
-               pos.x = xmax;
-               for(i = hud_num_fields-1; i > 0; --i)
-               {
-                       field = hud_field[i];
-                       if(field == SP_SEPARATOR)
-                               break;
-
-                       if(is_spec && field != SP_NAME && field != SP_PING) {
-                               pos.x -= hud_size[i] + hud_fontsize.x;
-                               continue;
-                       }
-
-                       str = HUD_GetField(pl, field);
-                       str = HUD_FixScoreboardColumnWidth(i, str);
-
-                       if(field == SP_NAME) {
-                               tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
-                               if(is_self)
-                                       drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                               else
-                                       drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
-                       } else {
-                               tmp.x = hud_fixscoreboardcolumnwidth_len;
-                               if(is_self)
-                                       drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                               else
-                                       drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
-                       }
-
-                       tmp.x = hud_size[i];
-                       if(hud_field_icon0 != "")
-                               if (is_self)
-                                       drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                               else
-                                       drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
-                       if(hud_field_icon1 != "")
-                               if (is_self)
-                                       drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                               else
-                                       drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
-                       if(hud_field_icon2 != "")
-                               if (is_self)
-                                       drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
-                               else
-                                       drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
-                       pos.x -= hud_size[i] + hud_fontsize.x;
-               }
-       }
-
-       if(pl.eliminated)
-               drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
-}
-
-/*
- * HUD_Scoreboard_MakeTable
- *
- * Makes a table for a team (for all playing players in DM) and fills it
- */
-
-vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
-{
-       float body_table_height;
-       vector tmp = '0 0 0', column_dim = '0 0 0';
-       entity pl;
-
-       body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
-
-       pos.y += autocvar_scoreboard_border_thickness;
-       pos -= '1 1 0';
-
-       tmp.x = sbwidth + 2;
-       tmp.y = 1.25 * hud_fontsize.y;
-
-       // rounded header
-       if (teamplay)
-               drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       else
-               drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-
-       // table border
-       tmp.y += autocvar_scoreboard_border_thickness;
-       tmp.y += body_table_height;
-       drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
-
-       // separator header/table
-       pos.y += 1.25 * hud_fontsize.y;
-       tmp.y = autocvar_scoreboard_border_thickness;
-       drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-
-       pos.y += autocvar_scoreboard_border_thickness;
-
-       // table background
-       tmp.y = body_table_height;
-       if (teamplay)
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       else
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-
-       // anyway, apply some color
-       //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
-
-       // go back to the top to make alternated columns highlighting and to print the strings
-       pos.y -= 1.25 * hud_fontsize.y;
-       pos.y -= autocvar_scoreboard_border_thickness;
-
-       pos += '1 1 0';
-
-       if (scoreboard_highlight)
-       {
-               column_dim.y = 1.25 * hud_fontsize.y; // header
-               column_dim.y += autocvar_scoreboard_border_thickness;
-               column_dim.y += body_table_height;
-       }
-
-       // print the strings of the columns headers and draw the columns
-       int i;
-       for(i = 0; i < hud_num_fields; ++i)
-       {
-               if(hud_field[i] == SP_SEPARATOR)
-                       break;
-               column_dim.x = hud_size[i] + hud_fontsize.x;
-               if (scoreboard_highlight)
-               {
-                       if (i % 2)
-                               drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
-               }
-               drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-               pos.x += column_dim.x;
-       }
-       if(hud_field[i] == SP_SEPARATOR)
-       {
-               pos.x = xmax;
-               tmp.y = 0;
-               for(i = hud_num_fields-1; i > 0; --i)
-               {
-                       if(hud_field[i] == SP_SEPARATOR)
-                               break;
-
-                       pos.x -= hud_size[i];
-
-                       if (scoreboard_highlight)
-                       {
-                               if (!(i % 2))
-                               {
-                                       if (i == hud_num_fields-1)
-                                               column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
-                                       else
-                                               column_dim.x = hud_size[i] + hud_fontsize.x;
-                                       drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
-                               }
-                       }
-
-                       tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
-                       tmp.x = (hud_size[i] - tmp.x);
-                       drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-                       pos.x -= hud_fontsize.x;
-               }
-       }
-
-       pos.x = xmin;
-       pos.y += 1.25 * hud_fontsize.y; // skip the header
-       pos.y += autocvar_scoreboard_border_thickness;
-
-       // item size
-       tmp.x = sbwidth;
-       tmp.y = hud_fontsize.y * 1.25;
-
-       // fill the table and draw the rows
-       i = 0;
-       if (teamplay)
-               for(pl = players.sort_next; pl; pl = pl.sort_next)
-               {
-                       if(pl.team != tm.team)
-                               continue;
-                       HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
-                       pos.y += 1.25 * hud_fontsize.y;
-                       ++i;
-               }
-       else
-               for(pl = players.sort_next; pl; pl = pl.sort_next)
-               {
-                       if(pl.team == NUM_SPECTATOR)
-                               continue;
-                       HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
-                       pos.y += 1.25 * hud_fontsize.y;
-                       ++i;
-               }
-
-       if (i == 0)
-               pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
-       pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
-
-       return pos;
-}
-
-float HUD_WouldDrawScoreboard() {
-       if (autocvar__hud_configure)
-               return 0;
-       else if (QuickMenu_IsOpened())
-               return 0;
-       else if (HUD_Radar_Clickable())
-               return 0;
-       else if (scoreboard_showscores)
-               return 1;
-       else if (intermission == 1)
-               return 1;
-       else if (intermission == 2)
-               return 0;
-       else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
-               return 1;
-       else if (scoreboard_showscores_force)
-               return 1;
-       return 0;
-}
-
-float average_accuracy;
-vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
-{
-       WepSet weapons_stat = WepSet_GetFromStat();
-       WepSet weapons_inmap = WepSet_GetFromStat_InMap();
-       float initial_posx = pos.x;
-       int disownedcnt = 0;
-       FOREACH(Weapons, it != WEP_Null, {
-               int weapon_stats = weapon_accuracy[i - WEP_FIRST];
-
-               WepSet set = it.m_wepset;
-               if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
-                       ++disownedcnt;
-       });
-
-       int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
-       if (weapon_cnt <= 0) return pos;
-
-       int rows = 1;
-       if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
-               rows = 2;
-       int columnns = ceil(weapon_cnt / rows);
-
-       float height = 40;
-       float fontsize = height * 1/3;
-       float weapon_height = height * 2/3;
-       float weapon_width = sbwidth / columnns / rows;
-
-       drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
-       vector tmp = '0 0 0';
-       tmp.x = sbwidth;
-       tmp.y = height * rows;
-
-       if (teamplay)
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       else
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
-
-       // column highlighting
-       for (int i = 0; i < columnns; ++i)
-       {
-               if ((i % 2) == 0)
-                       drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
-       }
-
-       // row highlighting
-       for (int i = 0; i < rows; ++i)
-       {
-               drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * sbwidth + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
-       }
-
-       average_accuracy = 0;
-       int weapons_with_stats = 0;
-       if (rows == 2)
-               pos.x += weapon_width / 2;
-
-       if (autocvar_scoreboard_accuracy_nocolors)
-               rgb = '1 1 1';
-       else
-               Accuracy_LoadColors();
-
-       float oldposx = pos.x;
-       vector tmpos = pos;
-
-       int column = 0;
-       FOREACH(Weapons, it != WEP_Null, {
-               int weapon_stats = weapon_accuracy[i - WEP_FIRST];
-
-               WepSet set = it.m_wepset;
-               if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
-                       continue;
-
-               float weapon_alpha;
-               if (weapon_stats >= 0)
-                       weapon_alpha = scoreboard_alpha_fg;
-               else
-                       weapon_alpha = 0.2 * scoreboard_alpha_fg;
-
-               // weapon icon
-               drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
-               // the accuracy
-               if (weapon_stats >= 0) {
-                       weapons_with_stats += 1;
-                       average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
-
-                       string s;
-                       s = sprintf("%d%%", weapon_stats*100);
-
-                       float padding;
-                       padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
-
-                       if(!autocvar_scoreboard_accuracy_nocolors)
-                               rgb = Accuracy_GetColor(weapon_stats);
-
-                       drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-               }
-               tmpos.x += weapon_width * rows;
-               pos.x += weapon_width * rows;
-               if (rows == 2 && column == columnns - 1) {
-                       tmpos.x = oldposx;
-                       tmpos.y += height;
-                       pos.y += height;
-               }
-               ++column;
-       });
-
-       if (weapons_with_stats)
-               average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
-
-       pos.y += height;
-       pos.y += 1.25 * hud_fontsize.y;
-       pos.x = initial_posx;
-       return pos;
-}
-
-vector HUD_DrawKeyValue(vector pos, string key, string value) {
-       float px = pos.x;
-       pos.x += hud_fontsize.x * 0.25;
-       drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
-       drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       pos.x = px;
-       pos.y+= hud_fontsize.y;
-
-       return pos;
-}
-
-vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
-       float stat_secrets_found, stat_secrets_total;
-       float stat_monsters_killed, stat_monsters_total;
-       float rows = 0;
-       string val;
-
-       // get monster stats
-       stat_monsters_killed = STAT(MONSTERS_KILLED);
-       stat_monsters_total = STAT(MONSTERS_TOTAL);
-
-       // get secrets stats
-       stat_secrets_found = STAT(SECRETS_FOUND);
-       stat_secrets_total = STAT(SECRETS_TOTAL);
-
-       // get number of rows
-       if(stat_secrets_total)
-               rows += 1;
-       if(stat_monsters_total)
-               rows += 1;
-
-       // if no rows, return
-       if (!rows)
-               return pos;
-
-       //  draw table header
-       drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
-
-       // draw table
-       vector tmp = '0 0 0';
-       tmp.x = sbwidth;
-       tmp.y = hud_fontsize.y * rows;
-
-       if (teamplay)
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       else
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
-
-       // draw monsters
-       if(stat_monsters_total)
-       {
-               val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
-               pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
-       }
-
-       // draw secrets
-       if(stat_secrets_total)
-       {
-               val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
-               pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
-       }
-
-       // update position
-       pos.y += 1.25 * hud_fontsize.y;
-       return pos;
-}
-
-
-vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_size)
-{
-       int i;
-       RANKINGS_RECEIVED_CNT = 0;
-       for (i=RANKINGS_CNT-1; i>=0; --i)
-               if (grecordtime[i])
-                       ++RANKINGS_RECEIVED_CNT;
-
-       if (RANKINGS_RECEIVED_CNT == 0)
-               return pos;
-
-       float is_spec;
-       is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
-       vector hl_rgb;
-       hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
-       hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
-       hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
-
-       pos.y += hud_fontsize.y;
-       drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
-       vector tmp = '0 0 0';
-       tmp.x = sbwidth;
-       tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
-
-       if (teamplay)
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       else
-               drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
-       drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
-
-       // row highlighting
-       for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
-       {
-               string n, p;
-               float t;
-               t = grecordtime[i];
-               if (t == 0)
-                       continue;
-               n = grecordholder[i];
-               p = count_ordinal(i+1);
-               if(grecordholder[i] == entcs_GetName(player_localnum))
-                       drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
-               else if(!(i % 2) && scoreboard_highlight)
-                       drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
-               drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-               drawstring(pos + '3 0 0' * hud_fontsize.y, TIME_ENCODED_TOSTRING(t), '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-               drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-               pos.y += 1.25 * hud_fontsize.y;
-       }
-       pos.y += autocvar_scoreboard_border_thickness;
-
-       return pos;
-}
-
-float hud_woulddrawscoreboard_prev;
-float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
-void HUD_DrawScoreboard()
-{
-       float hud_woulddrawscoreboard;
-       hud_woulddrawscoreboard = scoreboard_active;
-       if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
-               hud_woulddrawscoreboard_change = time;
-               hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
-       }
-
-       if(hud_woulddrawscoreboard) {
-               float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
-               if (scoreboard_fadeinspeed)
-                       scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
-               else
-                       scoreboard_fade_alpha = 1;
-       }
-       else {
-               float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
-               if (scoreboard_fadeoutspeed)
-                       scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
-               else
-                       scoreboard_fade_alpha = 0;
-       }
-
-       if (!scoreboard_fade_alpha)
-               return;
-
-       if (autocvar_scoreboard_dynamichud)
-               HUD_Scale_Enable();
-       else
-               HUD_Scale_Disable();
-
-       HUD_UpdatePlayerTeams();
-
-       scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
-       scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
-       scoreboard_highlight = autocvar_scoreboard_highlight;
-       scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
-       scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
-       scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
-       scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
-
-       vector rgb, pos, tmp;
-       entity pl, tm;
-       string str;
-
-       xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
-       ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
-
-       xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
-       ymax = (vid_conheight - ymin);
-
-       sbwidth = xmax - xmin;
-
-       // Initializes position
-       pos.x = xmin;
-       pos.y = ymin;
-       pos.z = 0;
-
-       // Heading
-       vector sb_heading_fontsize;
-       sb_heading_fontsize = hud_fontsize * 2;
-       draw_beginBoldFont();
-       drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       draw_endBoldFont();
-
-       pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
-
-       // Draw the scoreboard
-       vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
-
-       if(teamplay)
-       {
-               vector team_score_baseoffset;
-               team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
-               for(tm = teams.sort_next; tm; tm = tm.sort_next)
-               {
-                       if(tm.team == NUM_SPECTATOR)
-                               continue;
-                       if(!tm.team && teamplay)
-                               continue;
-
-                       draw_beginBoldFont();
-                       rgb = Team_ColorRGB(tm.team);
-                       str = ftos(tm.(teamscores[ts_primary]));
-                       drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-
-                       if(ts_primary != ts_secondary)
-                       {
-                               str = ftos(tm.(teamscores[ts_secondary]));
-                               drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-                       }
-                       draw_endBoldFont();
-
-                       pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
-               }
-               rgb.x = autocvar_scoreboard_color_bg_r;
-               rgb.y = autocvar_scoreboard_color_bg_g;
-               rgb.z = autocvar_scoreboard_color_bg_b;
-       }
-       else
-       {
-               rgb.x = autocvar_scoreboard_color_bg_r;
-               rgb.y = autocvar_scoreboard_color_bg_g;
-               rgb.z = autocvar_scoreboard_color_bg_b;
-
-               for(tm = teams.sort_next; tm; tm = tm.sort_next)
-               {
-                       if(tm.team == NUM_SPECTATOR)
-                               continue;
-                       if(!tm.team && teamplay)
-                               continue;
-
-                       pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
-               }
-       }
-
-       if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
-               if(race_speedaward) {
-                       drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-                       pos.y += 1.25 * hud_fontsize.y;
-               }
-               if(race_speedaward_alltimebest) {
-                       drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-                       pos.y += 1.25 * hud_fontsize.y;
-               }
-               pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
-       }
-       else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
-               if(teamplay)
-                       pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
-               else
-                       pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
-       }
-
-
-       if(teamplay)
-               pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
-       else
-               pos = HUD_DrawMapStats(pos, rgb, bg_size);
-
-       // List spectators
-       float specs;
-       specs = 0;
-       tmp = pos;
-       vector item_size;
-       item_size.x = sbwidth;
-       item_size.y = hud_fontsize.y * 1.25;
-       item_size.z = 0;
-       for(pl = players.sort_next; pl; pl = pl.sort_next)
-       {
-               if(pl.team != NUM_SPECTATOR)
-                       continue;
-               pos.y += 1.25 * hud_fontsize.y;
-               HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
-               ++specs;
-       }
-
-       if(specs)
-       {
-               draw_beginBoldFont();
-               drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-               draw_endBoldFont();
-               pos.y += 1.25 * hud_fontsize.y;
-       }
-
-       // Print info string
-       float tl, fl, ll;
-       str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
-       tl = STAT(TIMELIMIT);
-       fl = STAT(FRAGLIMIT);
-       ll = STAT(LEADLIMIT);
-       if(gametype == MAPINFO_TYPE_LMS)
-       {
-               if(tl > 0)
-                       str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
-       }
-       else
-       {
-               if(tl > 0)
-                       str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
-               if(fl > 0)
-               {
-                       if(tl > 0)
-                               str = strcat(str, _(" or"));
-                       if(teamplay)
-                       {
-                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
-                                       (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
-                                       (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
-                                       TranslateScoresLabel(teamscores_label[ts_primary])));
-                       }
-                       else
-                       {
-                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
-                                       (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
-                                       (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
-                                       TranslateScoresLabel(scores_label[ps_primary])));
-                       }
-               }
-               if(ll > 0)
-               {
-                       if(tl > 0 || fl > 0)
-                               str = strcat(str, _(" or"));
-                       if(teamplay)
-                       {
-                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
-                                       (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
-                                       (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
-                                       TranslateScoresLabel(teamscores_label[ts_primary])));
-                       }
-                       else
-                       {
-                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
-                                       (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
-                                       (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
-                                       TranslateScoresLabel(scores_label[ps_primary])));
-                       }
-               }
-       }
-
-       pos.y += 1.2 * hud_fontsize.y;
-       drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-
-       // print information about respawn status
-       float respawn_time = STAT(RESPAWN_TIME);
-       if(!intermission)
-       if(respawn_time)
-       {
-               if(respawn_time < 0)
-               {
-                       // a negative number means we are awaiting respawn, time value is still the same
-                       respawn_time *= -1; // remove mark now that we checked it
-                       respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
-
-                       str = sprintf(_("^1Respawning in ^3%s^1..."),
-                               (autocvar_scoreboard_respawntime_decimals ?
-                                       count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
-                                       :
-                                       count_seconds(respawn_time - time)
-                               )
-                       );
-               }
-               else if(time < respawn_time)
-               {
-                       str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
-                               (autocvar_scoreboard_respawntime_decimals ?
-                                       count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
-                                       :
-                                       count_seconds(respawn_time - time)
-                               )
-                       );
-               }
-               else if(time >= respawn_time)
-                       str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
-
-               pos.y += 1.2 * hud_fontsize.y;
-               drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
-       }
-
-       scoreboard_bottom = pos.y + 2 * hud_fontsize.y;
-}
diff --git a/qcsrc/client/scoreboard.qh b/qcsrc/client/scoreboard.qh
deleted file mode 100644 (file)
index 8fccae9..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-float xmin, xmax, ymin, ymax, sbwidth;
-
-float scoreboard_active;
-float scoreboard_fade_alpha;
-
-void Cmd_HUD_SetFields(float argc);
-void HUD_DrawScoreboard();
-void HUD_InitScores();
-void HUD_UpdatePlayerPos(entity pl);
-void HUD_UpdateTeamPos(entity Team);
-float HUD_WouldDrawScoreboard();
index 6700ba61c7b87cadfee0438093ffdf46a2d5ee7e..e46a97cb1d9066d12ef5e53fefdc01a8dc376731 100644 (file)
@@ -1,9 +1,10 @@
 #include "shownames.qh"
 
-#include "hud/all.qh"
+#include "hud/_mod.qh"
 
 #include <common/ent_cs.qh>
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/mapinfo.qh>
 #include <common/teams.qh>
 
@@ -34,7 +35,7 @@ const float SHOWNAMES_FADESPEED = 4;
 const float SHOWNAMES_FADEDELAY = 0.4;
 void Draw_ShowNames(entity this)
 {
-       if (this.sv_entnum == player_localentnum)  // self or spectatee
+       if (this.sv_entnum == (current_player + 1))  // self or spectatee
                if (!(autocvar_hud_shownames_self && autocvar_chase_active)) return;
        if (!this.sameteam && !autocvar_hud_shownames_enemies) return;
        bool hit;
@@ -48,34 +49,41 @@ void Draw_ShowNames(entity this)
                hit = !(trace_fraction < 1 && (trace_networkentity != this.sv_entnum && trace_ent.entnum != this.sv_entnum));
        }
        // handle tag fading
-       bool overlap = false;
+       int overlap = -1;
        vector o = project_3d_to_2d(this.origin + eZ * autocvar_hud_shownames_offset);
+       if (autocvar_hud_shownames_crosshairdistance)
+       {
+               float d = autocvar_hud_shownames_crosshairdistance;
+               float w = o.x - vid_conwidth / 2;
+               float h = o.y - vid_conheight / 2;
+               if (d * d > w * w + h * h) this.pointtime = time;
+               if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time)
+                       overlap = 1;
+               else if(!autocvar_hud_shownames_crosshairdistance_antioverlap)
+                       overlap = 0;
+       }
+
        float dist = vlen(this.origin - view_origin);
-       if (autocvar_hud_shownames_antioverlap)
+       if (overlap == -1 && autocvar_hud_shownames_antioverlap)
        {
                // fade tag out if another tag that is closer to you overlaps
-               LL_EACH(shownames_ent, it != this && entcs_receiver(i), {
+               entity entcs = NULL;
+               LL_EACH(shownames_ent, it != this, {
+                       entcs = entcs_receiver(i);
+                       if (!(entcs && entcs.has_sv_origin))
+                               continue;
                        vector eo = project_3d_to_2d(it.origin);
                        if (eo.z < 0 || eo.x < 0 || eo.y < 0 || eo.x > vid_conwidth || eo.y > vid_conheight) continue;
                        eo.z = 0;
                        if (vdist(((eX * o.x + eY * o.y) - eo), <, autocvar_hud_shownames_antioverlap_distance)
                            && vdist((it.origin - view_origin), <, dist))
                        {
-                               overlap = true;
+                               overlap = 1;
                                break;
                        }
                });
        }
        bool onscreen = (o.z >= 0 && o.x >= 0 && o.y >= 0 && o.x <= vid_conwidth && o.y <= vid_conheight);
-       if (autocvar_hud_shownames_crosshairdistance)
-       {
-               float d = autocvar_hud_shownames_crosshairdistance;
-               float w = o.x - vid_conwidth / 2;
-               float h = o.y - vid_conheight / 2;
-               if (d * d > w * w + h * h) this.pointtime = time;
-               if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) overlap = true;
-               else overlap = (autocvar_hud_shownames_crosshairdistance_antioverlap ? overlap : false); // override what antioverlap says unless allowed by cvar.
-       }
        if (!this.fadedelay) this.fadedelay = time + SHOWNAMES_FADEDELAY;
        if (this.csqcmodel_isdead)                                                                   // dead player, fade out slowly
        {
@@ -86,7 +94,7 @@ void Draw_ShowNames(entity this)
                this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime);
                this.fadedelay = 0;                         // reset fade in delay, enemy has left the view
        }
-       else if (overlap)                               // tag overlap detected, fade out
+       else if (overlap > 0) // tag overlap detected, fade out
        {
                this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime);
        }
index a8708f2460f809a579f96db0428fbc1522432c61..782776eb7866757ebc7ff8ad24076b4923e57859 100644 (file)
@@ -1,6 +1,6 @@
 #include "teamradar.qh"
 
-#include "hud/all.qh"
+#include "hud/_mod.qh"
 
 #include <common/mutators/mutator/waypoints/all.qh>
 
@@ -20,7 +20,7 @@ vector teamradar_texcoord_to_2dcoord(vector in)
        vector out;
        in -= teamradar_origin3d_in_texcoord;
 
-       out = rotate(in, teamradar_angle * DEG2RAD);
+       out = Rotate(in, teamradar_angle * DEG2RAD);
        out.y = - out.y; // screen space is reversed
 
        out = out * teamradar_size;
@@ -42,19 +42,19 @@ vector teamradar_2dcoord_to_texcoord(vector in)
        out = out / teamradar_size;
 
        out_y = - out_y; // screen space is reversed
-       out = rotate(out, -teamradar_angle * DEG2RAD);
+       out = Rotate(out, -teamradar_angle * DEG2RAD);
 
        out += teamradar_origin3d_in_texcoord;
 
        return out;
 }
 
-vector teamradar_texcoord_to_3dcoord(vector in,float z)
+vector teamradar_texcoord_to_3dcoord(vector in,float oz)
 {
        vector out;
        out_x = in_x * (mi_picmax_x - mi_picmin_x) + mi_picmin_x;
        out_y = in_y * (mi_picmax_y - mi_picmin_y) + mi_picmin_y;
-       out_z = z;
+       out_z = oz;
        return out;
 }
 
@@ -203,6 +203,7 @@ NET_HANDLE(ENT_CLIENT_RADARLINK, bool isnew)
 
        this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
        this.classname = "radarlink";
+       if (isnew) IL_PUSH(g_radarlinks, this);
 
        if(sendflags & 1)
        {
index 0b1884ccf3a713d8c77488bd7d809dcf3d013cb3..251c1a53f12a9185f0c5274839479066a4d7e759 100644 (file)
@@ -32,7 +32,7 @@ vector teamradar_3dcoord_to_texcoord(vector in);
 
 vector teamradar_texcoord_to_2dcoord(vector in);
 
-vector teamradar_texcoord_to_3dcoord(vector in,float z);
+vector teamradar_texcoord_to_3dcoord(vector in,float oz);
 
 void draw_teamradar_background(float fg);
 
index 5cb3c18c1f15ab4af2098d5f8c7cd18c4924f835..5f88e78edf4b13bcc8702fc030edb6f22cc1d8ac 100644 (file)
@@ -1,10 +1,10 @@
 #include "view.qh"
 
 #include "announcer.qh"
-#include "hud/all.qh"
+#include "hud/_mod.qh"
 #include "mapvoting.qh"
-#include "scoreboard.qh"
 #include "shownames.qh"
+#include "hud/panel/scoreboard.qh"
 #include "hud/panel/quickmenu.qh"
 
 #include "mutators/events.qh"
 #include <common/ent_cs.qh>
 #include <common/anim.qh>
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/debug.qh>
 #include <common/mapinfo.qh>
-#include <common/gamemodes/all.qh>
+#include <common/gamemodes/_mod.qh>
 #include <common/physics/player.qh>
 #include <common/stats.qh>
 #include <common/triggers/target/music.qh>
 #include <common/teams.qh>
 
+#include <common/weapons/weapon/tuba.qh>
+
 #include <common/vehicles/all.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/viewloc.qh>
 #include <common/minigames/cl_minigames.qh>
 #include <common/minigames/cl_minigames_hud.qh>
@@ -122,7 +125,6 @@ void calc_followmodel_ofs(entity view)
        float frac;
        vector gunorg = '0 0 0';
        static vector vel_average;
-       static vector gunorg_prev = '0 0 0';
        static vector gunorg_adjustment_highpass;
        static vector gunorg_adjustment_lowpass;
 
@@ -293,8 +295,8 @@ void viewmodel_draw(entity this)
        else if (wasinvehicle) a = 1;
        wasinvehicle = invehicle;
        Weapon wep = activeweapon;
-       int c = stof(getplayerkeyvalue(current_player, "colors"));
-       vector g = weaponentity_glowmod(wep, c);
+       int c = entcs_GetClientColors(current_player);
+       vector g = weaponentity_glowmod(wep, NULL, c);
        entity me = CSQCModel_server2csqc(player_localentnum - 1);
        int fx = ((me.csqcmodel_effects & EFMASK_CHEAP)
                | EF_NODEPTHTEST)
@@ -311,6 +313,9 @@ void viewmodel_draw(entity this)
        {
                static string name_last;
                string name = wep.mdl;
+               string newname = wep.wr_viewmodel(wep, this);
+               if(newname)
+                       name = newname;
                bool swap = name != name_last;
                // if (swap)
                {
@@ -363,6 +368,7 @@ STATIC_INIT(Porto)
 {
        entity e = new_pure(porto);
        e.draw = Porto_Draw;
+       IL_PUSH(g_drawables, e);
        e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
 }
 
@@ -447,7 +453,7 @@ vector GetCurrentFov(float fov)
                        zoomspeed = 3.5;
 
        zoomdir = button_zoom;
-       if(hud == HUD_NORMAL)
+       if(hud == HUD_NORMAL && !spectatee_status)
        if(switchweapon == activeweapon)
        if((activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary))) // do NOT use switchweapon here
                zoomdir += button_attack2;
@@ -709,7 +715,6 @@ float TrueAimCheck()
 
 void PostInit();
 void CSQC_Demo_Camera();
-float HUD_WouldDrawScoreboard();
 float camera_mode;
 const float CAMERA_FREE = 1;
 const float CAMERA_CHASE = 2;
@@ -742,7 +747,7 @@ bool WantEventchase(entity this)
 {
        if(autocvar_cl_orthoview)
                return false;
-       if(intermission)
+       if(STAT(GAMEOVER) || intermission)
                return true;
        if(this.viewloc)
                return true;
@@ -791,7 +796,7 @@ void UpdateDamage()
        if (damage_dealt_time != damage_dealt_time_prev)
        {
                unaccounted_damage += unaccounted_damage_new;
-               LOG_TRACE("dmg total: ", ftos(unaccounted_damage), " (+", ftos(unaccounted_damage_new), ")", "\n");
+               LOG_TRACE("dmg total: ", ftos(unaccounted_damage), " (+", ftos(unaccounted_damage_new), ")");
        }
        damage_dealt_time_prev = damage_dealt_time;
 
@@ -817,8 +822,8 @@ void HitSound()
                        float a = autocvar_cl_hitsound_max_pitch;
                        float b = autocvar_cl_hitsound_min_pitch;
                        float c = autocvar_cl_hitsound_nom_damage;
-                       float x = unaccounted_damage;
-                       float pitch_shift = (b*x*(a-1) + a*c*(1-b)) / (x*(a-1) + c*(1-b));
+                       float d = unaccounted_damage;
+                       float pitch_shift = (b*d*(a-1) + a*c*(1-b)) / (d*(a-1) + c*(1-b));
 
                        // if sound variation is disabled, set pitch_shift to 1
                        if (autocvar_cl_hitsound == 1)
@@ -831,7 +836,7 @@ void HitSound()
                                pitch_shift = mirror_value + (mirror_value - pitch_shift);
                        }
 
-                       LOG_TRACE("dmg total (dmg): ", ftos(unaccounted_damage), " , pitch shift: ", ftos(pitch_shift), "\n");
+                       LOG_TRACE("dmg total (dmg): ", ftos(unaccounted_damage), " , pitch shift: ", ftos(pitch_shift));
 
                        // todo: avoid very long and very short sounds from wave stretching using different sound files? seems unnecessary
                        // todo: normalize sound pressure levels? seems unnecessary
@@ -860,7 +865,7 @@ vector crosshair_getcolor(entity this, float health_stat)
        {
                case 1: // crosshair_color_per_weapon
                {
-                       if(this != WEP_Null)
+                       if(this != WEP_Null && hud == HUD_NORMAL)
                        {
                                wcross_color = this.wpcolor;
                                break;
@@ -870,7 +875,7 @@ vector crosshair_getcolor(entity this, float health_stat)
 
                case 2: // crosshair_color_by_health
                {
-                       float x = health_stat;
+                       float hp = health_stat;
 
                        //x = red
                        //y = green
@@ -878,33 +883,33 @@ vector crosshair_getcolor(entity this, float health_stat)
 
                        wcross_color.z = 0;
 
-                       if(x > 200)
+                       if(hp > 200)
                        {
                                wcross_color.x = 0;
                                wcross_color.y = 1;
                        }
-                       else if(x > 150)
+                       else if(hp > 150)
                        {
-                               wcross_color.x = 0.4 - (x-150)*0.02 * 0.4;
-                               wcross_color.y = 0.9 + (x-150)*0.02 * 0.1;
+                               wcross_color.x = 0.4 - (hp-150)*0.02 * 0.4;
+                               wcross_color.y = 0.9 + (hp-150)*0.02 * 0.1;
                        }
-                       else if(x > 100)
+                       else if(hp > 100)
                        {
-                               wcross_color.x = 1 - (x-100)*0.02 * 0.6;
-                               wcross_color.y = 1 - (x-100)*0.02 * 0.1;
-                               wcross_color.z = 1 - (x-100)*0.02;
+                               wcross_color.x = 1 - (hp-100)*0.02 * 0.6;
+                               wcross_color.y = 1 - (hp-100)*0.02 * 0.1;
+                               wcross_color.z = 1 - (hp-100)*0.02;
                        }
-                       else if(x > 50)
+                       else if(hp > 50)
                        {
                                wcross_color.x = 1;
                                wcross_color.y = 1;
-                               wcross_color.z = 0.2 + (x-50)*0.02 * 0.8;
+                               wcross_color.z = 0.2 + (hp-50)*0.02 * 0.8;
                        }
-                       else if(x > 20)
+                       else if(hp > 20)
                        {
                                wcross_color.x = 1;
-                               wcross_color.y = (x-20)*90/27/100;
-                               wcross_color.z = (x-20)*90/27/100 * 0.2;
+                               wcross_color.y = (hp-20)*90/27/100;
+                               wcross_color.z = (hp-20)*90/27/100 * 0.2;
                        }
                        else
                        {
@@ -935,13 +940,16 @@ void HUD_Crosshair(entity this)
 {
        float f, i, j;
        vector v;
-       if(!scoreboard_active && !camera_active && intermission != 2 &&
-               spectatee_status != -1 && !csqcplayer.viewloc &&
+       if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAMEOVER) &&
+               spectatee_status != -1 && !csqcplayer.viewloc && !MUTATOR_CALLHOOK(DrawCrosshair) &&
                !HUD_MinigameMenu_IsOpened() )
        {
                if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering
                        return;
 
+               if (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2)
+                       return;
+
                if (hud != HUD_NORMAL)
                {
                        HUD_Crosshair_Vehicle(this);
@@ -976,7 +984,7 @@ void HUD_Crosshair(entity this)
                                v = wcross_origin - wcross_oldorigin;
                                v.x /= vid_conwidth;
                                v.y /= vid_conheight;
-                               if(vlen(v) > 0.01)
+                               if(vdist(v, >, 0.01))
                                        shottype = SHOTTYPE_HITOBSTRUCTION;
                        }
                        if(!autocvar_crosshair_hittest_showimpact)
@@ -1318,6 +1326,13 @@ void HUD_Crosshair(entity this)
 
 void HUD_Draw(entity this)
 {
+       // if we don't know gametype and scores yet avoid drawing the scoreboard
+       // also in the very first frames, player state may be inconsistent so avoid drawing the hud at all
+       // e.g. since initial player's health is 0 hud would display the hud_damage effect,
+       // cl_deathscoreboard would show the scoreboard and so on
+       if(!gametype)
+               return;
+
        if(!intermission)
        if (MUTATOR_CALLHOOK(HUD_Draw_overlay))
        {
@@ -1333,6 +1348,11 @@ void HUD_Draw(entity this)
                DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * STAT(NADE_TIMER)) - ('0 1 1' * STAT(NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
                drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
        }
+       else if(STAT(CAPTURE_PROGRESS))
+       {
+               DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(CAPTURE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+               drawstring_aspect(eY * 0.64 * vid_conheight, _("Capture progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
+       }
        else if(STAT(REVIVE_PROGRESS))
        {
                DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
@@ -1346,7 +1366,6 @@ void HUD_Draw(entity this)
                                Accuracy_LoadLevels();
 
                        HUD_Main();
-                       HUD_DrawScoreboard();
                        HUD_Scale_Disable();
                }
 
@@ -1365,12 +1384,13 @@ float oldr_useportalculling;
 float oldr_useinfinitefarclip;
 
 void cl_notice_run();
+
 float prev_myteam;
 int lasthud;
 float vh_notice_time;
 void WaypointSprite_Load();
-void CSQC_UpdateView(float w, float h)
-{ENGINE_EVENT();
+void CSQC_UpdateView(entity this, float w, float h)
+{
     TC(int, w); TC(int, h);
        entity e;
        float fov;
@@ -1404,10 +1424,6 @@ void CSQC_UpdateView(float w, float h)
        else
                view_quality = 1;
 
-       // this needs to be updated manually now due to the destruction of engine physics stats
-       if(!isdemo() && autocvar_slowmo != STAT(MOVEVARS_TIMESCALE))
-               cvar_set("slowmo", ftos(STAT(MOVEVARS_TIMESCALE)));
-
        button_attack2 = PHYS_INPUT_BUTTON_ATCK2(this);
        button_zoom = PHYS_INPUT_BUTTON_ZOOM(this);
 
@@ -1459,9 +1475,26 @@ void CSQC_UpdateView(float w, float h)
        // event chase camera
        if(autocvar_chase_active <= 0) // greater than 0 means it's enabled manually, and this code is skipped
        {
+               if(STAT(CAMERA_SPECTATOR))
+               {
+                       if(spectatee_status > 0)
+                       {
+                               if(!autocvar_chase_active)
+                               {
+                                       cvar_set("chase_active", "-2");
+                                       goto skip_eventchase_death;
+                               }
+                       }
+                       else if(autocvar_chase_active == -2)
+                               cvar_set("chase_active", "0");
+
+                       if(autocvar_chase_active == -2)
+                               goto skip_eventchase_death;
+               }
+               else if(autocvar_chase_active == -2)
+                       cvar_set("chase_active", "0");
+
                float vehicle_chase = (hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0));
-               float ons_roundlost = (gametype == MAPINFO_TYPE_ONSLAUGHT && STAT(ROUNDLOST));
-               entity gen = NULL;
 
                float vehicle_viewdist = 0;
                vector vehicle_viewofs = '0 0 0';
@@ -1476,17 +1509,18 @@ void CSQC_UpdateView(float w, float h)
                        }
                }
 
-               if(ons_roundlost)
-               {
-                       FOREACH_ENTITY_CLASS("onslaught_generator", it.health <= 0, {
-                               gen = it;
-                               break;
-                       });
-                       if(!gen)
-                               ons_roundlost = false; // don't enforce the 3rd person camera if there is no dead generator to show
-               }
-               if(WantEventchase(this) || (!autocvar_cl_orthoview && ons_roundlost))
+               if(WantEventchase(this))
                {
+                       vector current_view_origin_override = '0 0 0';
+                       vector view_offset_override = '0 0 0';
+                       float chase_distance_override = 0;
+                       bool custom_eventchase = MUTATOR_CALLHOOK(CustomizeEventchase, this);
+                       if(custom_eventchase)
+                       {
+                               current_view_origin_override = M_ARGV(0, vector);
+                               view_offset_override = M_ARGV(1, vector);
+                               chase_distance_override = M_ARGV(0, float);
+                       }
                        eventchase_running = true;
 
                        entity local_player = ((csqcplayer) ? csqcplayer : CSQCModel_server2csqc(player_localentnum - 1));
@@ -1495,7 +1529,8 @@ void CSQC_UpdateView(float w, float h)
 
                        // make special vector since we can't use view_origin (It is one frame old as of this code, it gets set later with the results this code makes.)
                        vector current_view_origin = (csqcplayer ? csqcplayer.origin : pmove_org);
-                       if(ons_roundlost) { current_view_origin = gen.origin; }
+                       if (custom_eventchase)
+                               current_view_origin = current_view_origin_override;
 
                        // detect maximum viewoffset and use it
                        vector view_offset = autocvar_cl_eventchase_viewoffset;
@@ -1506,7 +1541,8 @@ void CSQC_UpdateView(float w, float h)
                                else
                                        view_offset = autocvar_cl_eventchase_vehicle_viewoffset;
                        }
-                       if(ons_roundlost) { view_offset = autocvar_cl_eventchase_generator_viewoffset; }
+                       if (custom_eventchase)
+                               view_offset = view_offset_override;
 
                        if(view_offset)
                        {
@@ -1529,7 +1565,8 @@ void CSQC_UpdateView(float w, float h)
                                else
                                        chase_distance = autocvar_cl_eventchase_vehicle_distance;
                        }
-                       if(ons_roundlost) { chase_distance = autocvar_cl_eventchase_generator_distance; }
+                       if (custom_eventchase)
+                               chase_distance = chase_distance_override;
 
                        if(autocvar_cl_eventchase_speed && eventchase_current_distance < chase_distance)
                                eventchase_current_distance += autocvar_cl_eventchase_speed * (chase_distance - eventchase_current_distance) * frametime; // slow down the further we get
@@ -1569,6 +1606,8 @@ void CSQC_UpdateView(float w, float h)
                eventchase_current_distance = 0;
        }
 
+       LABEL(skip_eventchase_death);
+
        // do lockview after event chase camera so that it still applies whenever necessary.
        if(autocvar_cl_lockview || (!autocvar_hud_cursormode && (autocvar__hud_configure && spectatee_status <= 0 || intermission > 1 || QuickMenu_IsOpened())))
        {
@@ -1709,6 +1748,9 @@ void CSQC_UpdateView(float w, float h)
        if(!postinit)
                PostInit();
 
+       if(intermission && !intermission_time)
+               intermission_time = time;
+
        if(intermission && !isdemo() && !(calledhooks & HOOK_END))
        {
                if(calledhooks & HOOK_START)
@@ -1748,13 +1790,6 @@ void CSQC_UpdateView(float w, float h)
 
        switchweapon = Weapons_from(STAT(SWITCHWEAPON));
 
-       f = (serverflags & SERVERFLAG_TEAMPLAY);
-       if(f != teamplay)
-       {
-               teamplay = f;
-               HUD_InitScores();
-       }
-
        if(last_switchweapon != switchweapon)
        {
                weapontime = time;
@@ -1805,8 +1840,7 @@ void CSQC_UpdateView(float w, float h)
        else if(csqcplayer.viewloc) { setproperty(VF_FOV, GetViewLocationFOV(110)); } // enforce 110 fov, so things dont look odd
        else { setproperty(VF_FOV, GetCurrentFov(fov)); }
 
-       // Camera for demo playback
-       if(camera_active)
+       if(camera_active) // Camera for demo playback
        {
                if(autocvar_camera_enable)
                        CSQC_Demo_Camera();
@@ -1847,7 +1881,7 @@ void CSQC_UpdateView(float w, float h)
           mousepos = mousepos*0.5 + getmousepos();
         */
 
-       FOREACH_ENTITY(it.draw, it.draw(it));
+       IL_EACH(g_drawables, it.draw, it.draw(it));
 
        addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
        renderscene();
@@ -2177,11 +2211,11 @@ void CSQC_UpdateView(float w, float h)
          } else */
 
        // draw 2D entities
-       FOREACH_ENTITY(it.draw2d, it.draw2d(it));
+       IL_EACH(g_drawables_2d, it.draw2d, it.draw2d(it));
        Draw_ShowNames_All();
        Debug_Draw();
 
-       scoreboard_active = HUD_WouldDrawScoreboard();
+       scoreboard_active = Scoreboard_WouldDraw();
 
        HUD_Draw(this); // this parameter for deep vehicle function
 
@@ -2220,7 +2254,7 @@ void CSQC_UpdateView(float w, float h)
 
        if(autocvar__hud_configure)
                HUD_Panel_Mouse();
-       else if ( HUD_MinigameMenu_IsOpened() || minigame_isactive() )
+       else if (HUD_MinigameMenu_IsOpened() || active_minigame)
                HUD_Minigame_Mouse();
        else if(QuickMenu_IsOpened())
                QuickMenu_Mouse();
@@ -2234,6 +2268,8 @@ void CSQC_UpdateView(float w, float h)
        // let's reset the view back to normal for the end
        setproperty(VF_MIN, '0 0 0');
        setproperty(VF_SIZE, '1 0 0' * w + '0 1 0' * h);
+
+       IL_ENDFRAME();
 }
 
 
index b94fddd8d81e91ce4283c915dad6ab56ed0dc149..64916ad8c17d05251f940ed0ae9e02e340ffb8bf 100644 (file)
@@ -213,11 +213,11 @@ NET_HANDLE(ENT_CLIENT_WALL, bool isnew)
                        this.movedir_z = ReadCoord();
                        this.lip = ReadByte() / 255.0;
                }
-               this.fade_start = ReadShort();
-               this.fade_end = ReadShort();
-               this.alpha_max = ReadShort();
-               this.alpha_min = ReadShort();
-               this.inactive = ReadShort();
+               this.fade_start = ReadByte();
+               this.fade_end = ReadByte();
+               this.alpha_max = ReadByte();
+               this.alpha_min = ReadByte();
+               this.inactive = ReadByte();
                this.fade_vertical_offset = ReadShort();
                BGMScript_InitEntity(this);
        }
@@ -230,5 +230,6 @@ NET_HANDLE(ENT_CLIENT_WALL, bool isnew)
 
        this.entremove = Ent_Wall_Remove;
        this.draw = Ent_Wall_Draw;
+       if (isnew) IL_PUSH(g_drawables, this);
        setpredraw(this, Ent_Wall_PreDraw);
 }
index 6261fa842f987773298274395246d7a32f782739..1f88bc8d60ae01519f0f13774d0f81191e35e279 100644 (file)
@@ -6,6 +6,7 @@
 #include "../mutators/events.qh"
 
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/physics/movetypes/movetypes.qh>
 
 #include <lib/csqcmodel/interpolate.qh>
 .float scale;
 .vector colormod;
 
-void SUB_Stop(entity this)
+void SUB_Stop(entity this, entity toucher)
 {
-       this.move_velocity = this.move_avelocity = '0 0 0';
-       this.move_movetype = MOVETYPE_NONE;
+       this.velocity = this.avelocity = '0 0 0';
+       set_movetype(this, MOVETYPE_NONE);
 }
 
 void Projectile_ResetTrail(entity this, vector to)
@@ -58,11 +59,11 @@ void Projectile_Draw(entity this)
        float t;
        float a;
 
-       f = this.move_flags;
+       f = this.flags;
 
        if (this.count & 0x80)
        {
-               // this.move_flags &= ~FL_ONGROUND;
+               // UNSET_ONGROUND(this);
                if (this.move_movetype == MOVETYPE_NONE || this.move_movetype == MOVETYPE_FLY)
                        Movetype_Physics_NoMatchServer(this);
                // the trivial movetypes do not have to match the
@@ -72,9 +73,9 @@ void Projectile_Draw(entity this)
                // moving, we might still be ticrate dependent.
                else
                        Movetype_Physics_MatchServer(this, autocvar_cl_projectiles_sloppy);
-               if (!(this.move_flags & FL_ONGROUND))
+               if (!IS_ONGROUND(this))
                        if (this.velocity != '0 0 0')
-                               this.move_angles = this.angles = vectoangles(this.velocity);
+                               this.angles = vectoangles(this.velocity);
        }
        else
        {
@@ -209,9 +210,9 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
        // projectiles no longer being able to lie on a bmodel
        this.move_nomonsters = MOVE_WORLDONLY;
        if (f & 0x40)
-               this.move_flags |= FL_ONGROUND;
+               SET_ONGROUND(this);
        else
-               this.move_flags &= ~FL_ONGROUND;
+               UNSET_ONGROUND(this);
 
        if (!this.move_time)
        {
@@ -243,8 +244,6 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                                this.gravity = ReadCoord();
                        else
                                this.gravity = 0;  // none
-                       this.move_origin = this.origin;
-                       this.move_velocity = this.velocity;
                }
 
                if (time == this.spawntime || (this.count & 0x80) || (f & 0x08))
@@ -332,7 +331,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                this.maxs = '0 0 0';
                this.colormod = '0 0 0';
                settouch(this, SUB_Stop);
-               this.move_movetype = MOVETYPE_TOSS;
+               set_movetype(this, MOVETYPE_TOSS);
                this.alphamod = 1;
 
                switch (this.cnt)
@@ -340,12 +339,12 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                        case PROJECTILE_ELECTRO:
                                // only new engines support sound moving with object
                                loopsound(this, CH_SHOTS_SINGLE, SND(ELECTRO_FLY), VOL_BASE, ATTEN_NORM);
-                               this.mins = '0 0 -4';
-                               this.maxs = '0 0 -4';
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               this.mins = '-4 -4 -4';
+                               this.maxs = '4 4 4';
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
-                               this.move_bounce_factor = WEP_CVAR_SEC(electro, bouncefactor);
-                               this.move_bounce_stopspeed = WEP_CVAR_SEC(electro, bouncestop);
+                               this.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
+                               this.bouncestop = WEP_CVAR_SEC(electro, bouncestop);
                                break;
                        case PROJECTILE_RPC:
                        case PROJECTILE_ROCKET:
@@ -360,10 +359,10 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                        case PROJECTILE_GRENADE_BOUNCING:
                                this.mins = '-3 -3 -3';
                                this.maxs = '3 3 3';
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
-                               this.move_bounce_factor = WEP_CVAR(mortar, bouncefactor);
-                               this.move_bounce_stopspeed = WEP_CVAR(mortar, bouncestop);
+                               this.bouncefactor = WEP_CVAR(mortar, bouncefactor);
+                               this.bouncestop = WEP_CVAR(mortar, bouncestop);
                                break;
                        case PROJECTILE_SHAMBLER_LIGHTNING:
                                this.mins = '-8 -8 -8';
@@ -378,21 +377,21 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                        case PROJECTILE_PORTO_RED:
                                this.colormod = '2 1 1';
                                this.alphamod = 0.5;
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
                                break;
                        case PROJECTILE_PORTO_BLUE:
                                this.colormod = '1 1 2';
                                this.alphamod = 0.5;
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
                                break;
                        case PROJECTILE_HAGAR_BOUNCING:
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
                                break;
                        case PROJECTILE_CRYLINK_BOUNCING:
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
                                break;
                        case PROJECTILE_FIREBALL:
@@ -402,7 +401,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                                break;
                        case PROJECTILE_FIREMINE:
                                loopsound(this, CH_SHOTS_SINGLE, SND(FIREBALL_FLY), VOL_BASE, ATTEN_NORM);
-                               this.move_movetype = MOVETYPE_BOUNCE;
+                               set_movetype(this, MOVETYPE_BOUNCE);
                                settouch(this, func_null);
                                this.mins = '-4 -4 -4';
                                this.maxs = '4 4 4';
@@ -443,9 +442,9 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
                            this.mins = '0 0 -4';
                            this.maxs = '0 0 -4';
                            this.move_movetype = MOVETYPE_BOUNCE;
-                           this.move_touch = func_null;
-                           this.move_bounce_factor = WEP_CVAR_SEC(electro, bouncefactor);
-                           this.move_bounce_stopspeed = WEP_CVAR_SEC(electro, bouncestop);
+                           settouch(this, func_null);
+                           this.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
+                           this.bouncestop = WEP_CVAR_SEC(electro, bouncestop);
                            break;
                        */
                        default:
@@ -462,16 +461,16 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
        if (this.gravity)
        {
                if (this.move_movetype == MOVETYPE_FLY)
-                       this.move_movetype = MOVETYPE_TOSS;
+                       set_movetype(this, MOVETYPE_TOSS);
                if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
-                       this.move_movetype = MOVETYPE_BOUNCE;
+                       set_movetype(this, MOVETYPE_BOUNCE);
        }
        else
        {
                if (this.move_movetype == MOVETYPE_TOSS)
-                       this.move_movetype = MOVETYPE_FLY;
+                       set_movetype(this, MOVETYPE_FLY);
                if (this.move_movetype == MOVETYPE_BOUNCE)
-                       this.move_movetype = MOVETYPE_BOUNCEMISSILE;
+                       set_movetype(this, MOVETYPE_BOUNCEMISSILE);
        }
 
        if (!(this.count & 0x80))
@@ -479,6 +478,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
 
        this.classname = "csqcprojectile";
        this.draw = Projectile_Draw;
+       if (isnew) IL_PUSH(g_drawables, this);
        this.entremove = Ent_RemoveProjectile;
 }
 
index 66e1be4707629bffeee90a18f694373958a4a2b1..27bca00c80008daa7a9c943af478aa49245a2576 100644 (file)
@@ -16,7 +16,7 @@ class(Projectile).float gravity;
 class(Projectile).int snd_looping;
 class(Projectile).bool silent;
 
-void SUB_Stop(entity this);
+void SUB_Stop(entity this, entity toucher);
 
 void Projectile_ResetTrail(entity this, vector to);
 
index b16c24fb47086179068f433c7b93a057f28836c0..988413f7a9593f9e17efc9a51ad354ac823c71f4 100644 (file)
@@ -1,6 +1,6 @@
-float autocvar_net_connecttimeout = 30;
+noref float autocvar_net_connecttimeout = 30;
 
-#ifndef MENUQC
+#ifdef GAMEQC
 #include "anim.qc"
 #include "animdecide.qc"
 #include "ent_cs.qc"
@@ -19,19 +19,21 @@ float autocvar_net_connecttimeout = 30;
 #include "campaign_setup.qc"
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 #include "physics/all.inc"
 #include "triggers/include.qc"
 #include "viewloc.qc"
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 #include "minigames/minigames.qc"
 #endif
 
 #include "debug.qh"
 
-#ifndef MENUQC
+#include "command/_mod.inc"
+
+#ifdef GAMEQC
 #include "deathtypes/all.qc"
 #include "effects/all.qc"
 #include "impulses/all.qc"
@@ -40,8 +42,8 @@ float autocvar_net_connecttimeout = 30;
 #endif
 
 #include "items/_mod.inc"
-    #include "weapons/all.qc"
-        #include "monsters/all.qc"
+    #include "weapons/_all.inc"
+        #include "monsters/_mod.inc"
         #include "turrets/all.qc"
         #include "vehicles/all.qc"
 
index acf6735f37924df3ed97fb5602150d56ffa51831..84d59720d467c199bc2593aee5ce540ec4ad0903 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ANIM_H
-#define ANIM_H
+#pragma once
 
 // begin engine fields
 
@@ -46,5 +45,3 @@ void anim_set(entity e, vector anim, bool looping, bool override, bool restart);
 #define setanim(...) anim_set(__VA_ARGS__)
 void anim_update(entity e);
 #define updateanim(...) anim_update(__VA_ARGS__)
-
-#endif
index ab389278d16fcd696d850af106c59947b4108da6..b53a9ba0e9cbf6123d7438736785e8206200358a 100644 (file)
@@ -1,6 +1,6 @@
 #include "animdecide.qh"
 
-#include <common/monsters/all.qh>
+#include <common/monsters/_mod.qh>
 
 #if defined(SVQC)
     #include "util.qh"
index 39ca54ff2f7783514755385f4e3af49b557d54c9..16feb948c35d5c2264b9bdc21329c531c47ae2d9 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ANIMDECIDE_H
-#define ANIMDECIDE_H
+#pragma once
 
 // must be called at least once to initialize, or when modelindex is changed
 void animdecide_load_if_needed(entity e);
@@ -9,7 +8,7 @@ void animdecide_setimplicitstate(entity e, float onground);
 void animdecide_setframes(entity e, bool support_blending, .int fld_frame, .int fld_frame1time, .int fld_frame2, .int fld_frame2time);
 
 CLASS(Animation, Object)
-       ATTRIB(Animation, m_framenames, string, string_null)
+       ATTRIB(Animation, m_framenames, string);
        STATIC_METHOD(Animation, getframe, int(Animation this, int mdlidx))
        {
                FOREACH_WORD(this.m_framenames, true, {
@@ -17,7 +16,7 @@ CLASS(Animation, Object)
                        if (f != -1) return f;
                });
 #ifdef CSQC
-               LOG_DEBUGF("Missing animation for %s: %s\n", modelnameforindex(mdlidx), this.registered_id);
+               LOG_DEBUGF("Missing animation for %s: %s", modelnameforindex(mdlidx), this.registered_id);
 #endif
                return -1;
        }
@@ -145,4 +144,3 @@ const int ANIMACTION_PAIN2 = 3; // pain
 const int ANIMACTION_SHOOT = 4; // shoot
 const int ANIMACTION_TAUNT = 5; // taunt
 const int ANIMACTION_MELEE = 6; // melee
-#endif
index 3f494a4639b723a310a26682d7d743e05971eaa6..3bdc8725c2803b5c9c27f6f0f30c9841d6ea8294 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CAMPAIGN_COMMON_H
-#define CAMPAIGN_COMMON_H
+#pragma once
 
 #ifndef CAMPAIGN_MAX_ENTRIES
 #define CAMPAIGN_MAX_ENTRIES 64
@@ -31,4 +30,3 @@ void CampaignFile_Unload();
 // Sets up the campaign for the n-th array item (meaning: campaign_offset+nth
 // level) using localcmd()
 void CampaignSetup(float n);
-#endif
index a8bbe8e856c430a7fdfaf85e9ab5a4988c96a13d..af81942a2001753c36e9fb1cebdadc6ae283b2bb 100644 (file)
@@ -1,3 +1,4 @@
+#include "campaign_file.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
diff --git a/qcsrc/common/campaign_file.qh b/qcsrc/common/campaign_file.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 8c20c755c31a33881a6b37933d464b63ab5bb329..258d47f452bef14facee6dc58a693086b05caded 100644 (file)
@@ -1,3 +1,4 @@
+#include "campaign_setup.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
diff --git a/qcsrc/common/campaign_setup.qh b/qcsrc/common/campaign_setup.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d2d24f03d901cb0775702698267551e80eabb7d9..e3099980edfca432898b372a6086463f04680b88 100644 (file)
@@ -1,5 +1,5 @@
 // generated file; do not modify
-#include <common/command/all.qc>
 #include <common/command/generic.qc>
 #include <common/command/markup.qc>
+#include <common/command/reg.qc>
 #include <common/command/rpn.qc>
index 440bdcb13460d5486a642803de0604d0a82ba58d..6ca293773c6c51b42551693bd54b1ad397db4d52 100644 (file)
@@ -1,5 +1,5 @@
 // generated file; do not modify
-#include <common/command/all.qh>
 #include <common/command/generic.qh>
 #include <common/command/markup.qh>
+#include <common/command/reg.qh>
 #include <common/command/rpn.qh>
diff --git a/qcsrc/common/command/all.qc b/qcsrc/common/command/all.qc
deleted file mode 100644 (file)
index dc1c044..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "generic.qc"
-#include "markup.qc"
-#include "rpn.qc"
diff --git a/qcsrc/common/command/all.qh b/qcsrc/common/command/all.qh
deleted file mode 100644 (file)
index 129090d..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef COMMON_COMMANDS_ALL_H
-#define COMMON_COMMANDS_ALL_H
-
-#include "command.qh"
-REGISTRY(GENERIC_COMMANDS, BITS(7))
-#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL)
-REGISTER_REGISTRY(GENERIC_COMMANDS)
-REGISTRY_SORT(GENERIC_COMMANDS)
-
-#define GENERIC_COMMAND(id, description) \
-       CLASS(genericcommand_##id, Command) \
-               ATTRIB(genericcommand_##id, m_name, string, #id); \
-       ATTRIB(genericcommand_##id, m_description, string, description); \
-       ENDCLASS(genericcommand_##id) \
-    REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \
-       METHOD(genericcommand_##id, m_invokecmd, void(genericcommand_##id this, int request, entity caller, int arguments, string command))
-
-STATIC_INIT(GENERIC_COMMANDS_aliases) {
-       FOREACH(GENERIC_COMMANDS, true, localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_svmenu")));
-}
-
-#include "generic.qh"
-#include "markup.qh"
-#include "rpn.qh"
-
-#endif
index 72eaa18dabb3189c7185ced9e66eeb686ddab832..349d492da82b4169113ecde8989e764f06690f22 100644 (file)
@@ -1,16 +1,13 @@
-#ifndef COMMAND_H
-#define COMMAND_H
+#pragma once
 
 const int CMD_REQUEST_COMMAND = 1;
 const int CMD_REQUEST_USAGE = 2;
 
 CLASS(Command, Object)
-       ATTRIB(Command, m_name, string, string_null);
-       ATTRIB(Command, m_description, string, string_null);
+       ATTRIB(Command, m_name, string);
+       ATTRIB(Command, m_description, string);
        METHOD(Command, m_invokecmd, void(Command this, int request, entity caller, int arguments, string command))
        {
         TC(Command, this);
        }
 ENDCLASS(Command)
-
-#endif
index 90d6cefe7bb4d0ac7819f9eecdb5edd859ab966b..49a9d130989015125f171c6dedfa59561fd7ace2 100644 (file)
@@ -1,11 +1,13 @@
-#include "all.qh"
+#include "generic.qh"
+#include "_mod.qh"
+#include "reg.qh"
 
 #include "markup.qh"
 #include "rpn.qh"
 
 #include "../mapinfo.qh"
 
-#ifndef MENUQC
+#ifdef GAMEQC
        #include "../notifications/all.qh"
 #endif
 
 #endif
 
 #ifdef SVQC
-       #include <server/command/banning.qh>
-       #include <server/command/cmd.qh>
-       #include <server/command/common.qh>
-       #include <server/command/sv_cmd.qh>
+       #include <server/command/_mod.qh>
        #include <common/turrets/config.qh>
        #include <common/weapons/config.qh>
 #endif
@@ -36,7 +35,7 @@ void Curl_URI_Get_Callback(int id, float status, string data)
        string do_cvar = curl_uri_get_cvar[i];
        if(status != 0)
        {
-               LOG_TRACEF("error: status is %d\n", status);
+               LOG_TRACEF("error: status is %d", status);
                if(do_cvar)
                        strunzone(do_cvar);
                return;
@@ -374,7 +373,7 @@ void GenericCommand_restartnotifs(float request)
        {
                case CMD_REQUEST_COMMAND:
                {
-                       #ifndef MENUQC
+                       #ifdef GAMEQC
                        int NOTIF_ANNCE_COUNT   = 0; FOREACH(Notifications, it.nent_type == MSG_ANNCE,   { ++NOTIF_ANNCE_COUNT;  });
                        int NOTIF_INFO_COUNT    = 0; FOREACH(Notifications, it.nent_type == MSG_INFO,    { ++NOTIF_INFO_COUNT;   });
                        int NOTIF_CENTER_COUNT  = 0; FOREACH(Notifications, it.nent_type == MSG_CENTER,  { ++NOTIF_CENTER_COUNT; });
@@ -426,9 +425,9 @@ void GenericCommand_settemp(float request, float argc)
                        {
                                float f = cvar_settemp(argv(1), argv(2));
                                if(f == 1)
-                                       LOG_TRACE("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.\n");
+                                       LOG_TRACE("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.");
                                else if(f == -1)
-                                       LOG_TRACE("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".\n");
+                                       LOG_TRACE("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".");
                                // else cvar_settemp itself errors out
 
                                return;
@@ -456,9 +455,9 @@ void GenericCommand_settemp_restore(float request, float argc)
                        float i = cvar_settemp_restore();
 
                        if(i)
-                               LOG_TRACE("Restored ", ftos(i), " temporary cvar settings to their original values.\n");
+                               LOG_TRACE("Restored ", ftos(i), " temporary cvar settings to their original values.");
                        else
-                               LOG_TRACE("Nothing to restore.\n");
+                               LOG_TRACE("Nothing to restore.");
 
                        return;
                }
index f8139aaf47e4a894bb803645628683b1168d5c96..b39c79901457de6a77b18d42ed93ac5113d71d61 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_GENERIC_H
-#define COMMAND_GENERIC_H
+#pragma once
 
 #include <common/constants.qh>
 
@@ -39,4 +38,3 @@ void Curl_URI_Get_Callback(int id, float status, string data);
 int curl_uri_get_pos;
 float curl_uri_get_exec[URI_GET_CURL_END - URI_GET_CURL + 1];
 string curl_uri_get_cvar[URI_GET_CURL_END - URI_GET_CURL + 1];
-#endif
index 9f0883bd61ba0ea6696dc9a9e443f1f1e733addb..95a3b53c54b9950b7b7dc525669c40b583f08895 100644 (file)
@@ -1,5 +1,5 @@
-#include "command.qh"
 #include "markup.qh"
+#include "command.qh"
 
 // =========================================================
 //  Markup chat characters command code, reworked by Samual
@@ -53,7 +53,6 @@ void GenericCommand_markup_init()
        markup_from[i] = "&.."; markup_to[i] = "\x9e"; ++i;
        markup_from[i] = "&.)"; markup_to[i] = "\x9f"; ++i;
        markup_from[i] = "&<|"; markup_to[i] = "\xff"; ++i;
-       unused_float = i;
 }
 
 string GenericCommand_markup(string s2)
index bccc78d0bcd3537d43c4aaa1c91410d47fad7a26..7a1b3876af07e726b3d120678562214f399e2f3e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_MARKUP_H
-#define COMMAND_MARKUP_H
+#pragma once
 
 // ==========================================================
 //  Declarations for markup command code, reworked by Samual
@@ -12,4 +11,3 @@ string markup_from[NUM_MARKUPS];
 string markup_to[NUM_MARKUPS];
 
 string GenericCommand_markup(string s2);
-#endif
diff --git a/qcsrc/common/command/reg.qc b/qcsrc/common/command/reg.qc
new file mode 100644 (file)
index 0000000..c0af5b5
--- /dev/null
@@ -0,0 +1 @@
+#include "reg.qh"
diff --git a/qcsrc/common/command/reg.qh b/qcsrc/common/command/reg.qh
new file mode 100644 (file)
index 0000000..9868e24
--- /dev/null
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "command.qh"
+REGISTRY(GENERIC_COMMANDS, BITS(7))
+#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL)
+REGISTER_REGISTRY(GENERIC_COMMANDS)
+REGISTRY_SORT(GENERIC_COMMANDS)
+
+#define GENERIC_COMMAND(id, description) \
+       CLASS(genericcommand_##id, Command) \
+               ATTRIB(genericcommand_##id, m_name, string, #id); \
+       ATTRIB(genericcommand_##id, m_description, string, description); \
+       ENDCLASS(genericcommand_##id) \
+    REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \
+       METHOD(genericcommand_##id, m_invokecmd, void(genericcommand_##id this, int request, entity caller, int arguments, string command))
+
+STATIC_INIT(GENERIC_COMMANDS_aliases) {
+       FOREACH(GENERIC_COMMANDS, true, localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_svmenu")));
+}
index 12bb99d1d3e862b43904298c57627bb1e874b05c..f88bf635ee665b9628f0e69f0a6b1d1d58b430a8 100644 (file)
@@ -1,5 +1,5 @@
-#include "command.qh"
 #include "rpn.qh"
+#include "command.qh"
 
 
 // ========================================
index 3c5a8019b2a0cfee52c3ab8e9c500edde24563fa..ba028e248431806131679777bf116d8430d4f07a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMAND_RPN_H
-#define COMMAND_RPN_H
+#pragma once
 
 // =========================================================
 //  Declarations for RPN command code, written by divVerent
@@ -13,5 +12,3 @@ int rpn_sp;
 string rpn_stack[MAX_RPN_STACK];
 
 void GenericCommand_rpn(float request, float argc, string command);
-
-#endif
index 28db23dc11a13fd08b5365a30ca670e2b8638701..910f34e326eaaed24ab4462ef31b5d8321bff4c8 100644 (file)
@@ -1,60 +1,6 @@
-#ifndef CONSTANTS_H
-#define CONSTANTS_H
-
-REGISTER_NET_TEMP(TE_CSQC_PICTURE)
-REGISTER_NET_TEMP(TE_CSQC_RACE)
-REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER)
-REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT)
-REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN)
-REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP)
-
-const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
-const int RACE_NET_CHECKPOINT_CLEAR = 1;
-const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder
-const int RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent
-const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent
-const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder
-const int RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason
-const int RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason
-const int RACE_NET_SERVER_RECORD = 8; // server record, sent to client
-const int RACE_NET_SPEED_AWARD = 9; // speed award, sent to client
-const int RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client
-const int RACE_NET_SERVER_RANKINGS = 11;
-const int RACE_NET_SERVER_STATUS = 12;
-const int RANKINGS_CNT = 15;
+#pragma once
 
-REGISTER_NET_LINKED(_ENT_CLIENT_INIT)
-#ifdef CSQC
-NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { make_pure(this); return true; }
-#endif
-/** Sent as a temp entity from a persistent linked entity */
-REGISTER_NET_TEMP(ENT_CLIENT_INIT)
-
-REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO)
-REGISTER_NET_LINKED(ENT_CLIENT_SCORES)
-REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES)
-REGISTER_NET_LINKED(ENT_CLIENT_NAGGER) // flags [votecalledvote]
-REGISTER_NET_LINKED(ENT_CLIENT_RADARLINK) // flags [startorigin] [endorigin] [startcolor+16*endcolor]
-REGISTER_NET_LINKED(ENT_CLIENT_PROJECTILE)
-REGISTER_NET_LINKED(ENT_CLIENT_MAPVOTE)
-REGISTER_NET_LINKED(ENT_CLIENT_CLIENTDATA)
-REGISTER_NET_LINKED(ENT_CLIENT_RANDOMSEED)
-REGISTER_NET_LINKED(ENT_CLIENT_ACCURACY)
-REGISTER_NET_LINKED(ENT_CLIENT_ELIMINATEDPLAYERS)
-
-REGISTER_NET_LINKED(ENT_CLIENT_MODEL)
-
-REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE)
-REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_CAMERA)
-REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_TELEPORTED)
-
-REGISTER_NET_LINKED(ENT_CLIENT_ARC_BEAM)
-REGISTER_NET_LINKED(ENT_CLIENT_HOOK)
-REGISTER_NET_LINKED(ENT_CLIENT_TUBANOTE)
-
-REGISTER_NET_LINKED(ENT_CLIENT_SPAWNPOINT)
-REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT)
-REGISTER_NET_LINKED(ENT_CLIENT_WALL)
+const int RANKINGS_CNT = 15;
 
 const int SPRITERULE_DEFAULT = 0;
 const int SPRITERULE_TEAMPLAY = 1;
@@ -123,19 +69,114 @@ const int SFL_SORT_PRIO_SECONDARY = 4;
 const int SFL_SORT_PRIO_PRIMARY = 8;
 const int SFL_SORT_PRIO_MASK = 12;
 
-/**
+/*
  * Score indices
  */
-#define MAX_SCORE 12
+
+#ifdef GAMEQC
+
+#define IS_INCREASING(x) ( (x) & SFL_LOWER_IS_BETTER )
+#define IS_DECREASING(x) ( !((x) & SFL_LOWER_IS_BETTER) )
+
+
+#define MAX_SCORE 64
+
+#define REGISTER_SP(id) REGISTER(Scores, SP, id, m_id, new_pure(PlayerScoreField))
+REGISTRY(Scores, MAX_SCORE);
+#define Scores_from(i) _Scores_from(i, NULL)
+REGISTER_REGISTRY(Scores)
+REGISTRY_SORT(Scores);
+REGISTRY_CHECK(Scores);
+STATIC_INIT(Scores_renumber) { FOREACH(Scores, true, it.m_id = i); }
+
+USING(PlayerScoreField, entity);
+.int _scores[MAX_SCORE];
+.string m_name;
+.int m_flags;
+
+#define scores(this) _scores[(this).m_id]
+#define scores_label(this) ((this).m_name)
+#define scores_flags(this) ((this).m_flags)
+
+REGISTER_SP(END);
+
+REGISTER_SP(PING);
+REGISTER_SP(PL);
+REGISTER_SP(NAME);
+REGISTER_SP(KDRATIO);
+REGISTER_SP(SUM);
+
+REGISTER_SP(SEPARATOR);
+
+REGISTER_SP(SCORE);
+
+REGISTER_SP(DMG);
+REGISTER_SP(DMGTAKEN);
+
+REGISTER_SP(KILLS);
+REGISTER_SP(DEATHS);
+REGISTER_SP(SUICIDES);
+REGISTER_SP(FRAGS);
+
+REGISTER_SP(ELO);
+
+// TODO: move to common mutators
+
+REGISTER_SP(RACE_TIME);
+REGISTER_SP(RACE_LAPS);
+REGISTER_SP(RACE_FASTEST);
+
+//REGISTER_SP(CTS_TIME);
+//REGISTER_SP(CTS_LAPS);
+//REGISTER_SP(CTS_FASTEST);
+
+REGISTER_SP(ASSAULT_OBJECTIVES);
+
+REGISTER_SP(CTF_PICKUPS);
+REGISTER_SP(CTF_FCKILLS);
+REGISTER_SP(CTF_RETURNS);
+REGISTER_SP(CTF_CAPS);
+REGISTER_SP(CTF_CAPTIME);
+REGISTER_SP(CTF_DROPS);
+
+REGISTER_SP(DOM_TAKES);
+REGISTER_SP(DOM_TICKS);
+
+REGISTER_SP(FREEZETAG_REVIVALS);
+
+REGISTER_SP(KEEPAWAY_PICKUPS);
+REGISTER_SP(KEEPAWAY_BCTIME);
+REGISTER_SP(KEEPAWAY_CARRIERKILLS);
+
+REGISTER_SP(KH_PICKUPS);
+REGISTER_SP(KH_CAPS);
+REGISTER_SP(KH_KCKILLS);
+REGISTER_SP(KH_PUSHES);
+REGISTER_SP(KH_DESTROYS);
+REGISTER_SP(KH_LOSSES);
+
+REGISTER_SP(LMS_RANK);
+REGISTER_SP(LMS_LIVES);
+
+REGISTER_SP(NEXBALL_GOALS);
+REGISTER_SP(NEXBALL_FAULTS);
+
+REGISTER_SP(ONS_TAKES);
+REGISTER_SP(ONS_CAPS);
+
 #define MAX_TEAMSCORE 2
+USING(ScoreTeam, string);
+.int _teamscores[MAX_TEAMSCORE];
+#define teamscores(i) _teamscores[i]
+string _teamscores_label[MAX_TEAMSCORE];
+#define teamscores_label(i) _teamscores_label[i]
+int _teamscores_flags[MAX_TEAMSCORE];
+#define teamscores_flags(i) _teamscores_flags[i]
+
+#endif
 
 const int ST_SCORE = 0;
-const int SP_KILLS = 0;
-const int SP_DEATHS = 1;
-const int SP_SUICIDES = 2;
-const int SP_SCORE = 3;
-const int SP_DMG = 10;
-const int SP_DMGTAKEN = 11;
+
 // game mode specific indices are not in common/, but in server/scores_rules.qc!
 
 // WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc
@@ -192,7 +233,7 @@ const int SPECIES_RESERVED = 15;
 const int FRAGS_PLAYER = 0;
 const int FRAGS_SPECTATOR = -666;
 const int FRAGS_LMS_LOSER = -616;
-const int FRAGS_PLAYER_NONSOLID = -616;
+const int FRAGS_PLAYER_NONSOLID = FRAGS_LMS_LOSER;
 // we can use this frags value for both
 
 // water levels
@@ -206,6 +247,7 @@ const int SERVERFLAG_ALLOW_FULLBRIGHT = 1;
 const int SERVERFLAG_TEAMPLAY = 2;
 const int SERVERFLAG_PLAYERSTATS = 4;
 
+#ifdef SVQC
 // FIXME/EXPLAINME: why? Mario: because
 vector autocvar_sv_player_maxs = '16 16 45';
 vector autocvar_sv_player_mins = '-16 -16 -24';
@@ -213,7 +255,8 @@ vector autocvar_sv_player_viewoffset = '0 0 20';
 vector autocvar_sv_player_crouch_maxs = '16 16 25';
 vector autocvar_sv_player_crouch_mins = '-16 -16 -24';
 vector autocvar_sv_player_crouch_viewoffset = '0 0 20';
-vector autocvar_sv_player_headsize = '24 24 12';
+//vector autocvar_sv_player_headsize = '24 24 12';
+#endif
 
 
 // a bit more constant
@@ -230,4 +273,3 @@ const int SPAWN_PRIO_GOOD_DISTANCE = 10;
 const int GTV_FORBIDDEN = 0; // Cannot be voted
 const int GTV_AVAILABLE = 1; // Can be voted
 const int GTV_CUSTOM    = 2; // Custom entry
-#endif
index ff890afec629db9f70bf333cc0fbbc053659afc3..9c4909671e1408e5695fd572f7fbb6211de8c41e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CSQCMODEL_SETTINGS_H
-#define CSQCMODEL_SETTINGS_H
+#pragma once
 
 // define this if svqc code wants to use .frame2 and .lerpfrac
 //#define CSQCMODEL_HAVE_TWO_FRAMES
 # define TAG_VIEWLOC_NAME tag_networkviewloc
 # define TAG_VIEWLOC_TYPE int
 .float tag_networkviewloc;
-
-# define MOVETYPE_NAME move_movetype
 #else
 # define TAG_ENTITY_NAME tag_entity
 # define TAG_ENTITY_TYPE entity
 
 # define TAG_VIEWLOC_NAME viewloc
 # define TAG_VIEWLOC_TYPE entity
-
-# define MOVETYPE_NAME movetype
 #endif
 
 // new fields
@@ -68,7 +63,7 @@
        CSQCMODEL_PROPERTY(BIT(13), int, ReadInt24_t, WriteInt24_t, dphitcontentsmask) \
        CSQCMODEL_PROPERTY(BIT(14), TAG_VIEWLOC_TYPE, ReadShort, WriteEntity, TAG_VIEWLOC_NAME) \
        CSQCMODEL_PROPERTY(BIT(15), int, ReadByte, WriteByte, multijump_count) \
-       CSQCMODEL_PROPERTY(BIT(16), int, ReadByte, WriteByte, MOVETYPE_NAME)
+       CSQCMODEL_PROPERTY(BIT(16), int, ReadByte, WriteByte, move_movetype)
 // TODO get rid of colormod/glowmod here; also get rid of some useless properties on non-players that only exist for CopyBody
 
 // add hook function calls here
@@ -94,4 +89,3 @@
 #endif
 
 #define CSQCMODEL_EF_RESPAWNGHOST EF_SELECTABLE
-#endif
index 903087947416dfe6978a24900a72020fa3af3354..0466c230ab3b709f40343ce3d2bba3c2c3442894 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef DEATHTYPES_ALL_H
-#define DEATHTYPES_ALL_H
+#pragma once
 
 #include <common/notifications/all.qh>
 
@@ -47,5 +46,3 @@ const int DT_FIRST = BIT(13);
 string Deathtype_Name(int deathtype);
 
 #include "all.inc"
-
-#endif
index 9aca8e774c34a669cf3439f6656914c703b7a0a3..6d580bd2d69a8b65631a465d0adee730e6bca07d 100644 (file)
@@ -4,7 +4,7 @@
 .entity tag_entity;
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 .bool debug;
 .int sv_entnum;
 REGISTER_NET_TEMP(net_debug)
@@ -47,7 +47,7 @@ REGISTER_NET_TEMP(net_debug)
        }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 /**
  * 0: off
  * 1: on
@@ -100,7 +100,7 @@ bool autocvar_debugdraw;
 //                                             if (it.entnum) break;
 //                                             if (it.drawmask) break;
 //                                             if (it.predraw) break;
-//                                             if (it.movetype) break;
+//                                             if (it.move_movetype) break;
                                                if (it.solid) break;
 //                                             if (it.origin) break;
 //                                             if (it.oldorigin) break;
@@ -223,6 +223,40 @@ GENERIC_COMMAND(version, "Print the current version")
        }
 }
 
+#ifdef CSQC
+void(float bufhandle, string pattern, string antipattern) buf_cvarlist = #517;
+#endif
+GENERIC_COMMAND(cvar_localchanges, "Print locally changed cvars")
+{
+       switch (request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       string s = "";
+                       int h = buf_create();
+                       buf_cvarlist(h, "", "_"); // exclude all _ cvars as they are temporary
+                       int n = buf_getsize(h);
+                       for (int i = 0; i < n; ++i) {
+                               string k = bufstr_get(h, i);
+                               string v = cvar_string(k);
+                               string d = cvar_defstring(k);
+                               if (v == d)
+                                       continue;
+                               s = strcat(s, k, " \"", v, "\" // \"", d, "\"\n");
+                       }
+                       buf_del(h);
+                       LOG_INFO(s);
+                       return;
+               }
+               default:
+               case CMD_REQUEST_USAGE:
+               {
+                       LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " cvar_localchanges"));
+                       return;
+               }
+       }
+}
+
 REGISTER_STAT(TRACE_ENT, int)
 #ifdef SVQC
 bool autocvar_debugtrace;
@@ -275,6 +309,7 @@ STATIC_INIT(TRACE_ENT)
 {
        entity e = TRACE_ENT = new_pure(TRACE_ENT);
        e.draw2d = Trace_draw2d;
+       IL_PUSH(g_drawables_2d, e);
 }
 #endif
 
index 6975259c17783f683e86e5717d0198e1e0106f51..d5dab8cad27418e143cb9aa8bf63f962e061711e 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
 #include <common/effects/all.qc>
 #include <common/effects/effectinfo.qc>
+
+#include <common/effects/qc/_mod.inc>
index 3f5ed82198904e347d7ad9850c4fab8eb4e6c8a2..8d6e8ed18c98e59ad7e3943100732bcb6b862932 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
 #include <common/effects/all.qh>
 #include <common/effects/effectinfo.qh>
+
+#include <common/effects/qc/_mod.qh>
index e69a03f729cf534468c8559314a0ba1e714b3e7b..d229b05100a751fb50a8795dbc11997d52a953c9 100644 (file)
@@ -76,7 +76,7 @@ void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
        net_eff.eent_eff_trail = eff.eent_eff_trail;
 
        FOREACH_CLIENT(IS_REAL_CLIENT(it), Net_Write_Effect(net_eff, it, 0));
-       remove(net_eff);
+       delete(net_eff);
 }
 
 void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt)
@@ -91,4 +91,6 @@ void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt)
 }
 #endif
 
-#include "effectinfo.qc"
+#ifdef EFFECTINFO
+       #include "effectinfo.qc"
+#endif
index e0e9a3ca869d16b85734356e49802dba3fc706b9..7581618375bd5bfc0b949b0b023435db4bebdd8e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef EFFECTS_ALL_H
-#define EFFECTS_ALL_H
+#pragma once
 
 #include "effect.qh"
 
@@ -17,5 +16,3 @@ REGISTRY_CHECK(Effects)
 
 EFFECT(0, Null, string_null)
 #include "all.inc"
-
-#endif
index e58d42ca7b4ba55f9ed82af6b623bd1d21a6b883..7802f0a91be3f3ad464e41b69ad62d1a2b09c0da 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef EFFECT_H
-#define EFFECT_H
+#pragma once
 
 #define particleeffectnum(e) \
        _particleeffectnum(e.eent_eff_name)
@@ -32,5 +31,3 @@ entity Create_Effect_Entity(string eff_name, bool eff_trail)
        this.eent_eff_trail = eff_trail;
        return this;
 }
-
-#endif
index 07d76c480a07627cddc8b284c1e614c89e804e16..f662aee7b2a0e4b8d097537308cf96a98f6a1e41 100644 (file)
@@ -1,3 +1,4 @@
+#include "effectinfo.qh"
 #define EFFECTINFO_PARSER(on, MY) \
     on(type,                                        MY(type) \
     ,{ demand(n == 1 && "type");                    MY(type) = strzone(argv(1)); \
     /**/
 
 CLASS(EffectInfo, Object)
-    ATTRIB(EffectInfo, effectinfo_name, string, string_null)
+    ATTRIB(EffectInfo, effectinfo_name, string);
     CONSTRUCTOR(EffectInfo, string s) {
         CONSTRUCT(EffectInfo);
         this.effectinfo_name = s;
@@ -193,7 +194,7 @@ CLASS(EffectInfo, Object)
     MY(velocityoffset, vector, '0 0 0') \
     /**/
 
-    #define MY(f, type, val) ATTRIB(EffectInfo, effectinfo_##f, type, val)
+    #define MY(f, type, val) ATTRIB(EffectInfo, effectinfo_##f, type, val);
     FIELDS(MY)
     #undef MY
 
@@ -227,8 +228,8 @@ CLASS(EffectInfo, Object)
 ENDCLASS(EffectInfo)
 
 CLASS(EffectInfoGroup, Object)
-    ATTRIBARRAY(EffectInfoGroup, children, EffectInfo, 16)
-    ATTRIB(EffectInfoGroup, children_count, int, 0)
+    ATTRIBARRAY(EffectInfoGroup, children, EffectInfo, 16);
+    ATTRIB(EffectInfoGroup, children_count, int, 0);
 ENDCLASS(EffectInfoGroup)
 
 void effectinfo_read()
@@ -253,7 +254,7 @@ void effectinfo_read()
             #undef p
             #undef MY
             default:
-                LOG_WARNINGF("Unknown property '%s'\n", k);
+                LOG_WARNF("Unknown property '%s'", k);
                 break;
         }
     }
@@ -306,7 +307,7 @@ GENERIC_COMMAND(dumpeffectinfo, "Dump all effectinfo to effectinfo_dump.txt")
                                LOG_INFOF("Reload with ^2cl_particles_reloadeffects data/%s^7.\n", filename);
                 fclose(fh);
             } else {
-                LOG_WARNINGF("Could not open file '%s'!\n", filename);
+                LOG_WARNF("Could not open file '%s'!", filename);
             }
             return;
         }
diff --git a/qcsrc/common/effects/effectinfo.qh b/qcsrc/common/effects/effectinfo.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 5b9b364313cea105d933bd2516bb6abdaaaf9b9a..54cf6b6ff007b674b5b0225f74b054e61863fe43 100644 (file)
@@ -1,4 +1,3 @@
-#ifndef EFFECTS_QC
-#define EFFECTS_QC
+#pragma once
+
 #include "all.inc"
-#endif
index 605d42e671a86dd7cf55b020d720fb1d83ac64ac..d0befbb7ed04705feb92bb01e63d291011003bed 100644 (file)
@@ -1,5 +1,6 @@
+#include "casings.qh"
 #ifdef SVQC
-void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner);
+void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity);
 #endif
 
 #ifdef IMPLEMENTATION
@@ -14,9 +15,8 @@ void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float ran
 REGISTER_NET_TEMP(casings)
 
 #ifdef SVQC
-void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner)
+void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity)
 {
-    .entity weaponentity = weaponentities[0]; // TODO: parameter
     entity wep = casingowner.(weaponentity);
     vector org = casingowner.origin + casingowner.view_ofs + wep.spawnorigin.x * v_forward - wep.spawnorigin.y * v_right + wep.spawnorigin.z * v_up;
 
@@ -44,16 +44,16 @@ class(Casing) .float cnt;
 
 void Casing_Delete(entity this)
 {
-    remove(this);
+    delete(this);
 }
 
 void Casing_Draw(entity this)
 {
-    if (this.move_flags & FL_ONGROUND)
+    if (IS_ONGROUND(this))
     {
-        this.move_angles_x = 0;
-        this.move_angles_z = 0;
-        UNSET_ONGROUND(this);
+        this.angles_x = 0;
+        this.angles_z = 0;
+        //UNSET_ONGROUND(this);
     }
 
     Movetype_Physics_MatchTicrate(this, autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy);
@@ -83,7 +83,7 @@ Sound SND_CASINGS_RANDOM() {
     return Sounds_from(SND_CASINGS1.m_id + floor(prandom() * 3));
 }
 
-void Casing_Touch(entity this)
+void Casing_Touch(entity this, entity toucher)
 {
     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
     {
@@ -121,8 +121,8 @@ void Casing_Damage(entity this, float thisdmg, int hittype, vector org, vector t
 {
     if (thisforce.z < 0)
         thisforce.z = 0;
-    this.move_velocity = this.move_velocity + thisforce + '0 0 100';
-    this.move_flags &= ~FL_ONGROUND;
+    this.velocity = this.velocity + thisforce + '0 0 100';
+    UNSET_ONGROUND(this);
 }
 
 NET_HANDLE(casings, bool isNew)
@@ -151,11 +151,10 @@ NET_HANDLE(casings, bool isNew)
     casing.drawmask = MASK_NORMAL;
 
     casing.draw = Casing_Draw;
-    casing.move_origin = casing.origin;
-    casing.move_velocity = casing.velocity + 2 * prandomvec();
-    casing.move_angles = casing.angles;
-    casing.move_avelocity = '0 250 0' + 100 * prandomvec();
-    casing.move_movetype = MOVETYPE_BOUNCE;
+    if (isNew) IL_PUSH(g_drawables, casing);
+    casing.velocity = casing.velocity + 2 * prandomvec();
+    casing.avelocity = '0 250 0' + 100 * prandomvec();
+    set_movetype(casing, MOVETYPE_BOUNCE);
     settouch(casing, Casing_Touch);
     casing.move_time = time;
     casing.event_damage = Casing_Damage;
diff --git a/qcsrc/common/effects/qc/casings.qh b/qcsrc/common/effects/qc/casings.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c1144d2616c9b58991d5d6e5d5c1af89b340314e..71e1e2a7bcdd96dcf3f72629d7e147d4db03aab3 100644 (file)
@@ -1,15 +1,4 @@
-#ifndef DAMAGEEFFECTS_H
-#define DAMAGEEFFECTS_H
-
-#ifdef CSQC
-#include <common/deathtypes/all.qh>
-#include <common/physics/movetypes/movetypes.qh>
-#include <client/mutators/events.qh>
-#include <common/vehicles/all.qh>
-#include <common/weapons/all.qh>
-#endif
-
-#endif
+#include "damageeffects.qh"
 
 #ifdef IMPLEMENTATION
 
@@ -81,7 +70,7 @@ void DamageEffect_Think(entity this)
        {
                // time is up or the player got gibbed / disconnected
                this.owner.total_damages = max(0, this.owner.total_damages - 1);
-               remove(this);
+               delete(this);
                return;
        }
        if(this.state && !this.owner.csqcmodel_isdead)
@@ -89,7 +78,7 @@ void DamageEffect_Think(entity this)
                // if the player was dead but is now alive, it means he respawned
                // if so, clear his damage effects, or damages from his dead body will be copied back
                this.owner.total_damages = max(0, this.owner.total_damages - 1);
-               remove(this);
+               delete(this);
                return;
        }
        this.state = this.owner.csqcmodel_isdead;
@@ -255,8 +244,8 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew)
                if(it.damageforcescale)
                        if(vdist(thisforce, !=, 0))
                        {
-                               it.move_velocity = it.move_velocity + damage_explosion_calcpush(it.damageforcescale * thisforce, it.move_velocity, autocvar_g_balance_damagepush_speedfactor);
-                               it.move_flags &= ~FL_ONGROUND;
+                               it.velocity = it.velocity + damage_explosion_calcpush(it.damageforcescale * thisforce, it.velocity, autocvar_g_balance_damagepush_speedfactor);
+                               UNSET_ONGROUND(it);
                        }
 
                if(w_issilent)
diff --git a/qcsrc/common/effects/qc/damageeffects.qh b/qcsrc/common/effects/qc/damageeffects.qh
new file mode 100644 (file)
index 0000000..2a1d587
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+#ifdef CSQC
+#include <common/deathtypes/all.qh>
+#include <common/physics/movetypes/movetypes.qh>
+#include <client/mutators/events.qh>
+#include <common/vehicles/all.qh>
+#include <common/weapons/_all.qh>
+#endif
index bc82b50d0c8e955355afaf0ed491ea0cca7e73c0..d1e3d9880da4244206e9ea38ef037c38a828653b 100644 (file)
@@ -46,7 +46,7 @@ void Violence_GibSplash_At(vector org, vector dir, float type, float amount, ent
        e.oldorigin_x = compressShortVector(e.velocity);
 
        FOREACH_CLIENT(IS_REAL_CLIENT(it), Violence_GibSplash_SendEntity(e, it, 0));
-       remove(e);
+       delete(e);
 }
 
 void Violence_GibSplash(entity source, float type, float amount, entity attacker)
@@ -70,7 +70,7 @@ void Violence_GibSplash(entity source, float type, float amount, entity attacker
 
 void Gib_Delete(entity this)
 {
-       remove(this);
+       delete(this);
 }
 
 string species_prefix(int specnum);
@@ -111,13 +111,13 @@ void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany)
                __pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50);
 }
 
-void SUB_RemoveOnNoImpact(entity this)
+void SUB_RemoveOnNoImpact(entity this, entity toucher)
 {
        if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
                Gib_Delete(this);
 }
 
-void Gib_Touch(entity this)
+void Gib_Touch(entity this, entity toucher)
 {
        // TODO maybe bounce of walls, make more gibs, etc.
 
@@ -170,7 +170,7 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector
 
        // TODO remove some gibs according to cl_nogibs
        gib = RubbleNew("gib");
-       gib.move_movetype = MOVETYPE_BOUNCE;
+       set_movetype(gib, MOVETYPE_BOUNCE);
        gib.gravity = 1;
        gib.solid = SOLID_CORPSE;
        gib.cnt = specnum;
@@ -180,6 +180,7 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector
        setsize (gib, '-8 -8 -8', '8 8 8');
 
        gib.draw = Gib_Draw;
+       IL_PUSH(g_drawables, gib);
        if(destroyontouch)
                settouch(gib, Gib_Touch);
        else
@@ -192,10 +193,9 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector
                org = trace_endpos;
        }
 
-       gib.move_origin = org;
        setorigin(gib, org);
-       gib.move_velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up;
-       gib.move_avelocity = prandomvec() * vlen(gib.move_velocity) * autocvar_cl_gibs_avelocity_scale;
+       gib.velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up;
+       gib.avelocity = prandomvec() * vlen(gib.velocity) * autocvar_cl_gibs_avelocity_scale;
        gib.move_time = time;
        gib.damageforcescale = autocvar_cl_gibs_damageforcescale;
 
@@ -311,7 +311,7 @@ NET_HANDLE(net_gibsplash, bool isNew)
                        // no gibs in gentle mode, sorry
                        break;
        }
-       remove(this);
+       delete(this);
 }
 #endif
 
index a2653238e7ebea30ad5a22f26203adf9156c66e1..d60b2e8a65892bd33f4d0b285ce00328b2b095a0 100644 (file)
@@ -6,7 +6,7 @@
        #include <common/animdecide.qh>
 
        #ifdef SVQC
-               #include <server/cl_player.qh>
+               #include <server/player.qh>
        #endif
 
        REGISTER_NET_TEMP(globalsound)
                        else
                        {
                                // Can this happen?
-                               LOG_WARNINGF("Missing entcs data for player %d\n", who);
+                               LOG_WARNF("Missing entcs data for player %d", who);
                                sound8(e, o, chan, sample, vol, atten, 0, 0);
                        }
                        return true;
                        else
                        {
                                // Can this happen?
-                               LOG_WARNINGF("Missing entcs data for player %d\n", who);
+                               LOG_WARNF("Missing entcs data for player %d", who);
                                sound8(e, o, chan, sample, vol, atten, 0, 0);
                        }
                        return true;
                int fh = fopen(f, FILE_READ);
                if (fh < 0)
                {
-                       LOG_WARNINGF("Player sound file not found: %s\n", f);
+                       LOG_WARNF("Player sound file not found: %s", f);
                        return;
                }
                for (string s; (s = fgets(fh)); )
                        int n = tokenize_console(s);
                        if (n != 3)
                        {
-                               if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s);
+                               if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
                                continue;
                        }
                        string file = argv(1);
                        int fh = fopen(f, FILE_READ);
                        if (fh < 0)
                        {
-                               if (strict) LOG_WARNINGF("Player sound file not found: %s\n", f);
+                               if (strict) LOG_WARNF("Player sound file not found: %s", f);
                                return false;
                        }
                        for (string s; (s = fgets(fh)); )
                                int n = tokenize_console(s);
                                if (n != 3)
                                {
-                                       if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s);
+                                       if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
                                        continue;
                                }
                                string key = argv(0);
                                if (GetPlayerSoundSampleField_notFound) field = GetVoiceMessageSampleField(key);
                                if (GetPlayerSoundSampleField_notFound)
                                {
-                                       LOG_TRACEF("Invalid sound info field: %s\n", key);
+                                       LOG_TRACEF("Invalid sound info field: %s", key);
                                        continue;
                                }
                                string file = argv(1);
 
        #ifdef SVQC
 
-               void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, int voicetype, bool fake)
+               void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake)
                {
                        if (gs == NULL && ps == NULL && sample == "") return;
                        if(this.classname == "body") return;
                                                if (IS_REAL_CLIENT(msg_entity))
                                                {
                                                        float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE;
-                                                       if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten);
-                                                       else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten);
-                                                       else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten);
+                                                       if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten);
+                                                       else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten);
+                                                       else soundto(MSG_ONE, this, chan, sample, vol, atten);
                                                }
                                        }
                                        if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break;
                                                MACRO_BEGIN \
                                                { \
                                                        float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \
-                                                       if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \
-                                                       else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); \
-                                                       else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \
+                                                       if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); \
+                                                       else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); \
+                                                       else soundto(MSG_ONE, this, chan, sample, vol, atten); \
                                                } MACRO_END
 
                                        if (fake) { msg_entity = this; X(); }
                                        else
                                        {
-                                               FOREACH_CLIENT(IS_REAL_CLIENT(it) && (!teamplay || msg_entity.team == this.team), {
+                                               FOREACH_CLIENT(IS_REAL_CLIENT(it) && SAME_TEAM(it, this), {
                                                        msg_entity = it;
                                                        X();
                                                });
                                                                    ? bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, \
                                                                        ATTEN_MAX) \
                                                                        : ATTEN_NONE; \
-                                                               if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \
-                                                               else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); \
-                                                               else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \
+                                                               if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); \
+                                                               else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); \
+                                                               else soundto(MSG_ONE, this, chan, sample, vol, atten); \
                                                        } \
                                                } MACRO_END
                                        if (fake)
                                        msg_entity = this;
                                        if (fake)
                                        {
-                                               if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NORM);
-                                               else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NORM);
-                                               else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NORM);
+                                               if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, ATTEN_NORM);
+                                               else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, ATTEN_NORM);
+                                               else soundto(MSG_ONE, this, chan, sample, vol, ATTEN_NORM);
                                        }
                                        else
                                        {
-                                               if (gs) globalsound(MSG_ALL, this, gs, r, chan, VOL_BASE, ATTEN_NORM);
-                                               else if (ps) playersound(MSG_ALL, this, ps, r, chan, VOL_BASE, ATTEN_NORM);
-                                               else _sound(this, chan, sample, VOL_BASE, ATTEN_NORM);
+                                               if (gs) globalsound(MSG_ALL, this, gs, r, chan, vol, ATTEN_NORM);
+                                               else if (ps) playersound(MSG_ALL, this, ps, r, chan, vol, ATTEN_NORM);
+                                               else _sound(this, chan, sample, vol, ATTEN_NORM);
                                        }
                                        break;
                                }
index 44925cd510cf81c71fe4018b792de9a2f46bda22..1df0b1a42b1145eeeea104092fec1a98d5b5b25d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef GLOBALSOUND_H
-#define GLOBALSOUND_H
+#pragma once
 
 #ifdef SVQC
        /** Use new sound handling. TODO: use when sounds play correctly on clients */
@@ -122,10 +121,10 @@ entity GetVoiceMessage(string type);
 
 #ifdef SVQC
 
-       void _GlobalSound(entity this, entity gs, entity ps, string sample, float chan, float voicetype, bool fake);
-       #define GlobalSound(this, def, chan, voicetype) _GlobalSound(this, def, NULL, string_null, chan, voicetype, false)
-       #define GlobalSound_string(this, def, chan, voicetype) _GlobalSound(this, NULL, NULL, def, chan, voicetype, false)
-       #define PlayerSound(this, def, chan, voicetype) _GlobalSound(this, NULL, def, string_null, chan, voicetype, false)
+       void _GlobalSound(entity this, entity gs, entity ps, string sample, float chan, float vol, float voicetype, bool fake);
+       #define GlobalSound(this, def, chan, vol, voicetype) _GlobalSound(this, def, NULL, string_null, chan, vol, voicetype, false)
+       #define GlobalSound_string(this, def, chan, vol, voicetype) _GlobalSound(this, NULL, NULL, def, chan, vol, voicetype, false)
+       #define PlayerSound(this, def, chan, vol, voicetype) _GlobalSound(this, NULL, def, string_null, chan, vol, voicetype, false)
        #define VoiceMessage(this, def, msg) \
                MACRO_BEGIN \
                { \
@@ -137,7 +136,7 @@ entity GetVoiceMessage(string type);
                        if (IS_SPEC(this) || IS_OBSERVER(this) || flood < 0) fake = true; \
                        else if (flood > 0) fake = false; \
                        else break; \
-                       _GlobalSound(this, NULL, VM, string_null, CH_VOICE, voicetype, fake); \
+                       _GlobalSound(this, NULL, VM, string_null, CH_VOICE, VOL_BASEVOICE, voicetype, fake); \
                } MACRO_END
 
 #endif
@@ -148,5 +147,3 @@ STATIC_INIT(allvoicesamples)
     FOREACH(PlayerSounds, it.instanceOfVoiceMessage, allvoicesamples = strcat(allvoicesamples, " ", it.m_playersoundstr));
     allvoicesamples = strzone(substring(allvoicesamples, 1, -1));
 }
-
-#endif
index f577762906b6737427a9f0edd2289123f78729ac..8fbef5b587e9c56a7ed1f7cbc20d445cd32dab4f 100644 (file)
@@ -103,7 +103,7 @@ void ModelEffect_Draw(entity this)
        this.alpha = this.cnt * bound(0, 1 - (time - this.lifetime) / this.fadetime, 1);
        if(this.alpha < ALPHA_MIN_VISIBLE)
        {
-               remove(this);
+               delete(this);
                return;
        }
        this.drawmask = MASK_NORMAL;
@@ -156,8 +156,9 @@ NET_HANDLE(ENT_CLIENT_MODELEFFECT, bool isnew)
        e.cnt = ReadByte() / 255.0; // actually alpha
 
        e.draw = ModelEffect_Draw;
+       if (isnew) IL_PUSH(g_drawables, e);
 
-       if (!isnew) remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then
+       if (!isnew) delete(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then
        return true;
 }
 #endif
index 7848b7b1f3a31d34d6069c9920ed81359f608952..dd3785b68ea2cf65af3fb5ecc5c77f86c0b102e0 100644 (file)
@@ -1,41 +1,34 @@
-#ifndef RUBBLE_H
-#define RUBBLE_H
+#pragma once
 
 #ifdef CSQC
 
 entityclass(Rubble);
 class(Rubble).float creationtime;
 
-void RubbleLimit(string cname, float limit, void(entity) deleteproc)
-{
-       entity e;
-       entity oldest;
-       float c;
-       float oldesttime;
+IntrusiveList g_rubble;
+STATIC_INIT(g_rubble) { g_rubble = IL_NEW(); }
 
+void RubbleLimit(string cname, int limit, void(entity) deleteproc)
+{
        // remove rubble of the same type if it's at the limit
        // remove multiple rubble if the limit has been decreased
        while (1)
        {
-               e = findchain(classname, cname);
-               if (e == NULL) break;
                // walk the list and count the entities, find the oldest
                // initialize our search with the first entity
-               c = 1;
-               oldest = e;
-               oldesttime = e.creationtime;
-               e = e.chain;
+               int c = 0;
+               entity oldest = NULL;
+               float oldesttime = 0;
                // compare to all other matching entities
-               while (e)
+               IL_EACH(g_rubble, it.classname == cname,
                {
-                       c = c + 1;
-                       if (oldesttime > e.creationtime)
+                       ++c;
+                       if(!oldest || oldesttime > it.creationtime)
                        {
-                               oldesttime = e.creationtime;
-                               oldest = e;
+                               oldest = it;
+                               oldesttime = it.creationtime;
                        }
-                       e = e.chain;
-               }
+               });
 
                // stop if there are less than the limit already
                if (c <= limit) break;
@@ -51,9 +44,8 @@ entity RubbleNew(string cname)
        entity e = spawn();
        e.classname = cname;
        e.creationtime = time;
+       IL_PUSH(g_rubble, e);
        return e;
 }
 
 #endif
-
-#endif
index cafef4868b540cd274de8d2f253fd113a6d43623..5a5c6acbebfefced8df45a82313944e36e49d496 100644 (file)
@@ -1,39 +1,53 @@
 #include "ent_cs.qh"
 
-// #define PROP(public, fld, sv, cl)
-#define ENTCS_NETPROPS(PROP) \
-       PROP(true, sv_entnum, \
-       { WriteByte(chan, etof(player) - 1); }, \
-       { this.sv_entnum = ReadByte(); }) \
+#define ENTCS_SET_NORMAL(var, x) MACRO_BEGIN \
+       var = x; \
+MACRO_END
+
+/** the engine player name strings are mutable! */
+#define ENTCS_SET_MUTABLE_STRING(var, x) MACRO_BEGIN \
+       if (var) strunzone(var); \
+       var = strzone(x); \
+MACRO_END
+
+// #define PROP(public, fld, set, sv, cl)
+#define ENTCS_NETPROPS(ent, PROP) PROP(false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */ \
+       PROP(false, origin, ENTCS_SET_NORMAL, \
+       { WriteCoord(chan, ent.origin.x);  WriteCoord(chan, ent.origin.y); \
+         WriteCoord(chan, ent.origin.z); }, \
+       { ent.has_sv_origin = true; vector v; v.x = ReadCoord(); v.y = ReadCoord(); v.z = ReadCoord(); setorigin(ent, v); }) \
     \
-       PROP(false, origin, \
-       { WriteShort(chan, this.origin.x);  WriteShort(chan, this.origin.y); \
-         WriteShort(chan, this.origin.z); }, \
-       { this.has_sv_origin = true; vector v; v.x = ReadShort(); v.y = ReadShort(); v.z = ReadShort(); setorigin(this, v); }) \
+       PROP(false, angles_y, ENTCS_SET_NORMAL, \
+       { WriteByte(chan, ent.angles.y / 360 * 256); }, \
+       { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; ent.angles = v; }) \
     \
-       PROP(false, angles_y, \
-       { WriteByte(chan, this.angles.y / 360 * 256); }, \
-       { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; this.angles = v; }) \
+       PROP(false, health, ENTCS_SET_NORMAL, \
+       { WriteByte(chan, bound(0, ent.health / 10, 255));  /* FIXME: use a better scale? */ }, \
+       { ent.healthvalue = ReadByte() * 10; }) \
     \
-       PROP(false, health, \
-       { WriteByte(chan, bound(0, this.health / 10, 255));  /* FIXME: use a better scale? */ }, \
-       { this.healthvalue = ReadByte() * 10; }) \
+       PROP(false, armorvalue, ENTCS_SET_NORMAL, \
+       { WriteByte(chan, bound(0, ent.armorvalue / 10, 255));  /* FIXME: use a better scale? */ }, \
+       { ent.armorvalue = ReadByte() * 10; }) \
     \
-       PROP(false, armorvalue, \
-       { WriteByte(chan, bound(0, this.armorvalue / 10, 255));  /* FIXME: use a better scale? */ }, \
-       { this.armorvalue = ReadByte() * 10; }) \
+       PROP(true, netname, ENTCS_SET_MUTABLE_STRING, \
+       { WriteString(chan, ent.netname); }, \
+       { if (ent.netname) strunzone(ent.netname); ent.netname = strzone(ReadString()); }) \
     \
-       PROP(true, netname, \
-       { WriteString(chan, this.netname); }, \
-       { if (this.netname) strunzone(this.netname); this.netname = strzone(ReadString()); }) \
+       PROP(true, model, ENTCS_SET_NORMAL, \
+       { WriteString(chan, ent.model); }, \
+       { if (ent.model) strunzone(ent.model); ent.model = strzone(ReadString()); }) \
     \
-       PROP(true, model, \
-       { WriteString(chan, this.model); }, \
-       { if (this.model) strunzone(this.model); this.model = strzone(ReadString()); }) \
+       PROP(true, skin, ENTCS_SET_NORMAL, \
+       { WriteByte(chan, ent.skin); }, \
+       { ent.skin = ReadByte(); }) \
     \
-       PROP(true, skin, \
-       { WriteByte(chan, this.skin); }, \
-       { this.skin = ReadByte(); }) \
+    PROP(true, clientcolors, ENTCS_SET_NORMAL, \
+       { WriteByte(chan, ent.clientcolors); }, \
+       { ent.colormap = ReadByte(); }) \
+    \
+    PROP(true, frags, ENTCS_SET_NORMAL, \
+       { WriteShort(chan, ent.frags); }, \
+       { ent.frags = ReadShort(); }) \
     \
        /**/
 
        int ENTCS_PUBLICMASK = 0;
        STATIC_INIT(ENTCS_PUBLICMASK)
        {
-               int i = 1;
-               #define X(public, fld, sv, cl) { if (public) ENTCS_PUBLICMASK |= BIT(i); } i += 1;
-               ENTCS_NETPROPS(X);
+               int i = 0;
+               #define X(public, fld, set, sv, cl) { \
+                       if (public) { \
+                               ENTCS_PUBLICMASK |= BIT(i); \
+                       } \
+                       i += 1; \
+               }
+               ENTCS_NETPROPS(this, X);
        #undef X
                if (i >= BITS(16 - 1)) LOG_FATAL("Exceeded ENTCS_NETPROPS limit");
        }
        bool _entcs_send(entity this, entity to, int sf, int chan)
        {
                entity player = this.owner;
-               sf |= BIT(0) | BIT(1);
-               if (IS_PLAYER(to) || to.caplayer)                                  // unless spectating,
-               {
-                       bool same_team = (to == player) || (teamplay && player.team == to.team);
-                       if (!same_team && !radar_showennemies) sf &= ENTCS_PUBLICMASK; // no private updates
-               }
+               sf |= BIT(0); // assume private
+               do {
+                       if (!(IS_PLAYER(player)))
+                       {
+                               sf &= ENTCS_PUBLICMASK; // no private updates
+                               break;
+                       }
+                       if (radar_showennemies) break;
+                       if (SAME_TEAM(to, player)) break;
+                       if (!(IS_PLAYER(to) || to.caplayer) && time > game_starttime) break;
+                       sf &= ENTCS_PUBLICMASK; // no private updates
+               } while (0);
                sf |= this.m_forceupdate;
                this.m_forceupdate = 0;
-               bool valid =
-                   IS_PLAYER(player)             // player must be active
-                   || player == to               // player is self
-               ;
-               if (!valid) sf = 0;
                if (chan == MSG_ENTITY)
                        WriteHeader(chan, ENT_CLIENT_ENTCS);
                else
                        WriteHeader(chan, CLIENT_ENTCS);
                WriteByte(chan, etof(player) - 1);
                WriteShort(chan, sf);
-               int i = 1;
-               #define X(public, fld, sv, cl) { if (sf & BIT(i)) sv; } i += 1;
-               ENTCS_NETPROPS(X);
+               int i = 0;
+               #define X(public, fld, set, sv, cl) { \
+                       if (sf & BIT(i)) { \
+                               sv; \
+                       } \
+                       i += 1; \
+               }
+               ENTCS_NETPROPS(this, X);
        #undef X
                return true;
        }
        {
                this.nextthink = time + 0.033333333333;  // TODO: increase this to like 0.15 once the client can do smoothing
                entity o = this.owner;
-               int i = 1;
-               #define X(public, fld, sv, cl) \
-                       if (o.fld != this.fld) \
-                       { \
-                               this.fld = o.fld; \
+               int i = 0;
+               #define X(public, fld, set, sv, cl) { \
+                       if (o.fld != this.fld) { \
+                               set(this.fld, o.fld); \
                                this.SendFlags |= BIT(i); \
                        } \
-                       i += 1;
-               ENTCS_NETPROPS(X);
+                       i += 1; \
+               }
+               ENTCS_NETPROPS(this, X);
        #undef X
            setorigin(this, this.origin);  // relink
        }
        void entcs_detach(entity player)
        {
                if (!player.entcs) return;
-               remove(player.entcs);
+               delete(player.entcs);
                player.entcs = NULL;
        }
 
                int n = this.sv_entnum;
                entity e = entcs_receiver(n);
                entcs_receiver(n, NULL);
-               if (e != this) remove(e);
+               if (e != this) delete(e);
        }
 
        void entcs_think(entity this)
                entity e = entcs_receiver(n);
                if (e == NULL)
                {
-                       if (this)
-                       {
-                               e = this;
-                       }
+                       if (!this)
+                               // initial = temp
+                               e = new_pure(entcs_receiver);
                        else
-                       {
-                               e = new(entcs_receiver);
-                               make_pure(e);
-                       }
-                       e.sv_entnum = n;
+                               // initial = linked
+                               e = this;
                        setthink(e, entcs_think);
                        entcs_receiver(n, e);
                }
-               else if (this && e != this)
+               else if (e != this && this)
                {
-                       this.classname = "entcs_gc";
-                       this.sv_entnum = n;
+                       // upgrade to linked
+                       delete(e);
+                       e = this;
+                       setthink(e, entcs_think);
+                       entcs_receiver(n, e);
                }
-               this = e;
-               InterpolateOrigin_Undo(this);
-               this.sv_entnum = n;
+
+               InterpolateOrigin_Undo(e);
+               e.sv_entnum = n;
                int sf = ReadShort();
-               this.has_sv_origin = false;
-               this.m_entcs_private = boolean(sf & BIT(0));
-               int i = 1;
-               #define X(public, fld, sv, cl) { if (sf & BIT(i)) cl; } i += 1;
-               ENTCS_NETPROPS(X);
+               e.has_sv_origin = false;
+               e.m_entcs_private = boolean(sf & BIT(0));
+               int i = 0;
+               #define X(public, fld, set, sv, cl) { \
+                       if (sf & BIT(i)) { \
+                               cl; \
+                       } \
+                       i += 1; \
+               }
+               ENTCS_NETPROPS(e, X);
        #undef X
-               this.iflags |= IFLAG_ORIGIN;
-               InterpolateOrigin_Note(this);
-               getthink(this)(this);
+               e.iflags |= IFLAG_ORIGIN;
+               InterpolateOrigin_Note(e);
+               getthink(e)(e);
                return true;
        }
 
index 1c72b351f7ff7e4e65b48fd9e5bf599f86c11a40..65cdd83d3a71f4f5df325a8e90b727d936cb1300 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ENT_CS_H
-#define ENT_CS_H
+#pragma once
 
 REGISTER_NET_LINKED(ENT_CLIENT_ENTCS)
 REGISTER_NET_TEMP(CLIENT_ENTCS)
@@ -59,10 +58,21 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
        /**
      * @param i zero indexed player
      */
+    .int frags;
        bool entcs_IsSpectating(int i)
        {
                bool unconnected = !playerslots[i].gotscores;
-               return unconnected || stof(getplayerkeyvalue(i, "frags")) == FRAGS_SPECTATOR;
+               entity e = entcs_receiver(i);
+               return unconnected || ((e) ? e.frags : stof(getplayerkeyvalue(i, "frags"))) == FRAGS_SPECTATOR;
+       }
+
+       /**
+     * @param i zero indexed player
+     */
+       int entcs_GetClientColors(int i)
+       {
+               entity e = entcs_receiver(i);
+               return e ? e.colormap : stof(getplayerkeyvalue(i, "colors"));
        }
 
        /**
@@ -71,7 +81,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
        */
        int entcs_GetTeamColor(int i)
        {
-               return (!teamplay) ? 0 : stof(getplayerkeyvalue(i, "colors")) & 15;
+               return (!teamplay) ? 0 : entcs_GetClientColors(i) & 15;
        }
 
        /**
@@ -98,7 +108,8 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
        */
        string entcs_GetName(int i)
        {
-               return ColorTranslateRGB(getplayerkeyvalue(i, "name"));
+               entity e = entcs_receiver(i);
+               return ColorTranslateRGB(e ? e.netname : getplayerkeyvalue(i, "name"));
        }
 
     /**
@@ -127,7 +138,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
                       ? '1 1 1'
                           : colormapPaletteColor(((e.colormap >= 1024)
                        ? e.colormap
-                       : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 15, true)
+                       : entcs_GetClientColors(e.colormap - 1)) & 15, true)
                ;
        }
 
@@ -141,5 +152,3 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
        }
 
 #endif
-
-#endif
index 0b779498b186ad5a57ba3373155f79baa6969a22..c3cec69dc5268cddd04a9afb11e422e4bf3b3a71 100644 (file)
@@ -1,2 +1,3 @@
 // generated file; do not modify
-#include <common/gamemodes/all.qc>
+
+#include <common/gamemodes/gamemode/_mod.inc>
index a7b7a54af44b8c2777885d56b7d38a48911ba5e8..685c277b4e18bf7771f2852d40138ad39dc14063 100644 (file)
@@ -1,2 +1,3 @@
 // generated file; do not modify
-#include <common/gamemodes/all.qh>
+
+#include <common/gamemodes/gamemode/_mod.qh>
diff --git a/qcsrc/common/gamemodes/all.inc b/qcsrc/common/gamemodes/all.inc
deleted file mode 100644 (file)
index bcdcd7c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "gamemode/nexball/module.inc"
-#include "gamemode/onslaught/module.inc"
diff --git a/qcsrc/common/gamemodes/all.qc b/qcsrc/common/gamemodes/all.qc
deleted file mode 100644 (file)
index f0fc719..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "all.qh"
-
-#define IMPLEMENTATION
-#include "all.inc"
-#undef IMPLEMENTATION
diff --git a/qcsrc/common/gamemodes/all.qh b/qcsrc/common/gamemodes/all.qh
deleted file mode 100644 (file)
index 62ba616..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef GAMEMODES_ALL_H
-#define GAMEMODES_ALL_H
-
-#include "all.inc"
-
-#endif
index 98fb4815c1ce28cc699a429537ea75c2643b4487..2fc2c404678883117dcd9205365e36317f5e997e 100644 (file)
@@ -1 +1,4 @@
 // generated file; do not modify
+
+#include <common/gamemodes/gamemode/nexball/_mod.inc>
+#include <common/gamemodes/gamemode/onslaught/_mod.inc>
index 98fb4815c1ce28cc699a429537ea75c2643b4487..d79957012609493478bdf9e0a03ea2116fec63c3 100644 (file)
@@ -1 +1,4 @@
 // generated file; do not modify
+
+#include <common/gamemodes/gamemode/nexball/_mod.qh>
+#include <common/gamemodes/gamemode/onslaught/_mod.qh>
diff --git a/qcsrc/common/gamemodes/gamemode/nexball/module.inc b/qcsrc/common/gamemodes/gamemode/nexball/module.inc
deleted file mode 100644 (file)
index 0d80962..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "nexball.qc"
-#include "weapon.qc"
index 46b4e034558b69f45e150d03a8cbf732155dd197..d67ce5ff070ed640bfa70c342907e803616951ec 100644 (file)
@@ -1,6 +1,5 @@
 #include "nexball.qh"
 
-#ifdef IMPLEMENTATION
 #ifdef CSQC
 int autocvar_cl_eventchase_nexball = 1;
 
@@ -35,6 +34,7 @@ float autocvar_g_nexball_football_bouncestop;
 bool autocvar_g_nexball_radar_showallplayers;
 bool autocvar_g_nexball_sound_bounce;
 int autocvar_g_nexball_trail_color;
+bool autocvar_g_nexball_playerclip_collisions = true;
 
 float autocvar_g_nexball_safepass_turnrate;
 float autocvar_g_nexball_safepass_maxdist;
@@ -52,8 +52,8 @@ float autocvar_g_balance_nexball_secondary_lifetime;
 float autocvar_g_balance_nexball_secondary_refire;
 float autocvar_g_balance_nexball_secondary_speed;
 
-void basketball_touch(entity this);
-void football_touch(entity this);
+void basketball_touch(entity this, entity toucher);
+void football_touch(entity this, entity toucher);
 void ResetBall(entity this);
 const int NBM_NONE = 0;
 const int NBM_FOOTBALL = 2;
@@ -70,9 +70,7 @@ float OtherTeam(float t)  //works only if there are two teams on the map!
 }
 
 const float ST_NEXBALL_GOALS = 1;
-const float SP_NEXBALL_GOALS = 4;
-const float SP_NEXBALL_FAULTS = 5;
-void nb_ScoreRules(float teams)
+void nb_ScoreRules(int teams)
 {
        ScoreRules_basics(teams, 0, 0, true);
        ScoreInfo_SetLabel_TeamScore(   ST_NEXBALL_GOALS,  "goals", SFL_SORT_PRIO_PRIMARY);
@@ -106,9 +104,9 @@ void nexball_setstatus(entity this)
        {
                if(this.ballcarried.teamtime && (this.ballcarried.teamtime < time))
                {
-                       bprint("The ", Team_ColoredFullName(this.team), " held the ball for too long.\n");
-                       DropBall(this.ballcarried, this.ballcarried.owner.origin, '0 0 0');
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_NEXBALL_RETURN_HELD));
                        entity e = this.ballcarried;
+                       DropBall(this.ballcarried, this.ballcarried.owner.origin, '0 0 0');
                        ResetBall(e);
                }
                else
@@ -178,7 +176,7 @@ void GiveBall(entity plyr, entity ball)
        ball.effects &= ~autocvar_g_nexball_basketball_effects_default;
 
        ball.velocity = '0 0 0';
-       ball.movetype = MOVETYPE_NONE;
+       set_movetype(ball, MOVETYPE_NONE);
        settouch(ball, func_null);
        ball.effects |= EF_NOSHADOW;
        ball.scale = 1; // scale down.
@@ -209,7 +207,7 @@ void DropBall(entity ball, vector org, vector vel)
 
        setattachment(ball, NULL, "");
        setorigin(ball, org);
-       ball.movetype = MOVETYPE_BOUNCE;
+       set_movetype(ball, MOVETYPE_BOUNCE);
        UNSET_ONGROUND(ball);
        ball.scale = ball_scale;
        ball.velocity = vel;
@@ -237,7 +235,7 @@ void InitBall(entity this)
 {
        if(gameover) return;
        UNSET_ONGROUND(this);
-       this.movetype = MOVETYPE_BOUNCE;
+       set_movetype(this, MOVETYPE_BOUNCE);
        if(this.classname == "nexball_basketball")
                settouch(this, basketball_touch);
        else if(this.classname == "nexball_football")
@@ -258,10 +256,10 @@ void ResetBall(entity this)
        if(this.cnt < 2)        // step 1
        {
                if(time == this.teamtime)
-                       bprint("The ", Team_ColoredFullName(this.team), " held the ball for too long.\n");
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_NEXBALL_RETURN_HELD));
 
                settouch(this, func_null);
-               this.movetype = MOVETYPE_NOCLIP;
+               set_movetype(this, MOVETYPE_NOCLIP);
                this.velocity = '0 0 0'; // just in case?
                if(!this.cnt)
                        LogNB("resetidle", NULL);
@@ -283,15 +281,15 @@ void ResetBall(entity this)
                                   vtos(this.origin - this.spawnorigin), " Velocity: ", vtos(this.velocity), "\n");
                this.velocity = '0 0 0';
                setorigin(this, this.spawnorigin); // make sure it's positioned correctly anyway
-               this.movetype = MOVETYPE_NONE;
+               set_movetype(this, MOVETYPE_NONE);
                setthink(this, InitBall);
                this.nextthink = max(time, game_starttime) + autocvar_g_nexball_delay_start;
        }
 }
 
-void football_touch(entity this)
+void football_touch(entity this, entity toucher)
 {
-       if(other.solid == SOLID_BSP)
+       if(toucher.solid == SOLID_BSP)
        {
                if(time > this.lastground + 0.1)
                {
@@ -302,54 +300,54 @@ void football_touch(entity this)
                        this.nextthink = time + autocvar_g_nexball_delay_idle;
                return;
        }
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
-       if(other.health < 1)
+       if(toucher.health < 1)
                return;
        if(!this.cnt)
                this.nextthink = time + autocvar_g_nexball_delay_idle;
 
-       this.pusher = other;
-       this.team = other.team;
+       this.pusher = toucher;
+       this.team = toucher.team;
 
        if(autocvar_g_nexball_football_physics == -1)   // MrBougo try 1, before decompiling Rev's original
        {
-               if(other.velocity)
-                       this.velocity = other.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up;
+               if(toucher.velocity)
+                       this.velocity = toucher.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up;
        }
        else if(autocvar_g_nexball_football_physics == 1)         // MrBougo's modded Rev style: partially independant of the height of the aiming point
        {
-               makevectors(other.v_angle);
-               this.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up;
+               makevectors(toucher.v_angle);
+               this.velocity = toucher.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up;
        }
        else if(autocvar_g_nexball_football_physics == 2)         // 2nd mod try: totally independant. Really playable!
        {
-               makevectors(other.v_angle.y * '0 1 0');
-               this.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
+               makevectors(toucher.v_angle.y * '0 1 0');
+               this.velocity = toucher.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
        }
        else     // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant)
        {
-               makevectors(other.v_angle);
-               this.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
+               makevectors(toucher.v_angle);
+               this.velocity = toucher.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
        }
        this.avelocity = -250 * v_forward;  // maybe there is a way to make it look better?
 }
 
-void basketball_touch(entity this)
+void basketball_touch(entity this, entity toucher)
 {
-       if(other.ballcarried)
+       if(toucher.ballcarried)
        {
-               football_touch(this);
+               football_touch(this, toucher);
                return;
        }
-       if(!this.cnt && IS_PLAYER(other) && !STAT(FROZEN, other) && !IS_DEAD(other) && (other != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect))
+       if(!this.cnt && IS_PLAYER(toucher) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (toucher != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect))
        {
-               if(other.health <= 0)
+               if(toucher.health <= 0)
                        return;
-               LogNB("caught", other);
-               GiveBall(other, this);
+               LogNB("caught", toucher);
+               GiveBall(toucher, this);
        }
-       else if(other.solid == SOLID_BSP)
+       else if(toucher.solid == SOLID_BSP)
        {
                _sound(this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
                if(this.velocity && !this.cnt)
@@ -357,26 +355,26 @@ void basketball_touch(entity this)
        }
 }
 
-void GoalTouch(entity this)
+void GoalTouch(entity this, entity toucher)
 {
        entity ball;
        float isclient, pscore, otherteam;
        string pname;
 
        if(gameover) return;
-       if((this.spawnflags & GOAL_TOUCHPLAYER) && other.ballcarried)
-               ball = other.ballcarried;
+       if((this.spawnflags & GOAL_TOUCHPLAYER) && toucher.ballcarried)
+               ball = toucher.ballcarried;
        else
-               ball = other;
+               ball = toucher;
        if(ball.classname != "nexball_basketball")
                if(ball.classname != "nexball_football")
                        return;
        if((!ball.pusher && this.team != GOAL_OUT) || ball.cnt)
                return;
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
 
-       if(nb_teams == 2)
+       if(NumTeams(nb_teams) == 2)
                otherteam = OtherTeam(ball.team);
        else
                otherteam = 0;
@@ -395,7 +393,7 @@ void GoalTouch(entity this)
        else if(this.team == GOAL_FAULT)
        {
                LogNB("fault", ball.pusher);
-               if(nb_teams == 2)
+               if(NumTeams(nb_teams) == 2)
                        bprint(Team_ColoredFullName(otherteam), " gets a point due to ", pname, "^7's silliness.\n");
                else
                        bprint(Team_ColoredFullName(ball.team), " loses a point due to ", pname, "^7's silliness.\n");
@@ -421,7 +419,7 @@ void GoalTouch(entity this)
 
        if(ball.team && pscore)
        {
-               if(nb_teams == 2 && pscore < 0)
+               if(NumTeams(nb_teams) == 2 && pscore < 0)
                        TeamScore_AddToTeam(otherteam, ST_NEXBALL_GOALS, -pscore);
                else
                        TeamScore_AddToTeam(ball.team, ST_NEXBALL_GOALS, pscore);
@@ -453,7 +451,7 @@ spawnfunc(nexball_team)
 {
        if(!g_nexball)
        {
-               remove(this);
+               delete(this);
                return;
        }
        this.team = this.cnt + 1;
@@ -461,12 +459,12 @@ spawnfunc(nexball_team)
 
 void nb_spawnteam(string teamname, float teamcolor)
 {
-       LOG_TRACE("^2spawned team ", teamname, "\n");
+       LOG_TRACE("^2spawned team ", teamname);
        entity e = new(nexball_team);
        e.netname = teamname;
        e.cnt = teamcolor;
        e.team = e.cnt + 1;
-       nb_teams += 1;
+       //nb_teams += 1;
 }
 
 void nb_spawnteams()
@@ -481,6 +479,7 @@ void nb_spawnteams()
                        if(!t_red)
                        {
                                nb_spawnteam("Red", e.team-1)   ;
+                               nb_teams |= BIT(0);
                                t_red = true;
                        }
                        break;
@@ -489,6 +488,7 @@ void nb_spawnteams()
                        {
                                nb_spawnteam("Blue", e.team-1)  ;
                                t_blue = true;
+                               nb_teams |= BIT(1);
                        }
                        break;
                case NUM_TEAM_3:
@@ -496,6 +496,7 @@ void nb_spawnteams()
                        {
                                nb_spawnteam("Yellow", e.team-1);
                                t_yellow = true;
+                               nb_teams |= BIT(2);
                        }
                        break;
                case NUM_TEAM_4:
@@ -503,6 +504,7 @@ void nb_spawnteams()
                        {
                                nb_spawnteam("Pink", e.team-1)  ;
                                t_pink = true;
+                               nb_teams |= BIT(3);
                        }
                        break;
                }
@@ -523,7 +525,7 @@ void nb_delayedinit(entity this)
 
 void SpawnBall(entity this)
 {
-       if(!g_nexball) { remove(this); return; }
+       if(!g_nexball) { delete(this); return; }
 
 //     balls += 4; // using the remaining bits to count balls will leave more than the max edict count, so it's fine
 
@@ -549,7 +551,10 @@ void SpawnBall(entity this)
                this.glow_trail = true;
        }
 
-       this.movetype = MOVETYPE_FLY;
+       set_movetype(this, MOVETYPE_FLY);
+
+       if(autocvar_g_nexball_playerclip_collisions)
+               this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP;
 
        if(!autocvar_g_nexball_sound_bounce)
                this.noise = "";
@@ -609,11 +614,10 @@ spawnfunc(nexball_football)
        SpawnBall(this);
 }
 
-float nb_Goal_Customize(entity this)
+bool nb_Goal_Customize(entity this, entity client)
 {
-       entity e, wp_owner;
-       e = WaypointSprite_getviewentity(other);
-       wp_owner = this.owner;
+       entity e = WaypointSprite_getviewentity(client);
+       entity wp_owner = this.owner;
        if(SAME_TEAM(e, wp_owner)) { return false; }
 
        return true;
@@ -621,7 +625,7 @@ float nb_Goal_Customize(entity this)
 
 void SpawnGoal(entity this)
 {
-       if(!g_nexball) { remove(this); return; }
+       if(!g_nexball) { delete(this); return; }
 
        EXACTTRIGGER_INIT;
 
@@ -730,45 +734,45 @@ void W_Nexball_Think(entity this)
        this.nextthink = time;
 }
 
-void W_Nexball_Touch(entity this)
+void W_Nexball_Touch(entity this, entity toucher)
 {
        entity ball, attacker;
        attacker = this.owner;
        //this.think = func_null;
        //this.enemy = NULL;
 
-       PROJECTILE_TOUCH(this);
-       if(attacker.team != other.team || autocvar_g_nexball_basketball_teamsteal)
-               if((ball = other.ballcarried) && !STAT(FROZEN, other) && !IS_DEAD(other) && (IS_PLAYER(attacker)))
+       PROJECTILE_TOUCH(this, toucher);
+       if(attacker.team != toucher.team || autocvar_g_nexball_basketball_teamsteal)
+               if((ball = toucher.ballcarried) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (IS_PLAYER(attacker)))
                {
-                       other.velocity = other.velocity + normalize(this.velocity) * other.damageforcescale * autocvar_g_balance_nexball_secondary_force;
-                       UNSET_ONGROUND(other);
+                       toucher.velocity = toucher.velocity + normalize(this.velocity) * toucher.damageforcescale * autocvar_g_balance_nexball_secondary_force;
+                       UNSET_ONGROUND(toucher);
                        if(!attacker.ballcarried)
                        {
                                LogNB("stole", attacker);
-                               _sound(other, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM);
+                               _sound(toucher, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM);
 
-                               if(SAME_TEAM(attacker, other) && time > attacker.teamkill_complain)
+                               if(SAME_TEAM(attacker, toucher) && time > attacker.teamkill_complain)
                                {
                                        attacker.teamkill_complain = time + 5;
                                        attacker.teamkill_soundtime = time + 0.4;
-                                       attacker.teamkill_soundsource = other;
+                                       attacker.teamkill_soundsource = toucher;
                                }
 
-                               GiveBall(attacker, other.ballcarried);
+                               GiveBall(attacker, toucher.ballcarried);
                        }
                }
-       remove(this);
+       delete(this);
 }
 
-void W_Nexball_Attack(entity actor, float t)
+void W_Nexball_Attack(entity actor, .entity weaponentity, float t)
 {
        entity ball;
        float mul, mi, ma;
        if(!(ball = actor.ballcarried))
                return;
 
-       W_SetupShot(actor, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
+       W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
        tracebox(w_shotorg, BALL_MINS, BALL_MAXS, w_shotorg, MOVE_WORLDONLY, NULL);
        if(trace_startsolid)
        {
@@ -799,12 +803,12 @@ void W_Nexball_Attack(entity actor, float t)
 
 vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
 
-void W_Nexball_Attack2(entity actor)
+void W_Nexball_Attack2(entity actor, .entity weaponentity)
 {
        if(actor.ballcarried.enemy)
        {
                entity _ball = actor.ballcarried;
-               W_SetupShot(actor, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
+               W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
                DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
                setthink(_ball, W_Nexball_Think);
                _ball.nextthink = time;
@@ -814,12 +818,12 @@ void W_Nexball_Attack2(entity actor)
        if(!autocvar_g_nexball_tackling)
                return;
 
-       W_SetupShot(actor, false, 2, SND_NB_SHOOT2, CH_WEAPON_A, 0);
+       W_SetupShot(actor, weaponentity, false, 2, SND_NB_SHOOT2, CH_WEAPON_A, 0);
        entity missile = new(ballstealer);
 
        missile.owner = actor;
 
-       missile.movetype = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(missile);
 
        //setmodel(missile, "models/elaser.mdl");  // precision set below
@@ -834,11 +838,13 @@ void W_Nexball_Attack2(entity actor)
 
        missile.effects = EF_BRIGHTFIELD | EF_LOWPRECISION;
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
 
        CSQCProjectile(missile, true, PROJECTILE_ELECTRO, true);
 }
 
-float ball_customize(entity this)
+bool ball_customize(entity this, entity client)
 {
        if(!this.owner)
        {
@@ -848,7 +854,7 @@ float ball_customize(entity this)
                return true;
        }
 
-       if(other == this.owner)
+       if(client == this.owner)
        {
                this.scale = autocvar_g_nexball_viewmodel_scale;
                if(this.enemy)
@@ -879,19 +885,19 @@ METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity we
             }
             else
             {
-                W_Nexball_Attack(actor, -1);
+                W_Nexball_Attack(actor, weaponentity, -1);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
             }
     if(fire & 2)
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_nexball_secondary_refire))
         {
-            W_Nexball_Attack2(actor);
+            W_Nexball_Attack2(actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
         }
 
     if(!(fire & 1) && actor.metertime && actor.ballcarried)
     {
-        W_Nexball_Attack(actor, time - actor.metertime);
+        W_Nexball_Attack(actor, weaponentity, time - actor.metertime);
         // DropBall or stealing will set metertime back to 0
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
     }
@@ -1073,7 +1079,18 @@ MUTATOR_HOOKFUNCTION(nb, FilterItem)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(nb, GetTeamCount)
+MUTATOR_HOOKFUNCTION(nb, ItemTouch)
+{
+       entity item = M_ARGV(0, entity);
+       entity toucher = M_ARGV(1, entity);
+
+       if(item.weapon && toucher.ballcarried)
+               return MUT_ITEMTOUCH_RETURN; // no new weapons for you, mister!
+
+       return MUT_ITEMTOUCH_CONTINUE;
+}
+
+MUTATOR_HOOKFUNCTION(nb, CheckAllowedTeams)
 {
        M_ARGV(1, string) = "nexball_team";
        return true;
@@ -1144,4 +1161,3 @@ REGISTER_MUTATOR(nb, g_nexball)
 }
 
 #endif
-#endif
index 9dd8042be32ab87f369874e7718ca1e19feb2031..53797d2bc6d245dc7de9ee17abaa5143836a5e31 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef GAMEMODE_NEXBALL_H
-#define GAMEMODE_NEXBALL_H
+#pragma once
 
 #ifdef SVQC
 //EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME
@@ -35,4 +34,3 @@ float nb_teams;
 
 .float teamtime;
 #endif
-#endif
index c6aa4be215a21fca52c75cf00388f6a7de730af3..f207263535387422196ff99095a2c5d01e6c8d54 100644 (file)
@@ -1,12 +1 @@
-#ifndef GAMEMODE_NEXBALL_WEAPON_H
-#define GAMEMODE_NEXBALL_WEAPON_H
-
-CLASS(BallStealer, PortoLaunch)
-/* flags     */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
-/* impulse   */ ATTRIB(BallStealer, impulse, int, 0);
-/* refname   */ ATTRIB(BallStealer, netname, string, "ballstealer");
-/* wepname   */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer"));
-ENDCLASS(BallStealer)
-REGISTER_WEAPON(NEXBALL, NEW(BallStealer));
-
-#endif
+#include "weapon.qh"
diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh
new file mode 100644 (file)
index 0000000..73b8872
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+CLASS(BallStealer, PortoLaunch)
+/* flags     */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(BallStealer, impulse, int, 0);
+/* refname   */ ATTRIB(BallStealer, netname, string, "ballstealer");
+/* wepname   */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer"));
+ENDCLASS(BallStealer)
+REGISTER_WEAPON(NEXBALL, NEW(BallStealer));
index 334eb561ee14fd816cfcd9113f7d9b5c1a9206af..ca8c83c19a08ecb0ba4ef9efb8203e2df13c55dd 100644 (file)
@@ -1,6 +1,19 @@
 // generated file; do not modify
-#include <common/gamemodes/gamemode/onslaught/cl_controlpoint.qc>
-#include <common/gamemodes/gamemode/onslaught/cl_generator.qc>
+#include <common/gamemodes/gamemode/onslaught/controlpoint.qc>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/onslaught/cl_controlpoint.qc>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/onslaught/sv_controlpoint.qc>
+#endif
+#include <common/gamemodes/gamemode/onslaught/generator.qc>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/onslaught/cl_generator.qc>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/onslaught/sv_generator.qc>
+#endif
 #include <common/gamemodes/gamemode/onslaught/onslaught.qc>
-#include <common/gamemodes/gamemode/onslaught/sv_controlpoint.qc>
-#include <common/gamemodes/gamemode/onslaught/sv_generator.qc>
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/onslaught/sv_onslaught.qc>
+#endif
index e895495581e4e57bf8d7d4e97bc9b95008a13856..bb95416b310a2206e025398b432ebe699533cd4d 100644 (file)
@@ -1,6 +1,19 @@
 // generated file; do not modify
-#include <common/gamemodes/gamemode/onslaught/cl_controlpoint.qh>
-#include <common/gamemodes/gamemode/onslaught/cl_generator.qh>
+#include <common/gamemodes/gamemode/onslaught/controlpoint.qh>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/onslaught/cl_controlpoint.qh>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/onslaught/sv_controlpoint.qh>
+#endif
+#include <common/gamemodes/gamemode/onslaught/generator.qh>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/onslaught/cl_generator.qh>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/onslaught/sv_generator.qh>
+#endif
 #include <common/gamemodes/gamemode/onslaught/onslaught.qh>
-#include <common/gamemodes/gamemode/onslaught/sv_controlpoint.qh>
-#include <common/gamemodes/gamemode/onslaught/sv_generator.qh>
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/onslaught/sv_onslaught.qh>
+#endif
index 8389b22dae1ee0945aceb633a91592dadaeb8917..ee348fdbc6244707f53e7b6202aae30cd6392ea7 100644 (file)
@@ -74,9 +74,9 @@ void cpicon_draw(entity this)
                }
 
                this.angles_x = this.punchangle_x;
-               this.angles_y = this.punchangle_y + this.move_angles_y;
+               this.angles_y = this.punchangle_y + this.angles_y;
                this.angles_z = this.punchangle_z;
-               this.move_angles_y = this.move_angles_y + 45 * frametime;
+               this.angles_y = this.angles_y + 45 * frametime;
        }
 
        setorigin(this, this.cp_origin + this.cp_bob_origin + this.cp_bob_dmg);
@@ -104,7 +104,7 @@ void cpicon_damage(entity this, float hp)
        setsize(this, CPICON_MIN, CPICON_MAX);
 }
 
-void cpicon_construct(entity this)
+void cpicon_construct(entity this, bool isnew)
 {
        this.netname = "Control Point Icon";
 
@@ -117,17 +117,15 @@ void cpicon_construct(entity this)
                setmodel(this.icon_realmodel, MDL_Null);
                setorigin(this.icon_realmodel, this.origin);
                setsize(this.icon_realmodel, CPICON_MIN, CPICON_MAX);
-               this.icon_realmodel.movetype = MOVETYPE_NOCLIP;
+               set_movetype(this.icon_realmodel, MOVETYPE_NOCLIP);
                this.icon_realmodel.solid = SOLID_NOT;
-               this.icon_realmodel.move_origin = this.icon_realmodel.origin;
        }
 
        if(this.iscaptured) { this.icon_realmodel.solid = SOLID_BBOX; }
 
-       this.move_movetype      = MOVETYPE_NOCLIP;
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.solid                      = SOLID_NOT;
-       this.movetype           = MOVETYPE_NOCLIP;
-       this.move_origin        = this.origin;
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.move_time          = time;
        this.drawmask           = MASK_NORMAL;
        this.alpha                      = 1;
@@ -135,6 +133,9 @@ void cpicon_construct(entity this)
        this.cp_origin          = this.origin;
        this.cp_bob_origin      = '0 0 0.1';
        this.cp_bob_spd         = 0;
+
+       if(isnew)
+               IL_PUSH(g_drawables, this);
 }
 
 .vector glowmod;
@@ -176,7 +177,7 @@ NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew)
                        this.count = (this.health - this.max_health) * frametime;
 
                cpicon_changeteam(this);
-               cpicon_construct(this);
+               cpicon_construct(this, isnew);
        }
 
        if(sf & CPSF_STATUS)
index 15586ea5dcd923d6aab26966dc063d3f534fbc5b..d5437338e3fb47ac0432e6de3b027bd81a366eec 100644 (file)
@@ -1,10 +1,7 @@
-#ifndef CLIENT_CONTROLPOINT_H
-#define CLIENT_CONTROLPOINT_H
+#pragma once
 
 const vector CPICON_MIN = '-32 -32 -9';
 const vector CPICON_MAX = '32 32 25';
 
 const int CPSF_STATUS = 4;
 const int CPSF_SETUP = 8;
-
-#endif
index 03e81f4bdea092deee9f965d104cf4f3d4c8f2af..ac7a066ab17032c3fb0ae3733bdd8ead99c686c8 100644 (file)
@@ -14,7 +14,8 @@ void ons_generator_ray_draw(entity this)
 
        if(this.count > 10)
        {
-               remove(this);
+               IL_REMOVE(g_drawables, this);
+               delete(this);
                return;
        }
 
@@ -33,13 +34,13 @@ void ons_generator_ray_spawn(vector org)
        setmodel(e, MDL_ONS_RAY);
        setorigin(e, org);
        e.angles = randomvec() * 360;
-       e.move_origin = org;
-       e.movetype = MOVETYPE_NONE;
+       set_movetype(e, MOVETYPE_NONE);
        e.alpha = 0;
        e.scale = random() * 5 + 8;
        e.move_time = time + 0.05;
        e.drawmask = MASK_NORMAL;
        e.draw = ons_generator_ray_draw;
+       IL_PUSH(g_drawables, e);
 }
 
 void generator_draw(entity this)
@@ -143,19 +144,24 @@ void generator_damage(entity this, float hp)
        setsize(this, GENERATOR_MIN, GENERATOR_MAX);
 }
 
-void generator_construct(entity this)
+void generator_construct(entity this, bool isnew)
 {
        this.netname = "Generator";
        this.classname = "onslaught_generator";
 
+       if(isnew)
+       {
+               IL_PUSH(g_onsgenerators, this);
+               IL_PUSH(g_drawables, this);
+       }
+
        setorigin(this, this.origin);
        setmodel(this, MDL_ONS_GEN);
        setsize(this, GENERATOR_MIN, GENERATOR_MAX);
 
-       this.move_movetype      = MOVETYPE_NOCLIP;
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.solid                      = SOLID_BBOX;
-       this.movetype           = MOVETYPE_NOCLIP;
-       this.move_origin        = this.origin;
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.move_time          = time;
        this.drawmask           = MASK_NORMAL;
        this.alpha                      = 1;
@@ -200,7 +206,7 @@ NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew)
                        this.count = 40;
 
                generator_changeteam(this);
-               generator_construct(this);
+               generator_construct(this, isnew);
        }
 
        if(sf & GSF_STATUS)
index 3c0cf28697536f2ad234a24d6ab08c7830e7adec..49524687f42d48d7c13d150f048a1f4dc0767217 100644 (file)
@@ -1,9 +1,7 @@
-#ifndef CLIENT_GENERATOR_H
-#define CLIENT_GENERATOR_H
+#pragma once
+
 const vector GENERATOR_MIN = '-52 -52 -14';
 const vector GENERATOR_MAX = '52 52 75';
 
 const int GSF_STATUS = 4;
 const int GSF_SETUP = 8;
-
-#endif
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc
new file mode 100644 (file)
index 0000000..b21f5fd
--- /dev/null
@@ -0,0 +1 @@
+#include "controlpoint.qh"
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc
new file mode 100644 (file)
index 0000000..f9415f6
--- /dev/null
@@ -0,0 +1 @@
+#include "generator.qh"
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/module.inc b/qcsrc/common/gamemodes/gamemode/onslaught/module.inc
deleted file mode 100644 (file)
index fee33b1..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ONS_CONSTANTS
-       #define ONS_CONSTANTS
-       REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR)
-       REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON)
-#endif
-
-#if defined(SVQC)
-       #include "onslaught.qc"
-       #ifndef IMPLEMENTATION
-               #include "sv_controlpoint.qh"
-               #include "sv_generator.qh"
-       #else
-               #include "sv_controlpoint.qc"
-               #include "sv_generator.qc"
-       #endif
-#elif defined(CSQC)
-       #ifndef IMPLEMENTATION
-               #include "cl_controlpoint.qh"
-               #include "cl_generator.qh"
-       #else
-               #include "cl_controlpoint.qc"
-               #include "cl_generator.qc"
-       #endif
-#endif
index 2c529827098ca7d6f28e2f20ff1d8cbc51285e23..afbd31842978a332d81c4a3d914a2202799fddae 100644 (file)
-#ifndef GAMEMODE_ONSLAUGHT_H
-#define GAMEMODE_ONSLAUGHT_H
+#include "onslaught.qh"
 
-float autocvar_g_onslaught_point_limit;
-void ons_Initialize();
-
-REGISTER_MUTATOR(ons, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               ons_Initialize();
-
-               ActivateTeamplay();
-               SetLimits(autocvar_g_onslaught_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1);
-               have_team_spawns = -1; // request team spawns
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back ons_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return false;
-}
-
-#ifdef SVQC
-
-.entity ons_toucher; // player who touched the control point
-
-// control point / generator constants
-const float ONS_CP_THINKRATE = 0.2;
-const float GEN_THINKRATE = 1;
-#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13))
-const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128');
-const vector CPICON_OFFSET = ('0 0 96');
-
-// list of generators on the map
-entity ons_worldgeneratorlist;
-.entity ons_worldgeneratornext;
-.entity ons_stalegeneratornext;
-
-// list of control points on the map
-entity ons_worldcplist;
-.entity ons_worldcpnext;
-.entity ons_stalecpnext;
-
-// list of links on the map
-entity ons_worldlinklist;
-.entity ons_worldlinknext;
-.entity ons_stalelinknext;
-
-// definitions
-.entity sprite;
-.string target2;
-.int iscaptured;
-.int islinked;
-.int isshielded;
-.float lasthealth;
-.int lastteam;
-.int lastshielded;
-.int lastcaptured;
-
-.bool waslinked;
-
-bool ons_stalemate;
-
-.float teleport_antispam;
-
-.bool ons_roundlost = _STAT(ROUNDLOST);
-
-// waypoint sprites
-.entity bot_basewaypoint; // generator waypointsprite
-
-.bool isgenneighbor[17];
-.bool iscpneighbor[17];
-float ons_notification_time[17];
-
-.float ons_overtime_damagedelay;
-
-.vector ons_deathloc;
-
-.entity ons_spawn_by;
-
-// declarations for functions used outside gamemode_onslaught.qc
-void ons_Generator_UpdateSprite(entity e);
-void ons_ControlPoint_UpdateSprite(entity e);
-bool ons_ControlPoint_Attackable(entity cp, int teamnumber);
-
-// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet
-float ons_captureshield_force; // push force of the shield
-
-// bot player logic
-const int HAVOCBOT_ONS_ROLE_NONE               = 0;
-const int HAVOCBOT_ONS_ROLE_DEFENSE    = 2;
-const int HAVOCBOT_ONS_ROLE_ASSISTANT  = 4;
-const int HAVOCBOT_ONS_ROLE_OFFENSE    = 8;
-
-.entity havocbot_ons_target;
-
-.int havocbot_role_flags;
-.float havocbot_attack_time;
-
-void havocbot_role_ons_defense(entity this);
-void havocbot_role_ons_offense(entity this);
-void havocbot_role_ons_assistant(entity this);
-
-void havocbot_ons_reset_role(entity this);
-void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius);
-void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius);
-
-// score rule declarations
-const int ST_ONS_CAPS = 1;
-const int SP_ONS_CAPS = 4;
-const int SP_ONS_TAKES = 6;
-
-#endif
-#endif
-
-#ifdef IMPLEMENTATION
-
-#include "sv_controlpoint.qh"
-#include "sv_generator.qh"
-
-bool g_onslaught;
-
-float autocvar_g_onslaught_teleport_wait;
-bool autocvar_g_onslaught_spawn_at_controlpoints;
-bool autocvar_g_onslaught_spawn_at_generator;
-float autocvar_g_onslaught_cp_proxydecap;
-float autocvar_g_onslaught_cp_proxydecap_distance = 512;
-float autocvar_g_onslaught_cp_proxydecap_dps = 100;
-float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
-float autocvar_g_onslaught_spawn_at_controlpoints_random;
-float autocvar_g_onslaught_spawn_at_generator_chance;
-float autocvar_g_onslaught_spawn_at_generator_random;
-float autocvar_g_onslaught_cp_buildhealth;
-float autocvar_g_onslaught_cp_buildtime;
-float autocvar_g_onslaught_cp_health;
-float autocvar_g_onslaught_cp_regen;
-float autocvar_g_onslaught_gen_health;
-float autocvar_g_onslaught_shield_force = 100;
-float autocvar_g_onslaught_allow_vehicle_touch;
-float autocvar_g_onslaught_round_timelimit;
-float autocvar_g_onslaught_warmup;
-float autocvar_g_onslaught_teleport_radius;
-float autocvar_g_onslaught_spawn_choose;
-float autocvar_g_onslaught_click_radius;
-
-void FixSize(entity e);
-
-// =======================
-// CaptureShield Functions
-// =======================
-
-bool ons_CaptureShield_Customize(entity this)
-{
-       entity e = WaypointSprite_getviewentity(other);
-
-       if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, e.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return false; }
-       if(SAME_TEAM(this, e)) { return false; }
-
-       return true;
-}
-
-void ons_CaptureShield_Touch(entity this)
-{
-       if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, other.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return; }
-       if(!IS_PLAYER(other)) { return; }
-       if(SAME_TEAM(other, this)) { return; }
-
-       vector mymid = (this.absmin + this.absmax) * 0.5;
-       vector othermid = (other.absmin + other.absmax) * 0.5;
-
-       Damage(other, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ons_captureshield_force);
-
-       if(IS_REAL_CLIENT(other))
-       {
-               play2(other, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
-
-               if(this.enemy.classname == "onslaught_generator")
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED);
-               else
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED);
-       }
-}
-
-void ons_CaptureShield_Reset(entity this)
-{
-       this.colormap = this.enemy.colormap;
-       this.team = this.enemy.team;
-}
-
-void ons_CaptureShield_Spawn(entity generator, bool is_generator)
-{
-       entity shield = new(ons_captureshield);
-
-       shield.enemy = generator;
-       shield.team = generator.team;
-       shield.colormap = generator.colormap;
-       shield.reset = ons_CaptureShield_Reset;
-       settouch(shield, ons_CaptureShield_Touch);
-       setcefc(shield, ons_CaptureShield_Customize);
-       shield.effects = EF_ADDITIVE;
-       shield.movetype = MOVETYPE_NOCLIP;
-       shield.solid = SOLID_TRIGGER;
-       shield.avelocity = '7 0 11';
-       shield.scale = 1;
-       shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3");
-
-       precache_model(shield.model);
-       setorigin(shield, generator.origin);
-       _setmodel(shield, shield.model);
-       setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
-}
-
-
-// ==========
-// Junk Pile
-// ==========
-
-void setmodel_fixsize(entity e, Model m)
-{
-       setmodel(e, m);
-       FixSize(e);
-}
-
-void onslaught_updatelinks()
-{
-       entity l;
-       // first check if the game has ended
-       LOG_DEBUG("--- updatelinks ---\n");
-       // mark generators as being shielded and networked
-       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
-       {
-               if (l.iscaptured)
-                       LOG_DEBUG(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"));
-               else
-                       LOG_DEBUG(strcat(etos(l), " (generator) is destroyed\n"));
-               l.islinked = l.iscaptured;
-               l.isshielded = l.iscaptured;
-               l.sprite.SendFlags |= 16;
-       }
-       // mark points as shielded and not networked
-       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
-       {
-               l.islinked = false;
-               l.isshielded = true;
-               int i;
-               for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; }
-               LOG_DEBUG(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n"));
-               l.sprite.SendFlags |= 16;
-       }
-       // flow power outward from the generators through the network
-       bool stop = false;
-       while (!stop)
-       {
-               stop = true;
-               for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
-               {
-                       // if both points are captured by the same team, and only one of
-                       // them is powered, mark the other one as powered as well
-                       if (l.enemy.iscaptured && l.goalentity.iscaptured)
-                               if (l.enemy.islinked != l.goalentity.islinked)
-                                       if(SAME_TEAM(l.enemy, l.goalentity))
-                                       {
-                                               if (!l.goalentity.islinked)
-                                               {
-                                                       stop = false;
-                                                       l.goalentity.islinked = true;
-                                                       LOG_DEBUG(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"));
-                                               }
-                                               else if (!l.enemy.islinked)
-                                               {
-                                                       stop = false;
-                                                       l.enemy.islinked = true;
-                                                       LOG_DEBUG(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"));
-                                               }
-                                       }
-               }
-       }
-       // now that we know which points are powered we can mark their neighbors
-       // as unshielded if team differs
-       for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
-       {
-               if (l.goalentity.islinked)
-               {
-                       if(DIFF_TEAM(l.goalentity, l.enemy))
-                       {
-                               LOG_DEBUG(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"));
-                               l.enemy.isshielded = false;
-                       }
-                       if(l.goalentity.classname == "onslaught_generator")
-                               l.enemy.isgenneighbor[l.goalentity.team] = true;
-                       else
-                               l.enemy.iscpneighbor[l.goalentity.team] = true;
-               }
-               if (l.enemy.islinked)
-               {
-                       if(DIFF_TEAM(l.goalentity, l.enemy))
-                       {
-                               LOG_DEBUG(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"));
-                               l.goalentity.isshielded = false;
-                       }
-                       if(l.enemy.classname == "onslaught_generator")
-                               l.goalentity.isgenneighbor[l.enemy.team] = true;
-                       else
-                               l.goalentity.iscpneighbor[l.enemy.team] = true;
-               }
-       }
-       // now update the generators
-       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
-       {
-               if (l.isshielded)
-               {
-                       LOG_DEBUG(strcat(etos(l), " (generator) is shielded\n"));
-                       l.takedamage = DAMAGE_NO;
-                       l.bot_attack = false;
-               }
-               else
-               {
-                       LOG_DEBUG(strcat(etos(l), " (generator) is not shielded\n"));
-                       l.takedamage = DAMAGE_AIM;
-                       l.bot_attack = true;
-               }
-
-               ons_Generator_UpdateSprite(l);
-       }
-       // now update the takedamage and alpha variables on control point icons
-       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
-       {
-               if (l.isshielded)
-               {
-                       LOG_DEBUG(strcat(etos(l), " (point) is shielded\n"));
-                       if (l.goalentity)
-                       {
-                               l.goalentity.takedamage = DAMAGE_NO;
-                               l.goalentity.bot_attack = false;
-                       }
-               }
-               else
-               {
-                       LOG_DEBUG(strcat(etos(l), " (point) is not shielded\n"));
-                       if (l.goalentity)
-                       {
-                               l.goalentity.takedamage = DAMAGE_AIM;
-                               l.goalentity.bot_attack = true;
-                       }
-               }
-               ons_ControlPoint_UpdateSprite(l);
-       }
-       l = findchain(classname, "ons_captureshield");
-       while(l)
-       {
-               l.team = l.enemy.team;
-               l.colormap = l.enemy.colormap;
-               l = l.chain;
-       }
-}
-
-
-// ===================
-// Main Link Functions
-// ===================
-
-bool ons_Link_Send(entity this, entity to, int sendflags)
-{
-       WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK);
-       WriteByte(MSG_ENTITY, sendflags);
-       if(sendflags & 1)
-       {
-               WriteCoord(MSG_ENTITY, this.goalentity.origin_x);
-               WriteCoord(MSG_ENTITY, this.goalentity.origin_y);
-               WriteCoord(MSG_ENTITY, this.goalentity.origin_z);
-       }
-       if(sendflags & 2)
-       {
-               WriteCoord(MSG_ENTITY, this.enemy.origin_x);
-               WriteCoord(MSG_ENTITY, this.enemy.origin_y);
-               WriteCoord(MSG_ENTITY, this.enemy.origin_z);
-       }
-       if(sendflags & 4)
-       {
-               WriteByte(MSG_ENTITY, this.clientcolors); // which is goalentity's color + enemy's color * 16
-       }
-       return true;
-}
-
-void ons_Link_CheckUpdate(entity this)
-{
-       // TODO check if the two sides have moved (currently they won't move anyway)
-       float cc = 0, cc1 = 0, cc2 = 0;
-
-       if(this.goalentity.islinked || this.goalentity.iscaptured) { cc1 = (this.goalentity.team - 1) * 0x01; }
-       if(this.enemy.islinked || this.enemy.iscaptured) { cc2 = (this.enemy.team - 1) * 0x10; }
-
-       cc = cc1 + cc2;
-
-       if(cc != this.clientcolors)
-       {
-               this.clientcolors = cc;
-               this.SendFlags |= 4;
-       }
-
-       this.nextthink = time;
-}
-
-void ons_DelayedLinkSetup(entity this)
-{
-       this.goalentity = find(NULL, targetname, this.target);
-       this.enemy = find(NULL, targetname, this.target2);
-       if(!this.goalentity) { objerror(this, "can not find target\n"); }
-       if(!this.enemy) { objerror(this, "can not find target2\n"); }
-
-       LOG_DEBUG(strcat(etos(this.goalentity), " linked with ", etos(this.enemy), "\n"));
-       this.SendFlags |= 3;
-       setthink(this, ons_Link_CheckUpdate);
-       this.nextthink = time;
-}
-
-
-// =============================
-// Main Control Point Functions
-// =============================
-
-int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber)
-{
-       if(cp.isgenneighbor[teamnumber]) { return 2; }
-       if(cp.iscpneighbor[teamnumber]) { return 1; }
-
-       return 0;
-}
-
-int ons_ControlPoint_Attackable(entity cp, int teamnumber)
-       // -2: SAME TEAM, attackable by enemy!
-       // -1: SAME TEAM!
-       // 0: off limits
-       // 1: attack it
-       // 2: touch it
-       // 3: attack it (HIGH PRIO)
-       // 4: touch it (HIGH PRIO)
-{
-       int a;
-
-       if(cp.isshielded)
-       {
-               return 0;
-       }
-       else if(cp.goalentity)
-       {
-               // if there's already an icon built, nothing happens
-               if(cp.team == teamnumber)
-               {
-                       a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
-                       if(a) // attackable by enemy?
-                               return -2; // EMERGENCY!
-                       return -1;
-               }
-               // we know it can be linked, so no need to check
-               // but...
-               a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
-               if(a == 2) // near our generator?
-                       return 3; // EMERGENCY!
-               return 1;
-       }
-       else
-       {
-               // free point
-               if(ons_ControlPoint_CanBeLinked(cp, teamnumber))
-               {
-                       a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t
-                       if(a == 2)
-                               return 4; // GET THIS ONE NOW!
-                       else
-                               return 2; // TOUCH ME
-               }
-       }
-       return 0;
-}
-
-void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if(damage <= 0) { return; }
-
-       if (this.owner.isshielded)
-       {
-               // this is protected by a shield, so ignore the damage
-               if (time > this.pain_finished)
-                       if (IS_PLAYER(attacker))
-                       {
-                               play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
-                               this.pain_finished = time + 1;
-                               attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
-                       }
-
-               return;
-       }
-
-       if(IS_PLAYER(attacker))
-       if(time - ons_notification_time[this.team] > 10)
-       {
-               play2team(this.team, SND(ONS_CONTROLPOINT_UNDERATTACK));
-               ons_notification_time[this.team] = time;
-       }
-
-       this.health = this.health - damage;
-       if(this.owner.iscaptured)
-               WaypointSprite_UpdateHealth(this.owner.sprite, this.health);
-       else
-               WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE));
-       this.pain_finished = time + 1;
-       // particles on every hit
-       pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1);
-       //sound on every hit
-       if (random() < 0.5)
-               sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM);
-       else
-               sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM);
-
-       if (this.health < 0)
-       {
-               sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
-               pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_CPDESTROYED), this.owner.message, attacker.netname);
-
-               PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
-               PlayerScore_Add(attacker, SP_SCORE, 10);
-
-               this.owner.goalentity = NULL;
-               this.owner.islinked = false;
-               this.owner.iscaptured = false;
-               this.owner.team = 0;
-               this.owner.colormap = 1024;
-
-               WaypointSprite_UpdateMaxHealth(this.owner.sprite, 0);
-
-               onslaught_updatelinks();
-
-               // Use targets now (somebody make sure this is in the right place..)
-               SUB_UseTargets(this.owner, this, NULL);
-
-               this.owner.waslinked = this.owner.islinked;
-               if(this.owner.model != "models/onslaught/controlpoint_pad.md3")
-                       setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1);
-               //setsize(this, '-32 -32 0', '32 32 8');
-
-               remove(this);
-       }
-
-       this.SendFlags |= CPSF_STATUS;
-}
-
-void ons_ControlPoint_Icon_Think(entity this)
-{
-       this.nextthink = time + ONS_CP_THINKRATE;
-
-       if(autocvar_g_onslaught_cp_proxydecap)
-       {
-               int _enemy_count = 0;
-               int _friendly_count = 0;
-
-               FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
-                       if(vdist(it.origin - this.origin, <, autocvar_g_onslaught_cp_proxydecap_distance))
-                       {
-                               if(SAME_TEAM(it, this))
-                                       ++_friendly_count;
-                               else
-                                       ++_enemy_count;
-                       }
-               });
-
-               _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
-               _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
-
-               this.health = bound(0, this.health + (_friendly_count - _enemy_count), this.max_health);
-               this.SendFlags |= CPSF_STATUS;
-               if(this.health <= 0)
-               {
-                       ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, this.origin, '0 0 0');
-                       return;
-               }
-       }
-
-       if (time > this.pain_finished + 5)
-       {
-               if(this.health < this.max_health)
-               {
-                       this.health = this.health + this.count;
-                       if (this.health >= this.max_health)
-                               this.health = this.max_health;
-                       WaypointSprite_UpdateHealth(this.owner.sprite, this.health);
-               }
-       }
-
-       if(this.owner.islinked != this.owner.waslinked)
-       {
-               // unteam the spawnpoint if needed
-               int t = this.owner.team;
-               if(!this.owner.islinked)
-                       this.owner.team = 0;
-
-               SUB_UseTargets(this.owner, this, NULL);
-
-               this.owner.team = t;
-
-               this.owner.waslinked = this.owner.islinked;
-       }
-
-       // damaged fx
-       if(random() < 0.6 - this.health / this.max_health)
-       {
-               Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
-
-               if(random() > 0.8)
-                       sound(this, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM);
-               else if (random() > 0.5)
-                       sound(this, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM);
-       }
-}
-
-void ons_ControlPoint_Icon_BuildThink(entity this)
-{
-       int a;
-
-       this.nextthink = time + ONS_CP_THINKRATE;
-
-       // only do this if there is power
-       a = ons_ControlPoint_CanBeLinked(this.owner, this.owner.team);
-       if(!a)
-               return;
-
-       this.health = this.health + this.count;
-
-       this.SendFlags |= CPSF_STATUS;
-
-       if (this.health >= this.max_health)
-       {
-               this.health = this.max_health;
-               this.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
-               setthink(this, ons_ControlPoint_Icon_Think);
-               sound(this, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM);
-               this.owner.iscaptured = true;
-               this.solid = SOLID_BBOX;
-
-               Send_Effect(EFFECT_CAP(this.owner.team), this.owner.origin, '0 0 0', 1);
-
-               WaypointSprite_UpdateMaxHealth(this.owner.sprite, this.max_health);
-               WaypointSprite_UpdateHealth(this.owner.sprite, this.health);
-
-               if(IS_PLAYER(this.owner.ons_toucher))
-               {
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, this.owner.ons_toucher.netname, this.owner.message);
-                       Send_Notification(NOTIF_ALL_EXCEPT, this.owner.ons_toucher, MSG_CENTER, APP_TEAM_NUM(this.owner.ons_toucher.team, CENTER_ONS_CAPTURE), this.owner.message);
-                       Send_Notification(NOTIF_ONE, this.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, this.owner.message);
-                       PlayerScore_Add(this.owner.ons_toucher, SP_ONS_CAPS, 1);
-                       PlayerTeamScore_AddScore(this.owner.ons_toucher, 10);
-               }
-
-               this.owner.ons_toucher = NULL;
-
-               onslaught_updatelinks();
-
-               // Use targets now (somebody make sure this is in the right place..)
-               SUB_UseTargets(this.owner, this, NULL);
-
-               this.SendFlags |= CPSF_SETUP;
-       }
-       if(this.owner.model != MDL_ONS_CP_PAD2.model_str())
-               setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2);
-
-       if(random() < 0.9 - this.health / this.max_health)
-               Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1);
-}
-
-void onslaught_controlpoint_icon_link(entity e, void(entity this) spawnproc);
-
-void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
-{
-       entity e = new(onslaught_controlpoint_icon);
-
-       setsize(e, CPICON_MIN, CPICON_MAX);
-       setorigin(e, cp.origin + CPICON_OFFSET);
-
-       e.owner = cp;
-       e.max_health = autocvar_g_onslaught_cp_health;
-       e.health = autocvar_g_onslaught_cp_buildhealth;
-       e.solid = SOLID_NOT;
-       e.takedamage = DAMAGE_AIM;
-       e.bot_attack = true;
-       e.event_damage = ons_ControlPoint_Icon_Damage;
-       e.team = player.team;
-       e.colormap = 1024 + (e.team - 1) * 17;
-       e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
-
-       sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM);
-
-       cp.goalentity = e;
-       cp.team = e.team;
-       cp.colormap = e.colormap;
-
-       Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1);
-
-       WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
-       WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
-       cp.sprite.SendFlags |= 16;
-
-       onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
-}
-
-entity ons_ControlPoint_Waypoint(entity e)
-{
-       if(e.team)
-       {
-               int a = ons_ControlPoint_Attackable(e, e.team);
-
-               if(a == -2) { return WP_OnsCPDefend; } // defend now
-               if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch
-               if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack
-       }
-       else
-               return WP_OnsCP;
-
-       return WP_Null;
-}
-
-void ons_ControlPoint_UpdateSprite(entity e)
-{
-       entity s1 = ons_ControlPoint_Waypoint(e);
-       WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
-
-       bool sh;
-       sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4));
-
-       if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
-       {
-               if(e.iscaptured) // don't mess up build bars!
-               {
-                       if(sh)
-                       {
-                               WaypointSprite_UpdateMaxHealth(e.sprite, 0);
-                       }
-                       else
-                       {
-                               WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
-                               WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
-                       }
-               }
-               if(e.lastshielded)
-               {
-                       if(e.team)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
-               }
-               else
-               {
-                       if(e.team)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
-               }
-               WaypointSprite_Ping(e.sprite);
-
-               e.lastteam = e.team + 2;
-               e.lastshielded = sh;
-               e.lastcaptured = e.iscaptured;
-       }
-}
-
-void ons_ControlPoint_Touch(entity this)
-{
-       entity toucher = other;
-       int attackable;
-
-       if(IS_VEHICLE(toucher) && toucher.owner)
-       if(autocvar_g_onslaught_allow_vehicle_touch)
-               toucher = toucher.owner;
-       else
-               return;
-
-       if(!IS_PLAYER(toucher)) { return; }
-       if(STAT(FROZEN, toucher)) { return; }
-       if(IS_DEAD(toucher)) { return; }
-
-       if ( SAME_TEAM(this,toucher) )
-       if ( this.iscaptured )
-       {
-               if(time <= toucher.teleport_antispam)
-                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time));
-               else
-                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
-       }
-
-       attackable = ons_ControlPoint_Attackable(this, toucher.team);
-       if(attackable != 2 && attackable != 4)
-               return;
-       // we've verified that this player has a legitimate claim to this point,
-       // so start building the captured point icon (which only captures this
-       // point if it successfully builds without being destroyed first)
-       ons_ControlPoint_Icon_Spawn(this, toucher);
-
-       this.ons_toucher = toucher;
-
-       onslaught_updatelinks();
-}
-
-void ons_ControlPoint_Think(entity this)
-{
-       this.nextthink = time + ONS_CP_THINKRATE;
-       CSQCMODEL_AUTOUPDATE(this);
-}
-
-void ons_ControlPoint_Reset(entity this)
-{
-       if(this.goalentity)
-               remove(this.goalentity);
-
-       this.goalentity = NULL;
-       this.team = 0;
-       this.colormap = 1024;
-       this.iscaptured = false;
-       this.islinked = false;
-       this.isshielded = true;
-       setthink(this, ons_ControlPoint_Think);
-       this.ons_toucher = NULL;
-       this.nextthink = time + ONS_CP_THINKRATE;
-       setmodel_fixsize(this, MDL_ONS_CP_PAD1);
-
-       WaypointSprite_UpdateMaxHealth(this.sprite, 0);
-       WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY);
-
-       onslaught_updatelinks();
-
-       SUB_UseTargets(this, this, NULL); // to reset the structures, playerspawns etc.
-
-       CSQCMODEL_AUTOUPDATE(this);
-}
-
-void ons_DelayedControlPoint_Setup(entity this)
-{
-       onslaught_updatelinks();
-
-       // captureshield setup
-       ons_CaptureShield_Spawn(this, false);
-
-       CSQCMODEL_AUTOINIT(this);
-}
-
-void ons_ControlPoint_Setup(entity cp)
-{
-       // main setup
-       cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist
-       ons_worldcplist = cp;
-
-       cp.netname = "Control point";
-       cp.team = 0;
-       cp.solid = SOLID_BBOX;
-       cp.movetype = MOVETYPE_NONE;
-       settouch(cp, ons_ControlPoint_Touch);
-       setthink(cp, ons_ControlPoint_Think);
-       cp.nextthink = time + ONS_CP_THINKRATE;
-       cp.reset = ons_ControlPoint_Reset;
-       cp.colormap = 1024;
-       cp.iscaptured = false;
-       cp.islinked = false;
-       cp.isshielded = true;
-
-       if(cp.message == "") { cp.message = "a"; }
-
-       // appearence
-       setmodel_fixsize(cp, MDL_ONS_CP_PAD1);
-
-       // control point placement
-       if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location
-       {
-               cp.noalign = true;
-               cp.movetype = MOVETYPE_NONE;
-       }
-       else // drop to floor, automatically find a platform and set that as spawn origin
-       {
-               setorigin(cp, cp.origin + '0 0 20');
-               cp.noalign = false;
-               droptofloor(cp);
-               cp.movetype = MOVETYPE_TOSS;
-       }
-
-       // waypointsprites
-       WaypointSprite_SpawnFixed(WP_Null, cp.origin + CPGEN_WAYPOINT_OFFSET, cp, sprite, RADARICON_NONE);
-       WaypointSprite_UpdateRule(cp.sprite, cp.team, SPRITERULE_TEAMPLAY);
-
-       InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION);
-}
-
-
-// =========================
-// Main Generator Functions
-// =========================
-
-entity ons_Generator_Waypoint(entity e)
-{
-       if (e.isshielded)
-               return WP_OnsGenShielded;
-       return WP_OnsGen;
-}
-
-void ons_Generator_UpdateSprite(entity e)
-{
-       entity s1 = ons_Generator_Waypoint(e);
-       WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
-
-       if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
-       {
-               e.lastteam = e.team + 2;
-               e.lastshielded = e.isshielded;
-               if(e.lastshielded)
-               {
-                       if(e.team)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
-               }
-               else
-               {
-                       if(e.team)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
-               }
-               WaypointSprite_Ping(e.sprite);
-       }
-}
-
-void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if(damage <= 0) { return; }
-       if(warmup_stage || gameover) { return; }
-       if(!round_handler_IsRoundStarted()) { return; }
-
-       if (attacker != this)
-       {
-               if (this.isshielded)
-               {
-                       // this is protected by a shield, so ignore the damage
-                       if (time > this.pain_finished)
-                               if (IS_PLAYER(attacker))
-                               {
-                                       play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
-                                       attacker.typehitsound += 1;
-                                       this.pain_finished = time + 1;
-                               }
-                       return;
-               }
-               if (time > this.pain_finished)
-               {
-                       this.pain_finished = time + 10;
-                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && SAME_TEAM(it, this), Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK));
-                       play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK));
-               }
-       }
-       this.health = this.health - damage;
-       WaypointSprite_UpdateHealth(this.sprite, this.health);
-       // choose an animation frame based on health
-       this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1);
-       // see if the generator is still functional, or dying
-       if (this.health > 0)
-       {
-               this.lasthealth = this.health;
-       }
-       else
-       {
-               if (attacker == this)
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME));
-               else
-               {
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED));
-                       PlayerScore_Add(attacker, SP_SCORE, 100);
-               }
-               this.iscaptured = false;
-               this.islinked = false;
-               this.isshielded = false;
-               this.takedamage = DAMAGE_NO; // can't be hurt anymore
-               this.event_damage = func_null; // won't do anything if hurt
-               this.count = 0; // reset counter
-               setthink(this, func_null);
-               this.nextthink = 0;
-               //this.think(); // do the first explosion now
-
-               WaypointSprite_UpdateMaxHealth(this.sprite, 0);
-               WaypointSprite_Ping(this.sprite);
-               //WaypointSprite_Kill(this.sprite); // can't do this yet, code too poor
-
-               onslaught_updatelinks();
-       }
-
-       // Throw some flaming gibs on damage, more damage = more chance for gib
-       if(random() < damage/220)
-       {
-               sound(this, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
-       }
-       else
-       {
-               // particles on every hit
-               Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1);
-
-               //sound on every hit
-               if (random() < 0.5)
-                       sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM);
-               else
-                       sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM);
-       }
-
-       this.SendFlags |= GSF_STATUS;
-}
-
-void ons_GeneratorThink(entity this)
-{
-       this.nextthink = time + GEN_THINKRATE;
-       if (!gameover)
-       {
-               if(!this.isshielded && this.wait < time)
-               {
-                       this.wait = time + 5;
-                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
-                               if(SAME_TEAM(it, this))
-                               {
-                                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM);
-                                       soundto(MSG_ONE, it, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE);    // FIXME: unique sound?
-                               }
-                               else
-                                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED));
-                       });
-               }
-       }
-}
-
-void ons_GeneratorReset(entity this)
-{
-       this.team = this.team_saved;
-       this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health;
-       this.takedamage = DAMAGE_AIM;
-       this.bot_attack = true;
-       this.iscaptured = true;
-       this.islinked = true;
-       this.isshielded = true;
-       this.event_damage = ons_GeneratorDamage;
-       setthink(this, ons_GeneratorThink);
-       this.nextthink = time + GEN_THINKRATE;
-
-       Net_LinkEntity(this, false, 0, generator_send);
-
-       this.SendFlags = GSF_SETUP; // just incase
-       this.SendFlags |= GSF_STATUS;
-
-       WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health);
-       WaypointSprite_UpdateHealth(this.sprite, this.health);
-       WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY);
-
-       onslaught_updatelinks();
-}
-
-void ons_DelayedGeneratorSetup(entity this)
-{
-       // bot waypoints
-       waypoint_spawnforitem_force(this, this.origin);
-       this.nearestwaypointtimeout = 0; // activate waypointing again
-       this.bot_basewaypoint = this.nearestwaypoint;
-
-       // captureshield setup
-       ons_CaptureShield_Spawn(this, true);
-
-       onslaught_updatelinks();
-
-       Net_LinkEntity(this, false, 0, generator_send);
-}
-
-
-void onslaught_generator_touch(entity this)
-{
-       if ( IS_PLAYER(other) )
-       if ( SAME_TEAM(this,other) )
-       if ( this.iscaptured )
-       {
-               Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT);
-       }
-}
-
-void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc
-{
-       // declarations
-       int teamnumber = gen.team;
-
-       // main setup
-       gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist
-       ons_worldgeneratorlist = gen;
-
-       gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber));
-       gen.classname = "onslaught_generator";
-       gen.solid = SOLID_BBOX;
-       gen.team_saved = teamnumber;
-       gen.movetype = MOVETYPE_NONE;
-       gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health;
-       gen.takedamage = DAMAGE_AIM;
-       gen.bot_attack = true;
-       gen.event_damage = ons_GeneratorDamage;
-       gen.reset = ons_GeneratorReset;
-       setthink(gen, ons_GeneratorThink);
-       gen.nextthink = time + GEN_THINKRATE;
-       gen.iscaptured = true;
-       gen.islinked = true;
-       gen.isshielded = true;
-       settouch(gen, onslaught_generator_touch);
-
-       // appearence
-       // model handled by CSQC
-       setsize(gen, GENERATOR_MIN, GENERATOR_MAX);
-       setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET));
-       gen.colormap = 1024 + (teamnumber - 1) * 17;
-
-       // generator placement
-       droptofloor(gen);
-
-       // waypointsprites
-       WaypointSprite_SpawnFixed(WP_Null, gen.origin + CPGEN_WAYPOINT_OFFSET, gen, sprite, RADARICON_NONE);
-       WaypointSprite_UpdateRule(gen.sprite, gen.team, SPRITERULE_TEAMPLAY);
-       WaypointSprite_UpdateMaxHealth(gen.sprite, gen.max_health);
-       WaypointSprite_UpdateHealth(gen.sprite, gen.health);
-
-       InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION);
-}
-
-
-// ===============
-//  Round Handler
-// ===============
-
-int total_generators;
-void Onslaught_count_generators()
-{
-       entity e;
-       total_generators = redowned = blueowned = yellowowned = pinkowned = 0;
-       for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
-       {
-               ++total_generators;
-               redowned += (e.team == NUM_TEAM_1 && e.health > 0);
-               blueowned += (e.team == NUM_TEAM_2 && e.health > 0);
-               yellowowned += (e.team == NUM_TEAM_3 && e.health > 0);
-               pinkowned += (e.team == NUM_TEAM_4 && e.health > 0);
-       }
-}
-
-int Onslaught_GetWinnerTeam()
-{
-       int winner_team = 0;
-       if(redowned > 0)
-               winner_team = NUM_TEAM_1;
-       if(blueowned > 0)
-       {
-               if(winner_team) return 0;
-               winner_team = NUM_TEAM_2;
-       }
-       if(yellowowned > 0)
-       {
-               if(winner_team) return 0;
-               winner_team = NUM_TEAM_3;
-       }
-       if(pinkowned > 0)
-       {
-               if(winner_team) return 0;
-               winner_team = NUM_TEAM_4;
-       }
-       if(winner_team)
-               return winner_team;
-       return -1; // no generators left?
-}
-
-void nades_Clear(entity e);
-
-#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
-#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1)
-bool Onslaught_CheckWinner()
-{
-       if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0))
-       {
-               ons_stalemate = true;
-
-               if (!wpforenemy_announced)
-               {
-                       Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
-                       sound(NULL, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE);
-
-                       wpforenemy_announced = true;
-               }
-
-               entity tmp_entity; // temporary entity
-               float d;
-               for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay)
-               {
-                       // tmp_entity.max_health / 300 gives 5 minutes of overtime.
-                       // control points reduce the overtime duration.
-                       d = 1;
-                       entity e;
-                       for(e = ons_worldcplist; e; e = e.ons_worldcpnext)
-                       {
-                               if(DIFF_TEAM(e, tmp_entity))
-                               if(e.islinked)
-                                       d = d + 1;
-                       }
-
-                       if(autocvar_g_campaign && autocvar__campaign_testrun)
-                               d = d * tmp_entity.max_health;
-                       else
-                               d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
-
-                       Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0');
-
-                       tmp_entity.sprite.SendFlags |= 16;
-
-                       tmp_entity.ons_overtime_damagedelay = time + 1;
-               }
-       }
-       else { wpforenemy_announced = false; ons_stalemate = false; }
-
-       Onslaught_count_generators();
-
-       if(ONS_OWNED_GENERATORS_OK())
-               return 0;
-
-       int winner_team = Onslaught_GetWinnerTeam();
-
-       if(winner_team > 0)
-       {
-               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
-               TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1);
-       }
-       else if(winner_team == -1)
-       {
-               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
-       }
-
-       ons_stalemate = false;
-
-       play2all(SND(CTF_CAPTURE(winner_team)));
-
-       round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
-
-       FOREACH_CLIENT(IS_PLAYER(it), {
-               it.ons_roundlost = true;
-               it.player_blocked = true;
-
-               nades_Clear(it);
-       });
-
-       return 1;
-}
-
-bool Onslaught_CheckPlayers()
-{
-       return 1;
-}
-
-void Onslaught_RoundStart()
-{
-       entity tmp_entity;
-       FOREACH_CLIENT(IS_PLAYER(it), it.player_blocked = false);
-
-       for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
-               tmp_entity.sprite.SendFlags |= 16;
-
-       for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
-               tmp_entity.sprite.SendFlags |= 16;
-}
-
-
-// ================
-// Bot player logic
-// ================
-
-// NOTE: LEGACY CODE, needs to be re-written!
-
-void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius)
-{
-       entity head;
-       float t, c;
-       bool needarmor = false, needweapons = false;
-
-       // Needs armor/health?
-       if(this.health<100)
-               needarmor = true;
-
-       // Needs weapons?
-       c = 0;
-       FOREACH(Weapons, it != WEP_Null, {
-               if(this.weapons & (it.m_wepset))
-               if(++c >= 4)
-                       break;
-       });
-
-       if(c<4)
-               needweapons = true;
-
-       if(!needweapons && !needarmor)
-               return;
-
-       LOG_DEBUG(strcat(this.netname, " needs weapons ", ftos(needweapons) , "\n"));
-       LOG_DEBUG(strcat(this.netname, " needs armor ", ftos(needarmor) , "\n"));
-
-       // See what is around
-       head = findchainfloat(bot_pickup, true);
-       while (head)
-       {
-               // gather health and armor only
-               if (head.solid)
-               if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
-               if (vdist(head.origin - org, <, sradius))
-               {
-                       t = head.bot_pickupevalfunc(this, head);
-                       if (t > 0)
-                               navigation_routerating(this, head, t * ratingscale, 500);
-               }
-               head = head.chain;
-       }
-}
-
-void havocbot_role_ons_setrole(entity this, int role)
-{
-       LOG_DEBUG(strcat(this.netname," switched to "));
-       switch(role)
-       {
-               case HAVOCBOT_ONS_ROLE_DEFENSE:
-                       LOG_DEBUG("defense");
-                       this.havocbot_role = havocbot_role_ons_defense;
-                       this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
-                       this.havocbot_role_timeout = 0;
-                       break;
-               case HAVOCBOT_ONS_ROLE_ASSISTANT:
-                       LOG_DEBUG("assistant");
-                       this.havocbot_role = havocbot_role_ons_assistant;
-                       this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
-                       this.havocbot_role_timeout = 0;
-                       break;
-               case HAVOCBOT_ONS_ROLE_OFFENSE:
-                       LOG_DEBUG("offense");
-                       this.havocbot_role = havocbot_role_ons_offense;
-                       this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
-                       this.havocbot_role_timeout = 0;
-                       break;
-       }
-       LOG_DEBUG("\n");
-}
-
-void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale)
-{
-       entity cp, cp1, cp2, best, wp;
-       float radius, bestvalue;
-       int c;
-       bool found;
-
-       // Filter control points
-       for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
-       {
-               cp2.wpcost = c = 0;
-               cp2.wpconsidered = false;
-
-               if(cp2.isshielded)
-                       continue;
-
-               // Ignore owned controlpoints
-               if(!(cp2.isgenneighbor[this.team] || cp2.iscpneighbor[this.team]))
-                       continue;
-
-               // Count team mates interested in this control point
-               // (easier and cleaner than keeping counters per cp and teams)
-               FOREACH_CLIENT(IS_PLAYER(it), {
-                       if(SAME_TEAM(it, this))
-                       if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
-                       if(it.havocbot_ons_target == cp2)
-                               ++c;
-               });
-
-               // NOTE: probably decrease the cost of attackable control points
-               cp2.wpcost = c;
-               cp2.wpconsidered = true;
-       }
-
-       // We'll consider only the best case
-       bestvalue = 99999999999;
-       cp = NULL;
-       for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext)
-       {
-               if (!cp1.wpconsidered)
-                       continue;
-
-               if(cp1.wpcost<bestvalue)
-               {
-                       bestvalue = cp1.wpcost;
-                       cp = cp1;
-                       this.havocbot_ons_target = cp1;
-               }
-       }
-
-       if (!cp)
-               return;
-
-       LOG_DEBUG(strcat(this.netname, " chose cp ranked ", ftos(bestvalue), "\n"));
-
-       if(cp.goalentity)
-       {
-               // Should be attacked
-               // Rate waypoints near it
-               found = false;
-               best = NULL;
-               bestvalue = 99999999999;
-               for(radius=0; radius<1000 && !found; radius+=500)
-               {
-                       for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
-                       {
-                               if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
-                               if(wp.classname=="waypoint")
-                               if(checkpvs(wp.origin,cp))
-                               {
-                                       found = true;
-                                       if(wp.cnt<bestvalue)
-                                       {
-                                               best = wp;
-                                               bestvalue = wp.cnt;
-                                       }
-                               }
-                       }
-               }
-
-               if(best)
-               {
-                       navigation_routerating(this, best, ratingscale, 10000);
-                       best.cnt += 1;
-
-                       this.havocbot_attack_time = 0;
-                       if(checkpvs(this.view_ofs,cp))
-                       if(checkpvs(this.view_ofs,best))
-                               this.havocbot_attack_time = time + 2;
-               }
-               else
-               {
-                       navigation_routerating(this, cp, ratingscale, 10000);
-               }
-               LOG_DEBUG(strcat(this.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n"));
-       }
-       else
-       {
-               // Should be touched
-               LOG_DEBUG(strcat(this.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n"));
-               found = false;
-
-               // Look for auto generated waypoint
-               if (!bot_waypoints_for_items)
-               for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
-               {
-                       if(wp.classname=="waypoint")
-                       {
-                               navigation_routerating(this, wp, ratingscale, 10000);
-                               found = true;
-                       }
-               }
-
-               // Nothing found, rate the controlpoint itself
-               if (!found)
-                       navigation_routerating(this, cp, ratingscale, 10000);
-       }
-}
-
-bool havocbot_goalrating_ons_generator_attack(entity this, float ratingscale)
-{
-       entity g, wp, bestwp;
-       bool found;
-       int best;
-
-       for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
-       {
-               if(SAME_TEAM(g, this) || g.isshielded)
-                       continue;
-
-               // Should be attacked
-               // Rate waypoints near it
-               found = false;
-               bestwp = NULL;
-               best = 99999999999;
-
-               for(wp=findradius(g.origin,400); wp; wp=wp.chain)
-               {
-                       if(wp.classname=="waypoint")
-                       if(checkpvs(wp.origin,g))
-                       {
-                               found = true;
-                               if(wp.cnt<best)
-                               {
-                                       bestwp = wp;
-                                       best = wp.cnt;
-                               }
-                       }
-               }
-
-               if(bestwp)
-               {
-                       LOG_DEBUG("waypoints found around generator\n");
-                       navigation_routerating(this, bestwp, ratingscale, 10000);
-                       bestwp.cnt += 1;
-
-                       this.havocbot_attack_time = 0;
-                       if(checkpvs(this.view_ofs,g))
-                       if(checkpvs(this.view_ofs,bestwp))
-                               this.havocbot_attack_time = time + 5;
-
-                       return true;
-               }
-               else
-               {
-                       LOG_DEBUG("generator found without waypoints around\n");
-                       // if there aren't waypoints near the generator go straight to it
-                       navigation_routerating(this, g, ratingscale, 10000);
-                       this.havocbot_attack_time = 0;
-                       return true;
-               }
-       }
-       return false;
-}
-
-void havocbot_role_ons_offense(entity this)
-{
-       if(IS_DEAD(this))
-       {
-               this.havocbot_attack_time = 0;
-               havocbot_ons_reset_role(this);
-               return;
-       }
-
-       // Set the role timeout if necessary
-       if (!this.havocbot_role_timeout)
-               this.havocbot_role_timeout = time + 120;
-
-       if (time > this.havocbot_role_timeout)
-       {
-               havocbot_ons_reset_role(this);
-               return;
-       }
-
-       if(this.havocbot_attack_time>time)
-               return;
-
-       if (this.bot_strategytime < time)
-       {
-               navigation_goalrating_start(this);
-               havocbot_goalrating_enemyplayers(this, 20000, this.origin, 650);
-               if(!havocbot_goalrating_ons_generator_attack(this, 20000))
-                       havocbot_goalrating_ons_controlpoints_attack(this, 20000);
-               havocbot_goalrating_ons_offenseitems(this, 10000, this.origin, 10000);
-               navigation_goalrating_end(this);
-
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-       }
-}
-
-void havocbot_role_ons_assistant(entity this)
-{
-       havocbot_ons_reset_role(this);
-}
-
-void havocbot_role_ons_defense(entity this)
-{
-       havocbot_ons_reset_role(this);
-}
-
-void havocbot_ons_reset_role(entity this)
-{
-       if(IS_DEAD(this))
-               return;
-
-       this.havocbot_ons_target = NULL;
-
-       // TODO: Defend control points or generator if necessary
-
-       havocbot_role_ons_setrole(this, HAVOCBOT_ONS_ROLE_OFFENSE);
-}
-
-
-/*
- * Find control point or generator owned by the same team self which is nearest to pos
- * if max_dist is positive, only control points within this range will be considered
- */
-entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist)
-{
-       entity tmp_entity, closest_target = NULL;
-       tmp_entity = findchain(classname, "onslaught_controlpoint");
-       while(tmp_entity)
-       {
-               if(SAME_TEAM(tmp_entity, this))
-               if(tmp_entity.iscaptured)
-               if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <=, max_dist))
-               if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
-                       closest_target = tmp_entity;
-               tmp_entity = tmp_entity.chain;
-       }
-       tmp_entity = findchain(classname, "onslaught_generator");
-       while(tmp_entity)
-       {
-               if(SAME_TEAM(tmp_entity, this))
-               if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <, max_dist))
-               if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
-                       closest_target = tmp_entity;
-               tmp_entity = tmp_entity.chain;
-       }
-
-       return closest_target;
-}
-
-/*
- * Find control point or generator owned by the same team self which is nearest to pos
- * if max_dist is positive, only control points within this range will be considered
- * This function only check distances on the XY plane, disregarding Z
- */
-entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist)
-{
-       entity tmp_entity, closest_target = NULL;
-       vector delta;
-       float smallest_distance = 0, distance;
-
-       tmp_entity = findchain(classname, "onslaught_controlpoint");
-       while(tmp_entity)
-       {
-               delta = tmp_entity.origin - pos;
-               delta_z = 0;
-               distance = vlen(delta);
-
-               if(SAME_TEAM(tmp_entity, this))
-               if(tmp_entity.iscaptured)
-               if(max_dist <= 0 || distance <= max_dist)
-               if(closest_target == NULL || distance <= smallest_distance )
-               {
-                       closest_target = tmp_entity;
-                       smallest_distance = distance;
-               }
-
-               tmp_entity = tmp_entity.chain;
-       }
-       tmp_entity = findchain(classname, "onslaught_generator");
-       while(tmp_entity)
-       {
-               delta = tmp_entity.origin - pos;
-               delta_z = 0;
-               distance = vlen(delta);
-
-               if(SAME_TEAM(tmp_entity, this))
-               if(max_dist <= 0 || distance <= max_dist)
-               if(closest_target == NULL || distance <= smallest_distance )
-               {
-                       closest_target = tmp_entity;
-                       smallest_distance = distance;
-               }
-
-               tmp_entity = tmp_entity.chain;
-       }
-
-       return closest_target;
-}
-/**
- * find the number of control points and generators in the same team as this
- */
-int ons_Count_SelfControlPoints(entity this)
-{
-       entity tmp_entity;
-       tmp_entity = findchain(classname, "onslaught_controlpoint");
-       int n = 0;
-       while(tmp_entity)
-       {
-               if(SAME_TEAM(tmp_entity, this))
-               if(tmp_entity.iscaptured)
-                       n++;
-               tmp_entity = tmp_entity.chain;
-       }
-       tmp_entity = findchain(classname, "onslaught_generator");
-       while(tmp_entity)
-       {
-               if(SAME_TEAM(tmp_entity, this))
-                       n++;
-               tmp_entity = tmp_entity.chain;
-       }
-       return n;
-}
-
-/**
- * Teleport player to a random position near tele_target
- * if tele_effects is true, teleport sound+particles are created
- * return false on failure
- */
-bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects)
-{
-       if ( !tele_target )
-               return false;
-
-       int i;
-       vector loc;
-       float theta;
-       // narrow the range for each iteration to increase chances that a spawnpoint
-       // can be found even if there's little room around the control point
-       float iteration_scale = 1;
-       for(i = 0; i < 16; ++i)
-       {
-               iteration_scale -= i / 16;
-               theta = random() * 2 * M_PI;
-               loc_y = sin(theta);
-               loc_x = cos(theta);
-               loc_z = 0;
-               loc *= random() * range * iteration_scale;
-
-               loc += tele_target.origin + '0 0 128' * iteration_scale;
-
-               tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player);
-               if(trace_fraction == 1.0 && !trace_startsolid)
-               {
-                       traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL
-                       if(trace_fraction == 1.0 && !trace_startsolid)
-                       {
-                               if ( tele_effects )
-                               {
-                                       Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1);
-                                       sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM);
-                               }
-                               setorigin(player, loc);
-                               player.angles = '0 1 0' * ( theta * RAD2DEG + 180 );
-                               makevectors(player.angles);
-                               player.fixangle = true;
-                               player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait;
-
-                               if ( tele_effects )
-                                       Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1);
-                               return true;
-                       }
-               }
-       }
-
-       return false;
-}
-
-// ==============
-// Hook Functions
-// ==============
-
-MUTATOR_HOOKFUNCTION(ons, reset_map_global)
-{
-       FOREACH_CLIENT(IS_PLAYER(it), {
-               it.ons_roundlost = false;
-               it.ons_deathloc = '0 0 0';
-               WITHSELF(it, PutClientInServer());
-       });
-       return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, ClientDisconnect)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.ons_deathloc = '0 0 0';
-}
-
-MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.ons_deathloc = '0 0 0';
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(!round_handler_IsRoundStarted())
-       {
-               player.player_blocked = true;
-               return false;
-       }
-
-       entity l;
-       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
-       {
-               l.sprite.SendFlags |= 16;
-       }
-       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
-       {
-               l.sprite.SendFlags |= 16;
-       }
-
-       if(ons_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
-
-       if ( autocvar_g_onslaught_spawn_choose )
-       if ( player.ons_spawn_by )
-       if ( ons_Teleport(player,player.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) )
-       {
-               player.ons_spawn_by = NULL;
-               return false;
-       }
-
-       if(autocvar_g_onslaught_spawn_at_controlpoints)
-       if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
-       {
-               float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random;
-               entity tmp_entity, closest_target = NULL;
-               vector spawn_loc = player.ons_deathloc;
-
-               // new joining player or round reset, don't bother checking
-               if(spawn_loc == '0 0 0') { return false; }
-
-               if(random_target) { RandomSelection_Init(); }
-
-               for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
-               {
-                       if(SAME_TEAM(tmp_entity, player))
-                       if(random_target)
-                               RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
-                       else if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL)
-                               closest_target = tmp_entity;
-               }
-
-               if(random_target) { closest_target = RandomSelection_chosen_ent; }
-
-               if(closest_target)
-               {
-                       float i;
-                       vector loc;
-                       float iteration_scale = 1;
-                       for(i = 0; i < 10; ++i)
-                       {
-                               iteration_scale -= i / 10;
-                               loc = closest_target.origin + '0 0 96' * iteration_scale;
-                               loc += ('0 1 0' * random()) * 128 * iteration_scale;
-                               tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player);
-                               if(trace_fraction == 1.0 && !trace_startsolid)
-                               {
-                                       traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL
-                                       if(trace_fraction == 1.0 && !trace_startsolid)
-                                       {
-                                               setorigin(player, loc);
-                                               player.angles = normalize(loc - closest_target.origin) * RAD2DEG;
-                                               return false;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       if(autocvar_g_onslaught_spawn_at_generator)
-       if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
-       {
-               float random_target = autocvar_g_onslaught_spawn_at_generator_random;
-               entity tmp_entity, closest_target = NULL;
-               vector spawn_loc = player.ons_deathloc;
-
-               // new joining player or round reset, don't bother checking
-               if(spawn_loc == '0 0 0') { return false; }
-
-               if(random_target) { RandomSelection_Init(); }
-
-               for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
-               {
-                       if(random_target)
-                               RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
-                       else
-                       {
-                               if(SAME_TEAM(tmp_entity, player))
-                               if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL)
-                                       closest_target = tmp_entity;
-                       }
-               }
-
-               if(random_target) { closest_target = RandomSelection_chosen_ent; }
-
-               if(closest_target)
-               {
-                       float i;
-                       vector loc;
-                       float iteration_scale = 1;
-                       for(i = 0; i < 10; ++i)
-                       {
-                               iteration_scale -= i / 10;
-                               loc = closest_target.origin + '0 0 128' * iteration_scale;
-                               loc += ('0 1 0' * random()) * 256 * iteration_scale;
-                               tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player);
-                               if(trace_fraction == 1.0 && !trace_startsolid)
-                               {
-                                       traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL
-                                       if(trace_fraction == 1.0 && !trace_startsolid)
-                                       {
-                                               setorigin(player, loc);
-                                               player.angles = normalize(loc - closest_target.origin) * RAD2DEG;
-                                               return false;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayerDies)
-{
-       entity frag_target = M_ARGV(2, entity);
-
-       frag_target.ons_deathloc = frag_target.origin;
-       entity l;
-       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
-       {
-               l.sprite.SendFlags |= 16;
-       }
-       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
-       {
-               l.sprite.SendFlags |= 16;
-       }
-
-       if ( autocvar_g_onslaught_spawn_choose )
-       if ( ons_Count_SelfControlPoints(frag_target) > 1 )
-               stuffcmd(frag_target, "qc_cmd_cl hud clickradar\n");
-
-       return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, MonsterMove)
-{
-       entity mon = M_ARGV(0, entity);
-
-       entity e = find(NULL, targetname, mon.target);
-       if (e != NULL)
-               mon.team = e.team;
-}
-
-void ons_MonsterSpawn_Delayed(entity this)
-{
-       entity own = this.owner;
-
-       if(!own) { remove(this); return; }
-
-       if(own.targetname)
-       {
-               entity e = find(NULL, target, own.targetname);
-               if(e != NULL)
-               {
-                       own.team = e.team;
-
-                       own.use(own, e, NULL);
-               }
-       }
-
-       remove(this);
-}
-
-MUTATOR_HOOKFUNCTION(ons, MonsterSpawn)
-{
-       entity mon = M_ARGV(0, entity);
-
-       entity e = spawn();
-       e.owner = mon;
-       InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET);
-}
-
-void ons_TurretSpawn_Delayed(entity this)
-{
-       entity own = this.owner;
-
-       if(!own) { remove(this); return; }
-
-       if(own.targetname)
-       {
-               entity e = find(NULL, target, own.targetname);
-               if(e != NULL)
-               {
-                       own.team = e.team;
-                       own.active = ACTIVE_NOT;
-
-                       own.use(own, e, NULL);
-               }
-       }
+#ifndef MENUQC
+REGISTER_NET_LINKED(ENT_ONSCAMERA)
+#endif
 
-       remove(this);
-}
+#ifdef CSQC
 
-MUTATOR_HOOKFUNCTION(ons, TurretSpawn)
+entity generator_camera;
+NET_HANDLE(ENT_ONSCAMERA, bool isnew)
 {
-       entity turret = M_ARGV(0, entity);
+       this.origin_x = ReadCoord();
+       this.origin_y = ReadCoord();
+       this.origin_z = ReadCoord();
+       setorigin(this, this.origin);
 
-       entity e = spawn();
-       e.owner = turret;
-       InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
+       this.angles_x = ReadAngle();
+       this.angles_y = ReadAngle();
+       this.angles_z = ReadAngle();
 
-       return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole)
-{
-       entity bot = M_ARGV(0, entity);
+       this.drawmask  = MASK_NORMAL;
+       setmodel(this, MDL_Null); // give it a size for clientcamera
+       setsize(this, '-1 -1 -1', '1 1 1');
 
-       havocbot_ons_reset_role(bot);
+       generator_camera = this;
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ons, GetTeamCount)
+REGISTER_MUTATOR(cl_ons, true);
+
+float ons_roundlost;
+vector generator_origin;
+vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80';
+float autocvar_cl_eventchase_generator_distance = 400;
+MUTATOR_HOOKFUNCTION(cl_ons, WantEventchase)
 {
-       // onslaught is special
-       for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+       ons_roundlost = STAT(ROUNDLOST);
+       entity gen = NULL;
+       if(ons_roundlost)
        {
-               switch(tmp_entity.team)
+               IL_EACH(g_onsgenerators, it.health <= 0,
                {
-                       case NUM_TEAM_1: c1 = 0; break;
-                       case NUM_TEAM_2: c2 = 0; break;
-                       case NUM_TEAM_3: c3 = 0; break;
-                       case NUM_TEAM_4: c4 = 0; break;
-               }
+                       gen = it;
+                       break;
+               });
+               if(!gen)
+                       ons_roundlost = false; // don't enforce the 3rd person camera if there is no dead generator to show
        }
 
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(ons, SpectateCopy)
-{
-       entity spectatee = M_ARGV(0, entity);
-       entity client = M_ARGV(1, entity);
-
-       client.ons_roundlost = spectatee.ons_roundlost; // make spectators see it too
-}
-
-MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand)
-{
-       if(MUTATOR_RETURNVALUE) // command was already handled?
-               return false;
-
-       entity player = M_ARGV(0, entity);
-       string cmd_name = M_ARGV(1, string);
-       int cmd_argc = M_ARGV(2, int);
-
-       if ( cmd_name == "ons_spawn" )
+       if(ons_roundlost)
        {
-               vector pos = player.origin;
-               if(cmd_argc > 1)
-                       pos_x = stof(argv(1));
-               if(cmd_argc > 2)
-                       pos_y = stof(argv(2));
-               if(cmd_argc > 3)
-                       pos_z = stof(argv(3));
-
-               if ( IS_PLAYER(player) )
-               {
-                       if ( !STAT(FROZEN, player) )
-                       {
-                               entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius);
-
-                               if ( !source_point && player.health > 0 )
-                               {
-                                       sprint(player, "\nYou need to be next to a control point\n");
-                                       return true;
-                               }
-
-
-                               entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius);
-
-                               if ( closest_target == NULL )
-                               {
-                                       sprint(player, "\nNo control point found\n");
-                                       return true;
-                               }
-
-                               if ( player.health <= 0 )
-                               {
-                                       player.ons_spawn_by = closest_target;
-                                       player.respawn_flags = player.respawn_flags | RESPAWN_FORCE;
-                               }
-                               else
-                               {
-                                       if ( source_point == closest_target )
-                                       {
-                                               sprint(player, "\nTeleporting to the same point\n");
-                                               return true;
-                                       }
-
-                                       if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) )
-                                               sprint(player, "\nUnable to teleport there\n");
-                               }
-
-                               return true;
-                       }
-
-                       sprint(player, "\nNo teleportation for you\n");
-               }
-
+               generator_origin = gen.origin;
                return true;
        }
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ons, PlayerUseKey)
+MUTATOR_HOOKFUNCTION(cl_ons, CustomizeEventchase)
 {
-       if(MUTATOR_RETURNVALUE || gameover) { return false; }
-
-       entity player = M_ARGV(0, entity);
-
-       if((time > player.teleport_antispam) && (!IS_DEAD(player)) && !player.vehicle)
-       {
-               entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius);
-               if ( source_point )
-               {
-                       stuffcmd(player, "qc_cmd_cl hud clickradar\n");
-                       return true;
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayHitsound)
-{
-       entity frag_victim = M_ARGV(0, entity);
-
-       return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded)
-               || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded);
-}
-
-MUTATOR_HOOKFUNCTION(ons, SendWaypoint)
-{
-    entity wp = M_ARGV(0, entity);
-    entity to = M_ARGV(1, entity);
-    int sf = M_ARGV(2, int);
-    int wp_flag = M_ARGV(3, int);
-
-       if(sf & 16)
-       {
-               if(wp.owner.classname == "onslaught_controlpoint")
-               {
-                       entity wp_owner = wp.owner;
-                       entity e = WaypointSprite_getviewentity(to);
-                       if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; }
-                       if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; }
-               }
-               if(wp.owner.classname == "onslaught_generator")
-               {
-                       entity wp_owner = wp.owner;
-                       if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; }
-                       if(wp_owner.health <= 0) { wp_flag |= 2; }
-               }
-       }
-
-       M_ARGV(3, int) = wp_flag;
-}
-
-MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget)
-{
-       entity turret_target = M_ARGV(1, entity);
-
-       if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!
+       if(ons_roundlost)
        {
-               M_ARGV(3, float) = -3;
+               M_ARGV(0, vector) = generator_camera.origin;
+               M_ARGV(1, vector) = autocvar_cl_eventchase_generator_viewoffset;
+               M_ARGV(0, float) = autocvar_cl_eventchase_generator_distance;
                return true;
        }
-
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ons, TurretThink)
-{
-    entity turret = M_ARGV(0, entity);
-
-       // ONS uses somewhat backwards linking.
-       if(turret.target)
-       {
-               entity e = find(NULL, targetname, turret.target);
-               if (e != NULL)
-                       turret.team = e.team;
-       }
-
-       if(turret.team != turret.tur_head.team)
-               turret_respawn(turret);
-}
-
-
-// ==========
-// Spawnfuncs
-// ==========
-
-/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
-  Link between control points.
-
-  This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
-
-keys:
-"target" - first control point.
-"target2" - second control point.
- */
-spawnfunc(onslaught_link)
-{
-       if(!g_onslaught) { remove(this); return; }
-
-       if (this.target == "" || this.target2 == "")
-               objerror(this, "target and target2 must be set\n");
-
-       this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
-       ons_worldlinklist = this;
-
-       InitializeEntity(this, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
-       Net_LinkEntity(this, false, 0, ons_Link_Send);
-}
-
-/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
-  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
-
-  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
-
-keys:
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
-"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
-"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
- */
-
-spawnfunc(onslaught_controlpoint)
-{
-       if(!g_onslaught) { remove(this); return; }
-
-       ons_ControlPoint_Setup(this);
-}
-
-/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
-  Base generator.
-
-  spawnfunc_onslaught_link entities can target this.
-
-keys:
-"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
- */
-spawnfunc(onslaught_generator)
-{
-       if(!g_onslaught) { remove(this); return; }
-       if(!this.team) { objerror(this, "team must be set"); }
-
-       ons_GeneratorSetup(this);
-}
-
-// scoreboard setup
-void ons_ScoreRules()
-{
-       CheckAllowedTeams(NULL);
-       ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true);
-       ScoreInfo_SetLabel_TeamScore  (ST_ONS_CAPS,     "destroyed", SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES,    "takes",     0);
-       ScoreRules_basics_end();
-}
-
-void ons_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up
-{
-       ons_ScoreRules();
-
-       round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart);
-       round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
-}
-
-void ons_Initialize()
-{
-       g_onslaught = true;
-       ons_captureshield_force = autocvar_g_onslaught_shield_force;
-
-       InitializeEntity(NULL, ons_DelayedInit, INITPRIO_GAMETYPE);
-}
-
 #endif
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh
new file mode 100644 (file)
index 0000000..c863e2f
--- /dev/null
@@ -0,0 +1,14 @@
+#pragma once
+
+REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR)
+REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON)
+
+#ifdef SVQC
+IntrusiveList g_onsshields;
+STATIC_INIT(g_onsshields) { g_onsshields = IL_NEW(); }
+#endif
+
+#ifdef CSQC
+IntrusiveList g_onsgenerators;
+STATIC_INIT(g_onsgenerators) { g_onsgenerators = IL_NEW(); }
+#endif
index 491acdd007d8635cac7417c2bd5acdc11c6cff95..0941833de8acd7430cc7fb35cef56c104bf405bf 100644 (file)
@@ -5,6 +5,8 @@
 bool cpicon_send(entity this, entity to, int sf)
 {
        WriteHeader(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON);
+       if(sf & CPSF_SETUP)
+               sf &= ~CPSF_STATUS;
        WriteByte(MSG_ENTITY, sf);
        if(sf & CPSF_SETUP)
        {
index d76f0ea069998ecc00bb61ed2c593a18f9c1db91..d5437338e3fb47ac0432e6de3b027bd81a366eec 100644 (file)
@@ -1,10 +1,7 @@
-#ifndef CONTROLPOINT_H
-#define CONTROLPOINT_H
+#pragma once
 
 const vector CPICON_MIN = '-32 -32 -9';
 const vector CPICON_MAX = '32 32 25';
 
 const int CPSF_STATUS = 4;
 const int CPSF_SETUP = 8;
-
-#endif
index 003c2b1d6855f1f93fed3d1ae078dbf2e8c841b9..8356689e204d7a6910c7fafd670c630fe4adf00b 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef GENERATOR_H
-#define GENERATOR_H
+#pragma once
+
 const vector GENERATOR_MIN = '-52 -52 -14';
 const vector GENERATOR_MAX = '52 52 75';
 
@@ -7,4 +7,3 @@ const int GSF_STATUS = 4;
 const int GSF_SETUP = 8;
 
 bool generator_send(entity this, entity to, int sf);
-#endif
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
new file mode 100644 (file)
index 0000000..195a3bd
--- /dev/null
@@ -0,0 +1,2198 @@
+#include "sv_onslaught.qh"
+#include "sv_controlpoint.qh"
+#include "sv_generator.qh"
+
+bool g_onslaught;
+
+float autocvar_g_onslaught_teleport_wait;
+bool autocvar_g_onslaught_spawn_at_controlpoints;
+bool autocvar_g_onslaught_spawn_at_generator;
+float autocvar_g_onslaught_cp_proxydecap;
+float autocvar_g_onslaught_cp_proxydecap_distance = 512;
+float autocvar_g_onslaught_cp_proxydecap_dps = 100;
+float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
+float autocvar_g_onslaught_spawn_at_controlpoints_random;
+float autocvar_g_onslaught_spawn_at_generator_chance;
+float autocvar_g_onslaught_spawn_at_generator_random;
+float autocvar_g_onslaught_cp_buildhealth;
+float autocvar_g_onslaught_cp_buildtime;
+float autocvar_g_onslaught_cp_health;
+float autocvar_g_onslaught_cp_regen;
+float autocvar_g_onslaught_gen_health;
+float autocvar_g_onslaught_shield_force = 100;
+float autocvar_g_onslaught_allow_vehicle_touch;
+float autocvar_g_onslaught_round_timelimit;
+float autocvar_g_onslaught_warmup;
+float autocvar_g_onslaught_teleport_radius;
+float autocvar_g_onslaught_spawn_choose;
+float autocvar_g_onslaught_click_radius;
+
+void FixSize(entity e);
+entity cam;
+
+// =======================
+// CaptureShield Functions
+// =======================
+
+bool clientcamera_send(entity this, entity to, int sf)
+{
+       WriteHeader(MSG_ENTITY, ENT_ONSCAMERA);
+
+       WriteCoord(MSG_ENTITY, this.origin_x);
+       WriteCoord(MSG_ENTITY, this.origin_y);
+       WriteCoord(MSG_ENTITY, this.origin_z);
+
+       WriteAngle(MSG_ENTITY, this.angles_x);
+       WriteAngle(MSG_ENTITY, this.angles_y);
+       WriteAngle(MSG_ENTITY, this.angles_z);
+
+       return true;
+}
+
+bool ons_CaptureShield_Customize(entity this, entity client)
+{
+       entity e = WaypointSprite_getviewentity(client);
+
+       if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, e.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return false; }
+       if(SAME_TEAM(this, e)) { return false; }
+
+       return true;
+}
+
+void ons_CaptureShield_Touch(entity this, entity toucher)
+{
+       if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, toucher.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return; }
+       if(!IS_PLAYER(toucher)) { return; }
+       if(SAME_TEAM(toucher, this)) { return; }
+
+       vector mymid = (this.absmin + this.absmax) * 0.5;
+       vector theirmid = (toucher.absmin + toucher.absmax) * 0.5;
+
+       Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(theirmid - mymid) * ons_captureshield_force);
+
+       if(IS_REAL_CLIENT(toucher))
+       {
+               play2(toucher, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
+
+               if(this.enemy.classname == "onslaught_generator")
+                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED);
+               else
+                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED);
+       }
+}
+
+void ons_CaptureShield_Reset(entity this)
+{
+       this.colormap = this.enemy.colormap;
+       this.team = this.enemy.team;
+}
+
+void ons_CaptureShield_Spawn(entity generator, bool is_generator)
+{
+       entity shield = new(ons_captureshield);
+       IL_PUSH(g_onsshields, shield);
+
+       shield.enemy = generator;
+       shield.team = generator.team;
+       shield.colormap = generator.colormap;
+       shield.reset = ons_CaptureShield_Reset;
+       settouch(shield, ons_CaptureShield_Touch);
+       setcefc(shield, ons_CaptureShield_Customize);
+       shield.effects = EF_ADDITIVE;
+       set_movetype(shield, MOVETYPE_NOCLIP);
+       shield.solid = SOLID_TRIGGER;
+       shield.avelocity = '7 0 11';
+       shield.scale = 1;
+       shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3");
+
+       precache_model(shield.model);
+       setorigin(shield, generator.origin);
+       _setmodel(shield, shield.model);
+       setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
+}
+
+
+// ==========
+// Junk Pile
+// ==========
+
+void setmodel_fixsize(entity e, Model m)
+{
+       setmodel(e, m);
+       FixSize(e);
+}
+
+void onslaught_updatelinks()
+{
+       entity l;
+       // first check if the game has ended
+       LOG_DEBUG("--- updatelinks ---");
+       // mark generators as being shielded and networked
+       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+       {
+               if (l.iscaptured)
+                       LOG_DEBUG(etos(l), " (generator) belongs to team ", ftos(l.team));
+               else
+                       LOG_DEBUG(etos(l), " (generator) is destroyed");
+               l.islinked = l.iscaptured;
+               l.isshielded = l.iscaptured;
+               l.sprite.SendFlags |= 16;
+       }
+       // mark points as shielded and not networked
+       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+       {
+               l.islinked = false;
+               l.isshielded = true;
+               int i;
+               for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; }
+               LOG_DEBUG(etos(l), " (point) belongs to team ", ftos(l.team));
+               l.sprite.SendFlags |= 16;
+       }
+       // flow power outward from the generators through the network
+       bool stop = false;
+       while (!stop)
+       {
+               stop = true;
+               for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
+               {
+                       // if both points are captured by the same team, and only one of
+                       // them is powered, mark the other one as powered as well
+                       if (l.enemy.iscaptured && l.goalentity.iscaptured)
+                               if (l.enemy.islinked != l.goalentity.islinked)
+                                       if(SAME_TEAM(l.enemy, l.goalentity))
+                                       {
+                                               if (!l.goalentity.islinked)
+                                               {
+                                                       stop = false;
+                                                       l.goalentity.islinked = true;
+                                                       LOG_DEBUG(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)");
+                                               }
+                                               else if (!l.enemy.islinked)
+                                               {
+                                                       stop = false;
+                                                       l.enemy.islinked = true;
+                                                       LOG_DEBUG(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)");
+                                               }
+                                       }
+               }
+       }
+       // now that we know which points are powered we can mark their neighbors
+       // as unshielded if team differs
+       for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
+       {
+               if (l.goalentity.islinked)
+               {
+                       if(DIFF_TEAM(l.goalentity, l.enemy))
+                       {
+                               LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)");
+                               l.enemy.isshielded = false;
+                       }
+                       if(l.goalentity.classname == "onslaught_generator")
+                               l.enemy.isgenneighbor[l.goalentity.team] = true;
+                       else
+                               l.enemy.iscpneighbor[l.goalentity.team] = true;
+               }
+               if (l.enemy.islinked)
+               {
+                       if(DIFF_TEAM(l.goalentity, l.enemy))
+                       {
+                               LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)");
+                               l.goalentity.isshielded = false;
+                       }
+                       if(l.enemy.classname == "onslaught_generator")
+                               l.goalentity.isgenneighbor[l.enemy.team] = true;
+                       else
+                               l.goalentity.iscpneighbor[l.enemy.team] = true;
+               }
+       }
+       // now update the generators
+       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+       {
+               if (l.isshielded)
+               {
+                       LOG_DEBUG(etos(l), " (generator) is shielded");
+                       l.takedamage = DAMAGE_NO;
+                       if(l.bot_attack)
+                               IL_REMOVE(g_bot_targets, l);
+                       l.bot_attack = false;
+               }
+               else
+               {
+                       LOG_DEBUG(etos(l), " (generator) is not shielded");
+                       l.takedamage = DAMAGE_AIM;
+                       if(!l.bot_attack)
+                               IL_PUSH(g_bot_targets, l);
+                       l.bot_attack = true;
+               }
+
+               ons_Generator_UpdateSprite(l);
+       }
+       // now update the takedamage and alpha variables on control point icons
+       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+       {
+               if (l.isshielded)
+               {
+                       LOG_DEBUG(etos(l), " (point) is shielded");
+                       if (l.goalentity)
+                       {
+                               l.goalentity.takedamage = DAMAGE_NO;
+                               if(l.goalentity.bot_attack)
+                                       IL_REMOVE(g_bot_targets, l.goalentity);
+                               l.goalentity.bot_attack = false;
+                       }
+               }
+               else
+               {
+                       LOG_DEBUG(etos(l), " (point) is not shielded");
+                       if (l.goalentity)
+                       {
+                               l.goalentity.takedamage = DAMAGE_AIM;
+                               if(!l.goalentity.bot_attack)
+                                       IL_PUSH(g_bot_targets, l.goalentity);
+                               l.goalentity.bot_attack = true;
+                       }
+               }
+               ons_ControlPoint_UpdateSprite(l);
+       }
+       IL_EACH(g_onsshields, true,
+       {
+               it.team = it.enemy.team;
+               it.colormap = it.enemy.colormap;
+       });
+}
+
+
+// ===================
+// Main Link Functions
+// ===================
+
+bool ons_Link_Send(entity this, entity to, int sendflags)
+{
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK);
+       WriteByte(MSG_ENTITY, sendflags);
+       if(sendflags & 1)
+       {
+               WriteCoord(MSG_ENTITY, this.goalentity.origin_x);
+               WriteCoord(MSG_ENTITY, this.goalentity.origin_y);
+               WriteCoord(MSG_ENTITY, this.goalentity.origin_z);
+       }
+       if(sendflags & 2)
+       {
+               WriteCoord(MSG_ENTITY, this.enemy.origin_x);
+               WriteCoord(MSG_ENTITY, this.enemy.origin_y);
+               WriteCoord(MSG_ENTITY, this.enemy.origin_z);
+       }
+       if(sendflags & 4)
+       {
+               WriteByte(MSG_ENTITY, this.clientcolors); // which is goalentity's color + enemy's color * 16
+       }
+       return true;
+}
+
+void ons_Link_CheckUpdate(entity this)
+{
+       // TODO check if the two sides have moved (currently they won't move anyway)
+       float cc = 0, cc1 = 0, cc2 = 0;
+
+       if(this.goalentity.islinked || this.goalentity.iscaptured) { cc1 = (this.goalentity.team - 1) * 0x01; }
+       if(this.enemy.islinked || this.enemy.iscaptured) { cc2 = (this.enemy.team - 1) * 0x10; }
+
+       cc = cc1 + cc2;
+
+       if(cc != this.clientcolors)
+       {
+               this.clientcolors = cc;
+               this.SendFlags |= 4;
+       }
+
+       this.nextthink = time;
+}
+
+void ons_DelayedLinkSetup(entity this)
+{
+       this.goalentity = find(NULL, targetname, this.target);
+       this.enemy = find(NULL, targetname, this.target2);
+       if(!this.goalentity) { objerror(this, "can not find target\n"); }
+       if(!this.enemy) { objerror(this, "can not find target2\n"); }
+
+       LOG_DEBUG(etos(this.goalentity), " linked with ", etos(this.enemy));
+       this.SendFlags |= 3;
+       setthink(this, ons_Link_CheckUpdate);
+       this.nextthink = time;
+}
+
+
+// =============================
+// Main Control Point Functions
+// =============================
+
+int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber)
+{
+       if(cp.isgenneighbor[teamnumber]) { return 2; }
+       if(cp.iscpneighbor[teamnumber]) { return 1; }
+
+       return 0;
+}
+
+int ons_ControlPoint_Attackable(entity cp, int teamnumber)
+       // -2: SAME TEAM, attackable by enemy!
+       // -1: SAME TEAM!
+       // 0: off limits
+       // 1: attack it
+       // 2: touch it
+       // 3: attack it (HIGH PRIO)
+       // 4: touch it (HIGH PRIO)
+{
+       int a;
+
+       if(cp.isshielded)
+       {
+               return 0;
+       }
+       else if(cp.goalentity)
+       {
+               // if there's already an icon built, nothing happens
+               if(cp.team == teamnumber)
+               {
+                       a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
+                       if(a) // attackable by enemy?
+                               return -2; // EMERGENCY!
+                       return -1;
+               }
+               // we know it can be linked, so no need to check
+               // but...
+               a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
+               if(a == 2) // near our generator?
+                       return 3; // EMERGENCY!
+               return 1;
+       }
+       else
+       {
+               // free point
+               if(ons_ControlPoint_CanBeLinked(cp, teamnumber))
+               {
+                       a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t
+                       if(a == 2)
+                               return 4; // GET THIS ONE NOW!
+                       else
+                               return 2; // TOUCH ME
+               }
+       }
+       return 0;
+}
+
+void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if(damage <= 0) { return; }
+
+       if (this.owner.isshielded)
+       {
+               // this is protected by a shield, so ignore the damage
+               if (time > this.pain_finished)
+                       if (IS_PLAYER(attacker))
+                       {
+                               play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
+                               this.pain_finished = time + 1;
+                               attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
+                       }
+
+               return;
+       }
+
+       if(IS_PLAYER(attacker))
+       if(time - ons_notification_time[this.team] > 10)
+       {
+               play2team(this.team, SND(ONS_CONTROLPOINT_UNDERATTACK));
+               ons_notification_time[this.team] = time;
+       }
+
+       this.health = this.health - damage;
+       if(this.owner.iscaptured)
+               WaypointSprite_UpdateHealth(this.owner.sprite, this.health);
+       else
+               WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE));
+       this.pain_finished = time + 1;
+       // particles on every hit
+       pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1);
+       //sound on every hit
+       if (random() < 0.5)
+               sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM);
+       else
+               sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM);
+
+       if (this.health < 0)
+       {
+               sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
+               pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_CPDESTROYED), this.owner.message, attacker.netname);
+
+               PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
+               PlayerScore_Add(attacker, SP_SCORE, 10);
+
+               this.owner.goalentity = NULL;
+               this.owner.islinked = false;
+               this.owner.iscaptured = false;
+               this.owner.team = 0;
+               this.owner.colormap = 1024;
+
+               WaypointSprite_UpdateMaxHealth(this.owner.sprite, 0);
+
+               onslaught_updatelinks();
+
+               // Use targets now (somebody make sure this is in the right place..)
+               SUB_UseTargets(this.owner, this, NULL);
+
+               this.owner.waslinked = this.owner.islinked;
+               if(this.owner.model != "models/onslaught/controlpoint_pad.md3")
+                       setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1);
+               //setsize(this, '-32 -32 0', '32 32 8');
+
+               delete(this);
+       }
+
+       this.SendFlags |= CPSF_STATUS;
+}
+
+void ons_ControlPoint_Icon_Think(entity this)
+{
+       this.nextthink = time + ONS_CP_THINKRATE;
+
+       if(autocvar_g_onslaught_cp_proxydecap)
+       {
+               int _enemy_count = 0;
+               int _friendly_count = 0;
+
+               FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
+                       if(vdist(it.origin - this.origin, <, autocvar_g_onslaught_cp_proxydecap_distance))
+                       {
+                               if(SAME_TEAM(it, this))
+                                       ++_friendly_count;
+                               else
+                                       ++_enemy_count;
+                       }
+               });
+
+               _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
+               _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
+
+               this.health = bound(0, this.health + (_friendly_count - _enemy_count), this.max_health);
+               this.SendFlags |= CPSF_STATUS;
+               if(this.health <= 0)
+               {
+                       ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, this.origin, '0 0 0');
+                       return;
+               }
+       }
+
+       if (time > this.pain_finished + 5)
+       {
+               if(this.health < this.max_health)
+               {
+                       this.health = this.health + this.count;
+                       if (this.health >= this.max_health)
+                               this.health = this.max_health;
+                       WaypointSprite_UpdateHealth(this.owner.sprite, this.health);
+               }
+       }
+
+       if(this.owner.islinked != this.owner.waslinked)
+       {
+               // unteam the spawnpoint if needed
+               int t = this.owner.team;
+               if(!this.owner.islinked)
+                       this.owner.team = 0;
+
+               SUB_UseTargets(this.owner, this, NULL);
+
+               this.owner.team = t;
+
+               this.owner.waslinked = this.owner.islinked;
+       }
+
+       // damaged fx
+       if(random() < 0.6 - this.health / this.max_health)
+       {
+               Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
+
+               if(random() > 0.8)
+                       sound(this, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM);
+               else if (random() > 0.5)
+                       sound(this, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM);
+       }
+}
+
+void ons_ControlPoint_Icon_BuildThink(entity this)
+{
+       int a;
+
+       this.nextthink = time + ONS_CP_THINKRATE;
+
+       // only do this if there is power
+       a = ons_ControlPoint_CanBeLinked(this.owner, this.owner.team);
+       if(!a)
+               return;
+
+       this.health = this.health + this.count;
+
+       this.SendFlags |= CPSF_STATUS;
+
+       if (this.health >= this.max_health)
+       {
+               this.health = this.max_health;
+               this.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
+               setthink(this, ons_ControlPoint_Icon_Think);
+               sound(this, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM);
+               this.owner.iscaptured = true;
+               this.solid = SOLID_BBOX;
+
+               Send_Effect(EFFECT_CAP(this.owner.team), this.owner.origin, '0 0 0', 1);
+
+               WaypointSprite_UpdateMaxHealth(this.owner.sprite, this.max_health);
+               WaypointSprite_UpdateHealth(this.owner.sprite, this.health);
+
+               if(IS_PLAYER(this.owner.ons_toucher))
+               {
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, this.owner.ons_toucher.netname, this.owner.message);
+                       Send_Notification(NOTIF_ALL_EXCEPT, this.owner.ons_toucher, MSG_CENTER, APP_TEAM_NUM(this.owner.ons_toucher.team, CENTER_ONS_CAPTURE), this.owner.message);
+                       Send_Notification(NOTIF_ONE, this.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, this.owner.message);
+                       PlayerScore_Add(this.owner.ons_toucher, SP_ONS_CAPS, 1);
+                       PlayerTeamScore_AddScore(this.owner.ons_toucher, 10);
+               }
+
+               this.owner.ons_toucher = NULL;
+
+               onslaught_updatelinks();
+
+               // Use targets now (somebody make sure this is in the right place..)
+               SUB_UseTargets(this.owner, this, NULL);
+
+               this.SendFlags |= CPSF_SETUP;
+       }
+       if(this.owner.model != MDL_ONS_CP_PAD2.model_str())
+               setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2);
+
+       if(random() < 0.9 - this.health / this.max_health)
+               Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1);
+}
+
+void onslaught_controlpoint_icon_link(entity e, void(entity this) spawnproc);
+
+void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
+{
+       entity e = new(onslaught_controlpoint_icon);
+
+       setsize(e, CPICON_MIN, CPICON_MAX);
+       setorigin(e, cp.origin + CPICON_OFFSET);
+
+       e.owner = cp;
+       e.max_health = autocvar_g_onslaught_cp_health;
+       e.health = autocvar_g_onslaught_cp_buildhealth;
+       e.solid = SOLID_NOT;
+       e.takedamage = DAMAGE_AIM;
+       e.bot_attack = true;
+       IL_PUSH(g_bot_targets, e);
+       e.event_damage = ons_ControlPoint_Icon_Damage;
+       e.team = player.team;
+       e.colormap = 1024 + (e.team - 1) * 17;
+       e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
+
+       sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM);
+
+       cp.goalentity = e;
+       cp.team = e.team;
+       cp.colormap = e.colormap;
+
+       Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1);
+
+       WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
+       WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
+       cp.sprite.SendFlags |= 16;
+
+       onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
+}
+
+entity ons_ControlPoint_Waypoint(entity e)
+{
+       if(e.team)
+       {
+               int a = ons_ControlPoint_Attackable(e, e.team);
+
+               if(a == -2) { return WP_OnsCPDefend; } // defend now
+               if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch
+               if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack
+       }
+       else
+               return WP_OnsCP;
+
+       return WP_Null;
+}
+
+void ons_ControlPoint_UpdateSprite(entity e)
+{
+       entity s1 = ons_ControlPoint_Waypoint(e);
+       WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
+
+       bool sh;
+       sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4));
+
+       if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
+       {
+               if(e.iscaptured) // don't mess up build bars!
+               {
+                       if(sh)
+                       {
+                               WaypointSprite_UpdateMaxHealth(e.sprite, 0);
+                       }
+                       else
+                       {
+                               WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
+                               WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
+                       }
+               }
+               if(e.lastshielded)
+               {
+                       if(e.team)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
+               }
+               else
+               {
+                       if(e.team)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
+               }
+               WaypointSprite_Ping(e.sprite);
+
+               e.lastteam = e.team + 2;
+               e.lastshielded = sh;
+               e.lastcaptured = e.iscaptured;
+       }
+}
+
+void ons_ControlPoint_Touch(entity this, entity toucher)
+{
+       int attackable;
+
+       if(IS_VEHICLE(toucher) && toucher.owner)
+       if(autocvar_g_onslaught_allow_vehicle_touch)
+               toucher = toucher.owner;
+       else
+               return;
+
+       if(!IS_PLAYER(toucher)) { return; }
+       if(STAT(FROZEN, toucher)) { return; }
+       if(IS_DEAD(toucher)) { return; }
+
+       if ( SAME_TEAM(this,toucher) )
+       if ( this.iscaptured )
+       {
+               if(time <= toucher.teleport_antispam)
+                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time));
+               else
+                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
+       }
+
+       attackable = ons_ControlPoint_Attackable(this, toucher.team);
+       if(attackable != 2 && attackable != 4)
+               return;
+       // we've verified that this player has a legitimate claim to this point,
+       // so start building the captured point icon (which only captures this
+       // point if it successfully builds without being destroyed first)
+       ons_ControlPoint_Icon_Spawn(this, toucher);
+
+       this.ons_toucher = toucher;
+
+       onslaught_updatelinks();
+}
+
+void ons_ControlPoint_Think(entity this)
+{
+       this.nextthink = time + ONS_CP_THINKRATE;
+       CSQCMODEL_AUTOUPDATE(this);
+}
+
+void ons_ControlPoint_Reset(entity this)
+{
+       if(this.goalentity)
+               delete(this.goalentity);
+
+       this.goalentity = NULL;
+       this.team = 0;
+       this.colormap = 1024;
+       this.iscaptured = false;
+       this.islinked = false;
+       this.isshielded = true;
+       setthink(this, ons_ControlPoint_Think);
+       this.ons_toucher = NULL;
+       this.nextthink = time + ONS_CP_THINKRATE;
+       setmodel_fixsize(this, MDL_ONS_CP_PAD1);
+
+       WaypointSprite_UpdateMaxHealth(this.sprite, 0);
+       WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY);
+
+       onslaught_updatelinks();
+
+       SUB_UseTargets(this, this, NULL); // to reset the structures, playerspawns etc.
+
+       CSQCMODEL_AUTOUPDATE(this);
+}
+
+void ons_DelayedControlPoint_Setup(entity this)
+{
+       onslaught_updatelinks();
+
+       // captureshield setup
+       ons_CaptureShield_Spawn(this, false);
+
+       CSQCMODEL_AUTOINIT(this);
+}
+
+void ons_ControlPoint_Setup(entity cp)
+{
+       // main setup
+       cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist
+       ons_worldcplist = cp;
+
+       cp.netname = "Control point";
+       cp.team = 0;
+       cp.solid = SOLID_BBOX;
+       set_movetype(cp, MOVETYPE_NONE);
+       settouch(cp, ons_ControlPoint_Touch);
+       setthink(cp, ons_ControlPoint_Think);
+       cp.nextthink = time + ONS_CP_THINKRATE;
+       cp.reset = ons_ControlPoint_Reset;
+       cp.colormap = 1024;
+       cp.iscaptured = false;
+       cp.islinked = false;
+       cp.isshielded = true;
+
+       if(cp.message == "") { cp.message = "a"; }
+
+       // appearence
+       setmodel_fixsize(cp, MDL_ONS_CP_PAD1);
+
+       // control point placement
+       if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location
+       {
+               cp.noalign = true;
+               set_movetype(cp, MOVETYPE_NONE);
+       }
+       else // drop to floor, automatically find a platform and set that as spawn origin
+       {
+               setorigin(cp, cp.origin + '0 0 20');
+               cp.noalign = false;
+               droptofloor(cp);
+               set_movetype(cp, MOVETYPE_TOSS);
+       }
+
+       // waypointsprites
+       WaypointSprite_SpawnFixed(WP_Null, cp.origin + CPGEN_WAYPOINT_OFFSET, cp, sprite, RADARICON_NONE);
+       WaypointSprite_UpdateRule(cp.sprite, cp.team, SPRITERULE_TEAMPLAY);
+
+       InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION);
+}
+
+
+// =========================
+// Main Generator Functions
+// =========================
+
+entity ons_Generator_Waypoint(entity e)
+{
+       if (e.isshielded)
+               return WP_OnsGenShielded;
+       return WP_OnsGen;
+}
+
+void ons_Generator_UpdateSprite(entity e)
+{
+       entity s1 = ons_Generator_Waypoint(e);
+       WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
+
+       if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
+       {
+               e.lastteam = e.team + 2;
+               e.lastshielded = e.isshielded;
+               if(e.lastshielded)
+               {
+                       if(e.team)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
+               }
+               else
+               {
+                       if(e.team)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
+               }
+               WaypointSprite_Ping(e.sprite);
+       }
+}
+
+void ons_camSetup(entity this)
+{
+       vector dir;
+       vector ang = '0 0 0';
+       vector best_ang = '0 0 0';
+       float best_trace_fraction = 0;
+       while(ang.y < 360)
+       {
+               dir = eX * cos(ang.y * DEG2RAD) + eY * sin(ang.y * DEG2RAD);
+               dir *= 500;
+               traceline(this.origin, this.origin - dir, MOVE_WORLDONLY, this);
+               if(trace_fraction > best_trace_fraction)
+               {
+                       best_trace_fraction = trace_fraction;
+                       best_ang = ang;
+                       if(trace_fraction == 1)
+                               break;
+               }
+               ang.y += 90;
+               if(ang.y == 360)
+                       ang.y = 45;
+       }
+       cam.origin = this.origin;
+       setorigin(cam, cam.origin);
+       cam.angles = best_ang;
+       Net_LinkEntity(cam, false, 0, clientcamera_send);
+
+       FOREACH_CLIENT(true, it.clientcamera = cam;);
+
+       WriteByte(MSG_ALL, SVC_SETVIEWANGLES);
+       WriteAngle(MSG_ALL, cam.angles_x);
+       WriteAngle(MSG_ALL, cam.angles_y);
+       WriteAngle(MSG_ALL, cam.angles_z);
+}
+
+void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if(damage <= 0) { return; }
+       if(warmup_stage || gameover) { return; }
+       if(!round_handler_IsRoundStarted()) { return; }
+
+       if (attacker != this)
+       {
+               if (this.isshielded)
+               {
+                       // generator is protected by a shield, so ignore the damage
+                       if (time > this.pain_finished)
+                               if (IS_PLAYER(attacker))
+                               {
+                                       play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
+                                       attacker.typehitsound += 1;
+                                       this.pain_finished = time + 1;
+                               }
+                       return;
+               }
+               if (time > this.pain_finished)
+               {
+                       this.pain_finished = time + 10;
+                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && SAME_TEAM(it, this), Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK));
+                       play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK));
+               }
+       }
+       this.health = this.health - damage;
+       WaypointSprite_UpdateHealth(this.sprite, this.health);
+       // choose an animation frame based on health
+       this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1);
+       // see if the generator is still functional, or dying
+       if (this.health > 0)
+       {
+               this.lasthealth = this.health;
+       }
+       else
+       {
+               if (attacker == this)
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME));
+               else
+               {
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED));
+                       PlayerScore_Add(attacker, SP_SCORE, 100);
+               }
+               this.iscaptured = false;
+               this.islinked = false;
+               this.isshielded = false;
+               this.takedamage = DAMAGE_NO; // can't be hurt anymore
+               this.event_damage = func_null; // won't do anything if hurt
+               this.count = 0; // reset counter
+               setthink(this, func_null);
+               this.nextthink = 0;
+               //this.think(); // do the first explosion now
+
+               WaypointSprite_UpdateMaxHealth(this.sprite, 0);
+               WaypointSprite_Ping(this.sprite);
+               //WaypointSprite_Kill(this.sprite); // can't do this yet, code too poor
+
+               onslaught_updatelinks();
+
+               ons_camSetup(this);
+       }
+
+       // Throw some flaming gibs on damage, more damage = more chance for gib
+       if(random() < damage/220)
+       {
+               sound(this, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+       }
+       else
+       {
+               // particles on every hit
+               Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1);
+
+               //sound on every hit
+               if (random() < 0.5)
+                       sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM);
+               else
+                       sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM);
+       }
+
+       this.SendFlags |= GSF_STATUS;
+}
+
+void ons_GeneratorThink(entity this)
+{
+       this.nextthink = time + GEN_THINKRATE;
+       if (!gameover)
+       {
+               if(!this.isshielded && this.wait < time)
+               {
+                       this.wait = time + 5;
+                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
+                               if(SAME_TEAM(it, this))
+                               {
+                                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM);
+                                       soundto(MSG_ONE, it, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE);    // FIXME: unique sound?
+                               }
+                               else
+                                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED));
+                       });
+               }
+       }
+}
+
+void ons_GeneratorReset(entity this)
+{
+       this.team = this.team_saved;
+       this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health;
+       this.takedamage = DAMAGE_AIM;
+       this.bot_attack = true;
+       if(!IL_CONTAINS(g_bot_targets, this))
+               IL_PUSH(g_bot_targets, this);
+       this.iscaptured = true;
+       this.islinked = true;
+       this.isshielded = true;
+       this.event_damage = ons_GeneratorDamage;
+       setthink(this, ons_GeneratorThink);
+       this.nextthink = time + GEN_THINKRATE;
+
+       Net_LinkEntity(this, false, 0, generator_send);
+
+       this.SendFlags = GSF_SETUP; // just incase
+       this.SendFlags |= GSF_STATUS;
+
+       WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health);
+       WaypointSprite_UpdateHealth(this.sprite, this.health);
+       WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY);
+
+       onslaught_updatelinks();
+}
+
+void ons_DelayedGeneratorSetup(entity this)
+{
+       // bot waypoints
+       waypoint_spawnforitem_force(this, this.origin);
+       this.nearestwaypointtimeout = 0; // activate waypointing again
+       this.bot_basewaypoint = this.nearestwaypoint;
+
+       // captureshield setup
+       ons_CaptureShield_Spawn(this, true);
+
+       onslaught_updatelinks();
+
+       Net_LinkEntity(this, false, 0, generator_send);
+}
+
+
+void onslaught_generator_touch(entity this, entity toucher)
+{
+       if ( IS_PLAYER(toucher) )
+       if ( SAME_TEAM(this,toucher) )
+       if ( this.iscaptured )
+       {
+               Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
+       }
+}
+
+void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc
+{
+       // declarations
+       int teamnumber = gen.team;
+
+       // main setup
+       gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist
+       ons_worldgeneratorlist = gen;
+
+       gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber));
+       gen.classname = "onslaught_generator";
+       gen.solid = SOLID_BBOX;
+       gen.team_saved = teamnumber;
+       set_movetype(gen, MOVETYPE_NONE);
+       gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health;
+       gen.takedamage = DAMAGE_AIM;
+       gen.bot_attack = true;
+       IL_PUSH(g_bot_targets, gen);
+       gen.event_damage = ons_GeneratorDamage;
+       gen.reset = ons_GeneratorReset;
+       setthink(gen, ons_GeneratorThink);
+       gen.nextthink = time + GEN_THINKRATE;
+       gen.iscaptured = true;
+       gen.islinked = true;
+       gen.isshielded = true;
+       settouch(gen, onslaught_generator_touch);
+
+       // appearence
+       // model handled by CSQC
+       setsize(gen, GENERATOR_MIN, GENERATOR_MAX);
+       setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET));
+       gen.colormap = 1024 + (teamnumber - 1) * 17;
+
+       // generator placement
+       droptofloor(gen);
+
+       // waypointsprites
+       WaypointSprite_SpawnFixed(WP_Null, gen.origin + CPGEN_WAYPOINT_OFFSET, gen, sprite, RADARICON_NONE);
+       WaypointSprite_UpdateRule(gen.sprite, gen.team, SPRITERULE_TEAMPLAY);
+       WaypointSprite_UpdateMaxHealth(gen.sprite, gen.max_health);
+       WaypointSprite_UpdateHealth(gen.sprite, gen.health);
+
+       InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION);
+}
+
+
+// ===============
+//  Round Handler
+// ===============
+
+int total_generators;
+void Onslaught_count_generators()
+{
+       entity e;
+       total_generators = redowned = blueowned = yellowowned = pinkowned = 0;
+       for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
+       {
+               ++total_generators;
+               redowned += (e.team == NUM_TEAM_1 && e.health > 0);
+               blueowned += (e.team == NUM_TEAM_2 && e.health > 0);
+               yellowowned += (e.team == NUM_TEAM_3 && e.health > 0);
+               pinkowned += (e.team == NUM_TEAM_4 && e.health > 0);
+       }
+}
+
+int Onslaught_GetWinnerTeam()
+{
+       int winner_team = 0;
+       if(redowned > 0)
+               winner_team = NUM_TEAM_1;
+       if(blueowned > 0)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_2;
+       }
+       if(yellowowned > 0)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_3;
+       }
+       if(pinkowned > 0)
+       {
+               if(winner_team) return 0;
+               winner_team = NUM_TEAM_4;
+       }
+       if(winner_team)
+               return winner_team;
+       return -1; // no generators left?
+}
+
+void nades_Clear(entity e);
+
+#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
+#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1)
+bool Onslaught_CheckWinner()
+{
+       if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0))
+       {
+               ons_stalemate = true;
+
+               if (!wpforenemy_announced)
+               {
+                       Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
+                       sound(NULL, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE);
+
+                       wpforenemy_announced = true;
+               }
+
+               entity tmp_entity; // temporary entity
+               float d;
+               for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay)
+               {
+                       // tmp_entity.max_health / 300 gives 5 minutes of overtime.
+                       // control points reduce the overtime duration.
+                       d = 1;
+                       entity e;
+                       for(e = ons_worldcplist; e; e = e.ons_worldcpnext)
+                       {
+                               if(DIFF_TEAM(e, tmp_entity))
+                               if(e.islinked)
+                                       d = d + 1;
+                       }
+
+                       if(autocvar_g_campaign && autocvar__campaign_testrun)
+                               d = d * tmp_entity.max_health;
+                       else
+                               d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
+
+                       Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0');
+
+                       tmp_entity.sprite.SendFlags |= 16;
+
+                       tmp_entity.ons_overtime_damagedelay = time + 1;
+               }
+       }
+       else { wpforenemy_announced = false; ons_stalemate = false; }
+
+       Onslaught_count_generators();
+
+       if(ONS_OWNED_GENERATORS_OK())
+               return 0;
+
+       int winner_team = Onslaught_GetWinnerTeam();
+
+       if(winner_team > 0)
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
+               TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1);
+       }
+       else if(winner_team == -1)
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
+       }
+
+       ons_stalemate = false;
+
+       play2all(SND(CTF_CAPTURE(winner_team)));
+
+       round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
+
+       FOREACH_CLIENT(IS_PLAYER(it), {
+               STAT(ROUNDLOST, it) = true;
+               it.player_blocked = true;
+
+               nades_Clear(it);
+       });
+
+       return 1;
+}
+
+bool Onslaught_CheckPlayers()
+{
+       return 1;
+}
+
+void Onslaught_RoundStart()
+{
+       entity tmp_entity;
+       FOREACH_CLIENT(IS_PLAYER(it), it.player_blocked = false);
+
+       for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+               tmp_entity.sprite.SendFlags |= 16;
+
+       for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+               tmp_entity.sprite.SendFlags |= 16;
+}
+
+
+// ================
+// Bot player logic
+// ================
+
+// NOTE: LEGACY CODE, needs to be re-written!
+
+void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius)
+{
+       bool needarmor = false, needweapons = false;
+
+       // Needs armor/health?
+       if(this.health<100)
+               needarmor = true;
+
+       // Needs weapons?
+       int c = 0;
+       FOREACH(Weapons, it != WEP_Null, {
+               if(this.weapons & (it.m_wepset))
+               if(++c >= 4)
+                       break;
+       });
+
+       if(c<4)
+               needweapons = true;
+
+       if(!needweapons && !needarmor)
+               return;
+
+       LOG_DEBUG(this.netname, " needs weapons ", ftos(needweapons));
+       LOG_DEBUG(this.netname, " needs armor ", ftos(needarmor));
+
+       // See what is around
+       IL_EACH(g_items, it.bot_pickup,
+       {
+               // gather health and armor only
+               if (it.solid)
+               if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) )
+               if (vdist(it.origin - org, <, sradius))
+               {
+                       int t = it.bot_pickupevalfunc(this, it);
+                       if (t > 0)
+                               navigation_routerating(this, it, t * ratingscale, 500);
+               }
+       });
+}
+
+void havocbot_role_ons_setrole(entity this, int role)
+{
+       LOG_DEBUG(this.netname," switched to ");
+       switch(role)
+       {
+               case HAVOCBOT_ONS_ROLE_DEFENSE:
+                       LOG_DEBUG("defense");
+                       this.havocbot_role = havocbot_role_ons_defense;
+                       this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
+                       this.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_ONS_ROLE_ASSISTANT:
+                       LOG_DEBUG("assistant");
+                       this.havocbot_role = havocbot_role_ons_assistant;
+                       this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
+                       this.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_ONS_ROLE_OFFENSE:
+                       LOG_DEBUG("offense");
+                       this.havocbot_role = havocbot_role_ons_offense;
+                       this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
+                       this.havocbot_role_timeout = 0;
+                       break;
+       }
+       LOG_DEBUG("");
+}
+
+void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale)
+{
+       entity cp, cp1, cp2, best, wp;
+       float radius, bestvalue;
+       int c;
+       bool found;
+
+       // Filter control points
+       for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
+       {
+               cp2.wpcost = c = 0;
+               cp2.wpconsidered = false;
+
+               if(cp2.isshielded)
+                       continue;
+
+               // Ignore owned controlpoints
+               if(!(cp2.isgenneighbor[this.team] || cp2.iscpneighbor[this.team]))
+                       continue;
+
+               // Count team mates interested in this control point
+               // (easier and cleaner than keeping counters per cp and teams)
+               FOREACH_CLIENT(IS_PLAYER(it), {
+                       if(SAME_TEAM(it, this))
+                       if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
+                       if(it.havocbot_ons_target == cp2)
+                               ++c;
+               });
+
+               // NOTE: probably decrease the cost of attackable control points
+               cp2.wpcost = c;
+               cp2.wpconsidered = true;
+       }
+
+       // We'll consider only the best case
+       bestvalue = 99999999999;
+       cp = NULL;
+       for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext)
+       {
+               if (!cp1.wpconsidered)
+                       continue;
+
+               if(cp1.wpcost<bestvalue)
+               {
+                       bestvalue = cp1.wpcost;
+                       cp = cp1;
+                       this.havocbot_ons_target = cp1;
+               }
+       }
+
+       if (!cp)
+               return;
+
+       LOG_DEBUG(this.netname, " chose cp ranked ", ftos(bestvalue));
+
+       if(cp.goalentity)
+       {
+               // Should be attacked
+               // Rate waypoints near it
+               found = false;
+               best = NULL;
+               bestvalue = 99999999999;
+               for(radius=0; radius<1000 && !found; radius+=500)
+               {
+                       for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
+                       {
+                               if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
+                               if(wp.classname=="waypoint")
+                               if(checkpvs(wp.origin,cp))
+                               {
+                                       found = true;
+                                       if(wp.cnt<bestvalue)
+                                       {
+                                               best = wp;
+                                               bestvalue = wp.cnt;
+                                       }
+                               }
+                       }
+               }
+
+               if(best)
+               {
+                       navigation_routerating(this, best, ratingscale, 10000);
+                       best.cnt += 1;
+
+                       this.havocbot_attack_time = 0;
+                       if(checkpvs(this.view_ofs,cp))
+                       if(checkpvs(this.view_ofs,best))
+                               this.havocbot_attack_time = time + 2;
+               }
+               else
+               {
+                       navigation_routerating(this, cp, ratingscale, 10000);
+               }
+               LOG_DEBUG(this.netname, " found an attackable controlpoint at ", vtos(cp.origin));
+       }
+       else
+       {
+               // Should be touched
+               LOG_DEBUG(this.netname, " found a touchable controlpoint at ", vtos(cp.origin));
+               found = false;
+
+               // Look for auto generated waypoint
+               if (!bot_waypoints_for_items)
+               for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
+               {
+                       if(wp.classname=="waypoint")
+                       {
+                               navigation_routerating(this, wp, ratingscale, 10000);
+                               found = true;
+                       }
+               }
+
+               // Nothing found, rate the controlpoint itself
+               if (!found)
+                       navigation_routerating(this, cp, ratingscale, 10000);
+       }
+}
+
+bool havocbot_goalrating_ons_generator_attack(entity this, float ratingscale)
+{
+       entity g, wp, bestwp;
+       bool found;
+       int best;
+
+       for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
+       {
+               if(SAME_TEAM(g, this) || g.isshielded)
+                       continue;
+
+               // Should be attacked
+               // Rate waypoints near it
+               found = false;
+               bestwp = NULL;
+               best = 99999999999;
+
+               for(wp=findradius(g.origin,400); wp; wp=wp.chain)
+               {
+                       if(wp.classname=="waypoint")
+                       if(checkpvs(wp.origin,g))
+                       {
+                               found = true;
+                               if(wp.cnt<best)
+                               {
+                                       bestwp = wp;
+                                       best = wp.cnt;
+                               }
+                       }
+               }
+
+               if(bestwp)
+               {
+                       LOG_DEBUG("waypoints found around generator");
+                       navigation_routerating(this, bestwp, ratingscale, 10000);
+                       bestwp.cnt += 1;
+
+                       this.havocbot_attack_time = 0;
+                       if(checkpvs(this.view_ofs,g))
+                       if(checkpvs(this.view_ofs,bestwp))
+                               this.havocbot_attack_time = time + 5;
+
+                       return true;
+               }
+               else
+               {
+                       LOG_DEBUG("generator found without waypoints around");
+                       // if there aren't waypoints near the generator go straight to it
+                       navigation_routerating(this, g, ratingscale, 10000);
+                       this.havocbot_attack_time = 0;
+                       return true;
+               }
+       }
+       return false;
+}
+
+void havocbot_role_ons_offense(entity this)
+{
+       if(IS_DEAD(this))
+       {
+               this.havocbot_attack_time = 0;
+               havocbot_ons_reset_role(this);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!this.havocbot_role_timeout)
+               this.havocbot_role_timeout = time + 120;
+
+       if (time > this.havocbot_role_timeout)
+       {
+               havocbot_ons_reset_role(this);
+               return;
+       }
+
+       if(this.havocbot_attack_time>time)
+               return;
+
+       if (this.bot_strategytime < time)
+       {
+               navigation_goalrating_start(this);
+               havocbot_goalrating_enemyplayers(this, 20000, this.origin, 650);
+               if(!havocbot_goalrating_ons_generator_attack(this, 20000))
+                       havocbot_goalrating_ons_controlpoints_attack(this, 20000);
+               havocbot_goalrating_ons_offenseitems(this, 10000, this.origin, 10000);
+               navigation_goalrating_end(this);
+
+               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+       }
+}
+
+void havocbot_role_ons_assistant(entity this)
+{
+       havocbot_ons_reset_role(this);
+}
+
+void havocbot_role_ons_defense(entity this)
+{
+       havocbot_ons_reset_role(this);
+}
+
+void havocbot_ons_reset_role(entity this)
+{
+       if(IS_DEAD(this))
+               return;
+
+       this.havocbot_ons_target = NULL;
+
+       // TODO: Defend control points or generator if necessary
+
+       havocbot_role_ons_setrole(this, HAVOCBOT_ONS_ROLE_OFFENSE);
+}
+
+
+/*
+ * Find control point or generator owned by the same team self which is nearest to pos
+ * if max_dist is positive, only control points within this range will be considered
+ */
+entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist)
+{
+       entity closest_target = NULL;
+       for(entity cp = ons_worldcplist; cp; cp = cp.ons_worldcpnext)
+       {
+               if(SAME_TEAM(cp, this))
+               if(cp.iscaptured)
+               if(max_dist <= 0 || vdist(cp.origin - pos, <=, max_dist))
+               if(vlen2(cp.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
+                       closest_target = cp;
+       }
+       for(entity gen = ons_worldgeneratorlist; gen; gen = gen.ons_worldgeneratornext)
+       {
+               if(SAME_TEAM(gen, this))
+               if(max_dist <= 0 || vdist(gen.origin - pos, <, max_dist))
+               if(vlen2(gen.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
+                       closest_target = gen;
+       }
+
+       return closest_target;
+}
+
+/*
+ * Find control point or generator owned by the same team self which is nearest to pos
+ * if max_dist is positive, only control points within this range will be considered
+ * This function only check distances on the XY plane, disregarding Z
+ */
+entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist)
+{
+       entity closest_target = NULL;
+       vector delta;
+       float smallest_distance = 0, distance;
+
+       for(entity cp = ons_worldcplist; cp; cp = cp.ons_worldcpnext)
+       {
+               delta = cp.origin - pos;
+               delta_z = 0;
+               distance = vlen(delta);
+
+               if(SAME_TEAM(cp, this))
+               if(cp.iscaptured)
+               if(max_dist <= 0 || distance <= max_dist)
+               if(closest_target == NULL || distance <= smallest_distance )
+               {
+                       closest_target = cp;
+                       smallest_distance = distance;
+               }
+       }
+       for(entity gen = ons_worldgeneratorlist; gen; gen = gen.ons_worldgeneratornext)
+       {
+               delta = gen.origin - pos;
+               delta_z = 0;
+               distance = vlen(delta);
+
+               if(SAME_TEAM(gen, this))
+               if(max_dist <= 0 || distance <= max_dist)
+               if(closest_target == NULL || distance <= smallest_distance )
+               {
+                       closest_target = gen;
+                       smallest_distance = distance;
+               }
+       }
+
+       return closest_target;
+}
+/**
+ * find the number of control points and generators in the same team as this
+ */
+int ons_Count_SelfControlPoints(entity this)
+{
+       int n = 0;
+       for(entity cp = ons_worldcplist; cp; cp = cp.ons_worldcpnext)
+       {
+               if(SAME_TEAM(cp, this))
+               if(cp.iscaptured)
+                       n++;
+       }
+       for(entity gen = ons_worldgeneratorlist; gen; gen = gen.ons_worldgeneratornext)
+       {
+               if(SAME_TEAM(gen, this))
+                       n++;
+       }
+       return n;
+}
+
+/**
+ * Teleport player to a random position near tele_target
+ * if tele_effects is true, teleport sound+particles are created
+ * return false on failure
+ */
+bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects)
+{
+       if ( !tele_target )
+               return false;
+
+       int i;
+       vector loc;
+       float theta;
+       // narrow the range for each iteration to increase chances that a spawnpoint
+       // can be found even if there's little room around the control point
+       float iteration_scale = 1;
+       for(i = 0; i < 16; ++i)
+       {
+               iteration_scale -= i / 16;
+               theta = random() * 2 * M_PI;
+               loc_y = sin(theta);
+               loc_x = cos(theta);
+               loc_z = 0;
+               loc *= random() * range * iteration_scale;
+
+               loc += tele_target.origin + '0 0 128' * iteration_scale;
+
+               tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player);
+               if(trace_fraction == 1.0 && !trace_startsolid)
+               {
+                       traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL
+                       if(trace_fraction == 1.0 && !trace_startsolid)
+                       {
+                               if ( tele_effects )
+                               {
+                                       Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1);
+                                       sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM);
+                               }
+                               setorigin(player, loc);
+                               player.angles = '0 1 0' * ( theta * RAD2DEG + 180 );
+                               makevectors(player.angles);
+                               player.fixangle = true;
+                               player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait;
+
+                               if ( tele_effects )
+                                       Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1);
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(ons, reset_map_global)
+{
+       FOREACH_CLIENT(IS_PLAYER(it), {
+               STAT(ROUNDLOST, it) = false;
+               it.ons_deathloc = '0 0 0';
+               PutClientInServer(it);
+               it.clientcamera = it;
+       });
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, ClientDisconnect)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.ons_deathloc = '0 0 0';
+}
+
+MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.ons_deathloc = '0 0 0';
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(!round_handler_IsRoundStarted())
+       {
+               player.player_blocked = true;
+               return false;
+       }
+
+       entity l;
+       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+       {
+               l.sprite.SendFlags |= 16;
+       }
+       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+       {
+               l.sprite.SendFlags |= 16;
+       }
+
+       if(ons_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
+
+       if ( autocvar_g_onslaught_spawn_choose )
+       if ( player.ons_spawn_by )
+       if ( ons_Teleport(player,player.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) )
+       {
+               player.ons_spawn_by = NULL;
+               return false;
+       }
+
+       if(autocvar_g_onslaught_spawn_at_controlpoints)
+       if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
+       {
+               float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random;
+               entity tmp_entity, closest_target = NULL;
+               vector spawn_loc = player.ons_deathloc;
+
+               // new joining player or round reset, don't bother checking
+               if(spawn_loc == '0 0 0') { return false; }
+
+               if(random_target) { RandomSelection_Init(); }
+
+               for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+               {
+                       if(SAME_TEAM(tmp_entity, player))
+                       if(random_target)
+                               RandomSelection_AddEnt(tmp_entity, 1, 1);
+                       else if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL)
+                               closest_target = tmp_entity;
+               }
+
+               if(random_target) { closest_target = RandomSelection_chosen_ent; }
+
+               if(closest_target)
+               {
+                       float i;
+                       vector loc;
+                       float iteration_scale = 1;
+                       for(i = 0; i < 10; ++i)
+                       {
+                               iteration_scale -= i / 10;
+                               loc = closest_target.origin + '0 0 96' * iteration_scale;
+                               loc += ('0 1 0' * random()) * 128 * iteration_scale;
+                               tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player);
+                               if(trace_fraction == 1.0 && !trace_startsolid)
+                               {
+                                       traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL
+                                       if(trace_fraction == 1.0 && !trace_startsolid)
+                                       {
+                                               setorigin(player, loc);
+                                               player.angles = normalize(loc - closest_target.origin) * RAD2DEG;
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if(autocvar_g_onslaught_spawn_at_generator)
+       if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
+       {
+               float random_target = autocvar_g_onslaught_spawn_at_generator_random;
+               entity tmp_entity, closest_target = NULL;
+               vector spawn_loc = player.ons_deathloc;
+
+               // new joining player or round reset, don't bother checking
+               if(spawn_loc == '0 0 0') { return false; }
+
+               if(random_target) { RandomSelection_Init(); }
+
+               for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+               {
+                       if(random_target)
+                               RandomSelection_AddEnt(tmp_entity, 1, 1);
+                       else
+                       {
+                               if(SAME_TEAM(tmp_entity, player))
+                               if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL)
+                                       closest_target = tmp_entity;
+                       }
+               }
+
+               if(random_target) { closest_target = RandomSelection_chosen_ent; }
+
+               if(closest_target)
+               {
+                       float i;
+                       vector loc;
+                       float iteration_scale = 1;
+                       for(i = 0; i < 10; ++i)
+                       {
+                               iteration_scale -= i / 10;
+                               loc = closest_target.origin + '0 0 128' * iteration_scale;
+                               loc += ('0 1 0' * random()) * 256 * iteration_scale;
+                               tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player);
+                               if(trace_fraction == 1.0 && !trace_startsolid)
+                               {
+                                       traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL
+                                       if(trace_fraction == 1.0 && !trace_startsolid)
+                                       {
+                                               setorigin(player, loc);
+                                               player.angles = normalize(loc - closest_target.origin) * RAD2DEG;
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayerDies)
+{
+       entity frag_target = M_ARGV(2, entity);
+
+       frag_target.ons_deathloc = frag_target.origin;
+       entity l;
+       for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+       {
+               l.sprite.SendFlags |= 16;
+       }
+       for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+       {
+               l.sprite.SendFlags |= 16;
+       }
+
+       if ( autocvar_g_onslaught_spawn_choose )
+       if ( ons_Count_SelfControlPoints(frag_target) > 1 )
+               stuffcmd(frag_target, "qc_cmd_cl hud clickradar\n");
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, MonsterMove)
+{
+       entity mon = M_ARGV(0, entity);
+
+       entity e = find(NULL, targetname, mon.target);
+       if (e != NULL)
+               mon.team = e.team;
+}
+
+void ons_MonsterSpawn_Delayed(entity this)
+{
+       entity own = this.owner;
+
+       if(!own) { delete(this); return; }
+
+       if(own.targetname)
+       {
+               entity e = find(NULL, target, own.targetname);
+               if(e != NULL)
+               {
+                       own.team = e.team;
+
+                       own.use(own, e, NULL);
+               }
+       }
+
+       delete(this);
+}
+
+MUTATOR_HOOKFUNCTION(ons, MonsterSpawn)
+{
+       entity mon = M_ARGV(0, entity);
+
+       entity e = spawn();
+       e.owner = mon;
+       InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET);
+}
+
+void ons_TurretSpawn_Delayed(entity this)
+{
+       entity own = this.owner;
+
+       if(!own) { delete(this); return; }
+
+       if(own.targetname)
+       {
+               entity e = find(NULL, target, own.targetname);
+               if(e != NULL)
+               {
+                       own.team = e.team;
+                       own.active = ACTIVE_NOT;
+
+                       own.use(own, e, NULL);
+               }
+       }
+
+       delete(this);
+}
+
+MUTATOR_HOOKFUNCTION(ons, TurretSpawn)
+{
+       entity turret = M_ARGV(0, entity);
+
+       entity e = spawn();
+       e.owner = turret;
+       InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole)
+{
+       entity bot = M_ARGV(0, entity);
+
+       havocbot_ons_reset_role(bot);
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(ons, CheckAllowedTeams)
+{
+       // onslaught is special
+       for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+       {
+               switch(tmp_entity.team)
+               {
+                       case NUM_TEAM_1: c1 = 0; break;
+                       case NUM_TEAM_2: c2 = 0; break;
+                       case NUM_TEAM_3: c3 = 0; break;
+                       case NUM_TEAM_4: c4 = 0; break;
+               }
+       }
+
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(ons, SpectateCopy)
+{
+       entity spectatee = M_ARGV(0, entity);
+       entity client = M_ARGV(1, entity);
+
+       STAT(ROUNDLOST, client) = STAT(ROUNDLOST, spectatee); // make spectators see it too
+}
+
+MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand)
+{
+       if(MUTATOR_RETURNVALUE) // command was already handled?
+               return false;
+
+       entity player = M_ARGV(0, entity);
+       string cmd_name = M_ARGV(1, string);
+       int cmd_argc = M_ARGV(2, int);
+
+       if ( cmd_name == "ons_spawn" )
+       {
+               vector pos = player.origin;
+               if(cmd_argc > 1)
+                       pos_x = stof(argv(1));
+               if(cmd_argc > 2)
+                       pos_y = stof(argv(2));
+               if(cmd_argc > 3)
+                       pos_z = stof(argv(3));
+
+               if ( IS_PLAYER(player) )
+               {
+                       if ( !STAT(FROZEN, player) )
+                       {
+                               entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius);
+
+                               if ( !source_point && player.health > 0 )
+                               {
+                                       sprint(player, "\nYou need to be next to a control point\n");
+                                       return true;
+                               }
+
+
+                               entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius);
+
+                               if ( closest_target == NULL )
+                               {
+                                       sprint(player, "\nNo control point found\n");
+                                       return true;
+                               }
+
+                               if ( player.health <= 0 )
+                               {
+                                       player.ons_spawn_by = closest_target;
+                                       player.respawn_flags = player.respawn_flags | RESPAWN_FORCE;
+                               }
+                               else
+                               {
+                                       if ( source_point == closest_target )
+                                       {
+                                               sprint(player, "\nTeleporting to the same point\n");
+                                               return true;
+                                       }
+
+                                       if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) )
+                                               sprint(player, "\nUnable to teleport there\n");
+                               }
+
+                               return true;
+                       }
+
+                       sprint(player, "\nNo teleportation for you\n");
+               }
+
+               return true;
+       }
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayerUseKey)
+{
+       if(MUTATOR_RETURNVALUE || gameover) { return false; }
+
+       entity player = M_ARGV(0, entity);
+
+       if((time > player.teleport_antispam) && (!IS_DEAD(player)) && !player.vehicle)
+       {
+               entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius);
+               if ( source_point )
+               {
+                       stuffcmd(player, "qc_cmd_cl hud clickradar\n");
+                       return true;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayHitsound)
+{
+       entity frag_victim = M_ARGV(0, entity);
+
+       return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded)
+               || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded);
+}
+
+MUTATOR_HOOKFUNCTION(ons, SendWaypoint)
+{
+    entity wp = M_ARGV(0, entity);
+    entity to = M_ARGV(1, entity);
+    int sf = M_ARGV(2, int);
+    int wp_flag = M_ARGV(3, int);
+
+       if(sf & 16)
+       {
+               if(wp.owner.classname == "onslaught_controlpoint")
+               {
+                       entity wp_owner = wp.owner;
+                       entity e = WaypointSprite_getviewentity(to);
+                       if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; }
+                       if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; }
+               }
+               if(wp.owner.classname == "onslaught_generator")
+               {
+                       entity wp_owner = wp.owner;
+                       if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; }
+                       if(wp_owner.health <= 0) { wp_flag |= 2; }
+               }
+       }
+
+       M_ARGV(3, int) = wp_flag;
+}
+
+MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget)
+{
+       entity turret_target = M_ARGV(1, entity);
+
+       if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!
+       {
+               M_ARGV(3, float) = -3;
+               return true;
+       }
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, TurretThink)
+{
+    entity turret = M_ARGV(0, entity);
+
+       // ONS uses somewhat backwards linking.
+       if(turret.target)
+       {
+               entity e = find(NULL, targetname, turret.target);
+               if (e != NULL)
+                       turret.team = e.team;
+       }
+
+       if(turret.team != turret.tur_head.team)
+               turret_respawn(turret);
+}
+
+
+// ==========
+// Spawnfuncs
+// ==========
+
+/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
+  Link between control points.
+
+  This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
+
+keys:
+"target" - first control point.
+"target2" - second control point.
+ */
+spawnfunc(onslaught_link)
+{
+       if(!g_onslaught) { delete(this); return; }
+
+       if (this.target == "" || this.target2 == "")
+               objerror(this, "target and target2 must be set\n");
+
+       this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
+       ons_worldlinklist = this;
+
+       InitializeEntity(this, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
+       Net_LinkEntity(this, false, 0, ons_Link_Send);
+}
+
+/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
+  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
+
+  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
+
+keys:
+"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
+"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
+"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
+ */
+
+spawnfunc(onslaught_controlpoint)
+{
+       if(!g_onslaught) { delete(this); return; }
+
+       ons_ControlPoint_Setup(this);
+}
+
+/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
+  Base generator.
+
+  spawnfunc_onslaught_link entities can target this.
+
+keys:
+"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
+"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
+ */
+spawnfunc(onslaught_generator)
+{
+       if(!g_onslaught) { delete(this); return; }
+       if(!this.team) { objerror(this, "team must be set"); }
+
+       ons_GeneratorSetup(this);
+}
+
+// scoreboard setup
+void ons_ScoreRules()
+{
+       CheckAllowedTeams(NULL);
+       int teams = 0;
+       if(c1 >= 0) teams |= BIT(0);
+       if(c2 >= 0) teams |= BIT(1);
+       if(c3 >= 0) teams |= BIT(2);
+       if(c4 >= 0) teams |= BIT(3);
+       ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
+       ScoreInfo_SetLabel_TeamScore  (ST_ONS_CAPS,     "destroyed", SFL_SORT_PRIO_PRIMARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS,    "caps",      SFL_SORT_PRIO_SECONDARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES,    "takes",     0);
+       ScoreRules_basics_end();
+}
+
+void ons_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up
+{
+       ons_ScoreRules();
+
+       round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart);
+       round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
+}
+
+void ons_Initialize()
+{
+       g_onslaught = true;
+       ons_captureshield_force = autocvar_g_onslaught_shield_force;
+
+       cam = new(objective_camera);
+
+       InitializeEntity(NULL, ons_DelayedInit, INITPRIO_GAMETYPE);
+}
diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh
new file mode 100644 (file)
index 0000000..d8b7fd0
--- /dev/null
@@ -0,0 +1,114 @@
+#pragma once
+
+float autocvar_g_onslaught_point_limit;
+void ons_Initialize();
+
+REGISTER_MUTATOR(ons, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               ons_Initialize();
+
+               ActivateTeamplay();
+               SetLimits(autocvar_g_onslaught_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1);
+               have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back ons_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return false;
+}
+
+.entity ons_toucher; // player who touched the control point
+
+// control point / generator constants
+const float ONS_CP_THINKRATE = 0.2;
+const float GEN_THINKRATE = 1;
+#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13))
+const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128');
+const vector CPICON_OFFSET = ('0 0 96');
+
+// list of generators on the map
+entity ons_worldgeneratorlist;
+.entity ons_worldgeneratornext;
+
+// list of control points on the map
+entity ons_worldcplist;
+.entity ons_worldcpnext;
+
+// list of links on the map
+entity ons_worldlinklist;
+.entity ons_worldlinknext;
+
+// definitions
+.entity sprite;
+.string target2;
+.int iscaptured;
+.int islinked;
+.int isshielded;
+.float lasthealth;
+.int lastteam;
+.int lastshielded;
+.int lastcaptured;
+
+.bool waslinked;
+
+bool ons_stalemate;
+
+.float teleport_antispam;
+
+// waypoint sprites
+.entity bot_basewaypoint; // generator waypointsprite
+
+.bool isgenneighbor[17];
+.bool iscpneighbor[17];
+float ons_notification_time[17];
+
+.float ons_overtime_damagedelay;
+
+.vector ons_deathloc;
+
+.entity ons_spawn_by;
+
+// declarations for functions used outside gamemode_onslaught.qc
+void ons_Generator_UpdateSprite(entity e);
+void ons_ControlPoint_UpdateSprite(entity e);
+bool ons_ControlPoint_Attackable(entity cp, int teamnumber);
+
+// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet
+float ons_captureshield_force; // push force of the shield
+
+// bot player logic
+const int HAVOCBOT_ONS_ROLE_NONE               = 0;
+const int HAVOCBOT_ONS_ROLE_DEFENSE    = 2;
+const int HAVOCBOT_ONS_ROLE_ASSISTANT  = 4;
+const int HAVOCBOT_ONS_ROLE_OFFENSE    = 8;
+
+.entity havocbot_ons_target;
+
+.int havocbot_role_flags;
+.float havocbot_attack_time;
+
+void havocbot_role_ons_defense(entity this);
+void havocbot_role_ons_offense(entity this);
+void havocbot_role_ons_assistant(entity this);
+
+void havocbot_ons_reset_role(entity this);
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius);
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius);
+
+// score rule declarations
+const int ST_ONS_CAPS = 1;
index 6780ac16d0fa1ddd83c766319d05f2d9289b2c01..45a8f1323b9f142bd02b5eae8ad61202a273ae63 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef IMPULSES_ALL_H
-#define IMPULSES_ALL_H
+#pragma once
 
 REGISTRY(IMPULSES, 255)
 REGISTER_REGISTRY(IMPULSES)
@@ -216,5 +215,3 @@ CHIMPULSE(SPEEDRUN, 141)
 CHIMPULSE(CLONE_STANDING, 142)
 CHIMPULSE(TELEPORT, 143)
 CHIMPULSE(R00T, 148)
-
-#endif
index 3b5dd9550f26286b0ad64b35916fce48dbb1fdc4..6214fac042b7783f96bae24935208873061a9d3f 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
 #include <common/items/all.qc>
+
+#include <common/items/item/_mod.inc>
index 158814c5570bbf929537dbe6bf473e864fc50b50..a04e90b1dce9ef7801f36cccd55094c4efc11ecc 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
 #include <common/items/all.qh>
+
+#include <common/items/item/_mod.qh>
index 7a3e5004696ee1c655a8f5eca7e3cb5cb3506b54..0a26a75b5a7fd33aeee0f0b35a3dfd4b8ec92b8b 100644 (file)
@@ -1,9 +1,5 @@
-#ifndef ITEMS_ALL_C
-#define ITEMS_ALL_C
 #include "all.qh"
 
-#include "item/_mod.inc"
-
 void Dump_Items()
 {
     FOREACH(Items, true, ITEM_HANDLE(Show, it));
@@ -19,4 +15,13 @@ string Item_Model(string item_mdl)
     return output;
 }
 
+string Item_Sound(string it_snd)
+{
+       string output = strcat("misc/", it_snd);
+#ifdef SVQC
+               MUTATOR_CALLHOOK(ItemSound, it_snd, output);
+               return M_ARGV(1, string);
+#else
+               return output;
 #endif
+}
index 18cc5ae4f3b707dfd11caea3774cc2473d2587f2..dc8cf21c020a6d06993f87281cc1dad98f29cbac 100644 (file)
@@ -1,11 +1,10 @@
-#ifndef ITEMS_ALL_H
-#define ITEMS_ALL_H
+#pragma once
 
-#include <common/command/all.qh>
+#include <common/command/_mod.qh>
 
 #include "item.qh"
 
-REGISTRY(Items, BITS(5))
+REGISTRY(Items, BITS(7))
 #define Items_from(i) _Items_from(i, NULL)
 REGISTER_REGISTRY(Items)
 #define REGISTER_ITEM(id, class) REGISTER(Items, ITEM, id, m_id, NEW(class))
@@ -30,8 +29,6 @@ GENERIC_COMMAND(dumpitems, "Dump all items to the console") {
     }
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 string Item_Model(string item_mdl);
 #endif
-
-#endif
index 934271059ba55ed6cc464e9c7122cdd970bc65eb..a022979a7065f7ee07c8cdfb0fc94c3ab9a0038a 100644 (file)
@@ -1,14 +1,13 @@
-#ifndef INVENTORY_H
-#define INVENTORY_H
+#pragma once
 
 #include "all.qh"
 #include "item/pickup.qh"
 
 CLASS(Inventory, Object)
     /** Stores counts of items, the id being the index */
-    ATTRIBARRAY(Inventory, inv_items, int, Items_MAX)
+    ATTRIBARRAY(Inventory, inv_items, int, Items_MAX);
     /** Previous state */
-    ATTRIB(Inventory, inventory, Inventory, NULL)
+    ATTRIB(Inventory, inventory, Inventory);
 ENDCLASS(Inventory)
 
 /** Player inventory */
@@ -16,17 +15,33 @@ ENDCLASS(Inventory)
 
 REGISTER_NET_LINKED(ENT_CLIENT_INVENTORY)
 
+const int Inventory_groups_major = 16;
+const int Inventory_groups_minor = 8; // ceil(Items_MAX / Inventory_groups_major)
+
+#define G_MAJOR(id) (floor((id) / Inventory_groups_minor))
+#define G_MINOR(id) ((id) % Inventory_groups_minor)
+
 #ifdef CSQC
 NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew)
 {
     make_pure(this);
-    const int bits = ReadInt24_t();
-    FOREACH(Items, bits & BIT(it.m_id), {
-        .int fld = inv_items[it.m_id];
-        int prev = this.(fld);
-        int next = this.(fld) = ReadByte();
-        LOG_TRACEF("%s: %.0f -> %.0f\n", it.m_name, prev, next);
-    });
+    const int majorBits = ReadShort();
+    for (int i = 0; i < Inventory_groups_major; ++i) {
+        if (!(majorBits & BIT(i))) {
+            continue;
+        }
+        const int minorBits = ReadByte();
+        for (int j = 0; j < Inventory_groups_minor; ++j) {
+            if (!(minorBits & BIT(j))) {
+                continue;
+            }
+            const GameItem it = Items_from(Inventory_groups_minor * i + j);
+            .int fld = inv_items[it.m_id];
+            int prev = this.(fld);
+            int next = this.(fld) = ReadByte();
+            LOG_TRACEF("%s: %.0f -> %.0f", it.m_name, prev, next);
+        }
+    }
     return true;
 }
 #endif
@@ -35,22 +50,56 @@ NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew)
 void Inventory_Write(Inventory data)
 {
     if (!data) {
-        WriteInt24_t(MSG_ENTITY, 0);
+        WriteShort(MSG_ENTITY, 0);
         return;
     }
     TC(Inventory, data);
-    int bits = 0;
+
+    int majorBits = 0;
     FOREACH(Items, true, {
         .int fld = inv_items[it.m_id];
-        bits = BITSET(bits, BIT(it.m_id), data.inventory.(fld) != (data.inventory.(fld) = data.(fld)));
+        const bool changed = data.inventory.(fld) != data.(fld);
+        if (changed) {
+            majorBits = BITSET(majorBits, BIT(G_MAJOR(it.m_id)), true);
+        }
     });
-    WriteInt24_t(MSG_ENTITY, bits);
-    FOREACH(Items, bits & BIT(it.m_id), {
-        WriteByte(MSG_ENTITY, data.inv_items[it.m_id]);
+    WriteShort(MSG_ENTITY, majorBits);
+
+    int minorBits = 0;
+    int lastMaj = 0;
+    int maj = 0;
+    FOREACH(Items, majorBits & BIT(maj = G_MAJOR(it.m_id)), {
+        .int fld = inv_items[it.m_id];
+        const bool changed = data.inventory.(fld) != (data.inventory.(fld) = data.(fld));
+        if (changed) {
+            if (maj != lastMaj) {
+                lastMaj = maj;
+#define X() MACRO_BEGIN \
+    if (minorBits) { \
+        WriteByte(MSG_ENTITY, minorBits); \
+        for (int j = 0; j < Inventory_groups_minor; ++j) { \
+            if (!(minorBits & BIT(j))) { \
+                continue; \
+            } \
+            const GameItem it = Items_from(Inventory_groups_minor * maj + j); \
+            WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); \
+        } \
+    } \
+MACRO_END
+                X();
+                minorBits = 0;
+            }
+            minorBits = BITSET(minorBits, BIT(G_MINOR(it.m_id)), true);
+        }
     });
+    X();
+#undef X
 }
 #endif
 
+#undef G_MAJOR
+#undef G_MINOR
+
 #ifdef SVQC
 bool Inventory_Send(Inventory this, Client to, int sf)
 {
@@ -71,8 +120,6 @@ void Inventory_new(entity e)
     inv.drawonlytoclient = e;
     Net_LinkEntity((inv.owner = e).inventory = inv, false, 0, Inventory_Send);
 }
-void Inventory_delete(entity e) { remove(e.inventory.inventory); remove(e.inventory); }
+void Inventory_delete(entity e) { delete(e.inventory.inventory); delete(e.inventory); }
 void Inventory_update(entity e) { e.inventory.SendFlags = 0xFFFFFF; }
 #endif
-
-#endif
index 4ab7af4f2317341ebeb4d1f625dfb8659cecb3d8..f3aa0cec26b067f47244b96c4af521028b677991 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef GAMEITEM_H
-#define GAMEITEM_H
+#pragma once
+#include <common/t_items.qh>
 
 const int IT_UNLIMITED_WEAPON_AMMO             =  BIT(0); // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
 const int IT_UNLIMITED_SUPERWEAPONS            =  BIT(1); // when this bit is set, superweapons don't expire. Checkpoints can give this powerup.
@@ -34,18 +34,21 @@ const int IT_SUPERWEAPON                            = BIT(21); // suit
 const int IT_STRENGTH                                  = BIT(22);
 
 // item masks
-const int IT_AMMO                              = IT_FUEL | IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_PLASMA;
 const int IT_UNLIMITED_AMMO                    = IT_UNLIMITED_WEAPON_AMMO | IT_UNLIMITED_SUPERWEAPONS;
 const int IT_PICKUPMASK                        = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FUEL_REGEN; // strength and invincible are handled separately
 
 #define ITEM_HANDLE(signal, ...) __Item_Send_##signal(__VA_ARGS__)
 CLASS(GameItem, Object)
-    ATTRIB(GameItem, m_id, int, 0)
-    ATTRIB(GameItem, m_name, string, string_null)
-    ATTRIB(GameItem, m_icon, string, string_null)
-    ATTRIB(GameItem, m_color, vector, '1 1 1')
-    ATTRIB(GameItem, m_waypoint, string, string_null)
-    ATTRIB(GameItem, m_waypointblink, int, 1)
+    ATTRIB(GameItem, m_id, int, 0);
+    ATTRIB(GameItem, m_name, string);
+    ATTRIB(GameItem, m_icon, string);
+    ATTRIB(GameItem, m_color, vector, '1 1 1');
+    ATTRIB(GameItem, m_waypoint, string);
+    ATTRIB(GameItem, m_waypointblink, int, 1);
+#ifdef GAMEQC
+    ATTRIB(GameItem, m_glow, bool, false);
+    ATTRIB(GameItem, m_respawnsound, Sound, SND_ITEMRESPAWN);
+#endif
     METHOD(GameItem, display, void(GameItem this, void(string name, string icon) returns))
     {
         TC(GameItem, this);
@@ -58,5 +61,3 @@ CLASS(GameItem, Object)
     }
     void ITEM_HANDLE(Show, GameItem this) { this.show(this); }
 ENDCLASS(GameItem)
-
-#endif
index 525a5b3d930a14d07fb47ca546a1dd41b94ef7d0..d7e0dcc6872d0d55b9d7b00aad234920acf5cb45 100644 (file)
@@ -1,84 +1 @@
 #include "ammo.qh"
-#ifdef SVQC
-    #include <common/t_items.qh>
-#endif
-
-#ifndef MENUQC
-MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl"));
-#endif
-
-REGISTER_ITEM(Bullets, Ammo) {
-#ifndef MENUQC
-    this.m_model    =   MDL_Bullets_ITEM;
-#endif
-    this.m_name     =   "bullets";
-    this.m_icon     =   "ammo_bullets";
-#ifdef SVQC
-    this.m_botvalue =   2000;
-    this.m_itemid   =   IT_NAILS;
-#endif
-}
-
-#ifndef MENUQC
-MODEL(Cells_ITEM, Item_Model("a_cells.md3"));
-#endif
-
-REGISTER_ITEM(Cells, Ammo) {
-#ifndef MENUQC
-    this.m_model    =   MDL_Cells_ITEM;
-#endif
-    this.m_name     =   "cells";
-    this.m_icon     =   "ammo_cells";
-#ifdef SVQC
-    this.m_botvalue =   2000;
-    this.m_itemid   =   IT_CELLS;
-#endif
-}
-
-#ifndef MENUQC
-MODEL(Plasma_ITEM, Item_Model("a_cells.md3"));
-#endif
-
-REGISTER_ITEM(Plasma, Ammo) {
-#ifndef MENUQC
-    this.m_model    =   MDL_Plasma_ITEM;
-#endif
-    this.m_name     =   "plasma";
-    this.m_icon     =   "ammo_plasma";
-#ifdef SVQC
-    this.m_botvalue =   2000;
-    this.m_itemid   =   IT_PLASMA;
-#endif
-}
-
-#ifndef MENUQC
-MODEL(Rockets_ITEM, Item_Model("a_rockets.md3"));
-#endif
-
-REGISTER_ITEM(Rockets, Ammo) {
-#ifndef MENUQC
-    this.m_model    =   MDL_Rockets_ITEM;
-#endif
-    this.m_name     =   "rockets";
-    this.m_icon     =   "ammo_rockets";
-#ifdef SVQC
-    this.m_botvalue =   3000;
-    this.m_itemid   =   IT_ROCKETS;
-#endif
-}
-
-#ifndef MENUQC
-MODEL(Shells_ITEM, Item_Model("a_shells.md3"));
-#endif
-
-REGISTER_ITEM(Shells, Ammo) {
-#ifndef MENUQC
-    this.m_model    =   MDL_Shells_ITEM;
-#endif
-    this.m_name     =   "shells";
-    this.m_icon     =   "ammo_shells";
-#ifdef SVQC
-    this.m_botvalue =   500;
-    this.m_itemid   =   IT_SHELLS;
-#endif
-}
index 84f20483e4f2902d65cc325ee8d0f0ca31d0509b..e1d493fe975a7c50dfcd11af3d9fba717a67c1da 100644 (file)
@@ -1,11 +1,94 @@
-#ifndef AMMO_H
-#define AMMO_H
+#pragma once
+
 #include "pickup.qh"
 CLASS(Ammo, Pickup)
 #ifdef SVQC
-    ATTRIB(Ammo, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
-    ATTRIB(Ammo, m_respawntime, float(), GET(g_pickup_respawntime_ammo))
-    ATTRIB(Ammo, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_ammo))
+    ATTRIB(Ammo, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc);
+    ATTRIB(Ammo, m_respawntime, float(), GET(g_pickup_respawntime_ammo));
+    ATTRIB(Ammo, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_ammo));
 #endif
 ENDCLASS(Ammo)
+
+#ifdef SVQC
+    #include <common/t_items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl"));
+#endif
+
+REGISTER_ITEM(Bullets, Ammo) {
+#ifdef GAMEQC
+    this.m_model    =   MDL_Bullets_ITEM;
+#endif
+    this.m_name     =   "bullets";
+    this.m_icon     =   "ammo_bullets";
+#ifdef SVQC
+    this.m_botvalue =   2000;
+    this.m_itemid   =   IT_NAILS;
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(Cells_ITEM, Item_Model("a_cells.md3"));
+#endif
+
+REGISTER_ITEM(Cells, Ammo) {
+#ifdef GAMEQC
+    this.m_model    =   MDL_Cells_ITEM;
+#endif
+    this.m_name     =   "cells";
+    this.m_icon     =   "ammo_cells";
+#ifdef SVQC
+    this.m_botvalue =   2000;
+    this.m_itemid   =   IT_CELLS;
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(Plasma_ITEM, Item_Model("a_cells.md3"));
+#endif
+
+REGISTER_ITEM(Plasma, Ammo) {
+#ifdef GAMEQC
+    this.m_model    =   MDL_Plasma_ITEM;
+#endif
+    this.m_name     =   "plasma";
+    this.m_icon     =   "ammo_plasma";
+#ifdef SVQC
+    this.m_botvalue =   2000;
+    this.m_itemid   =   IT_PLASMA;
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(Rockets_ITEM, Item_Model("a_rockets.md3"));
+#endif
+
+REGISTER_ITEM(Rockets, Ammo) {
+#ifdef GAMEQC
+    this.m_model    =   MDL_Rockets_ITEM;
+#endif
+    this.m_name     =   "rockets";
+    this.m_icon     =   "ammo_rockets";
+#ifdef SVQC
+    this.m_botvalue =   3000;
+    this.m_itemid   =   IT_ROCKETS;
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(Shells_ITEM, Item_Model("a_shells.md3"));
+#endif
+
+REGISTER_ITEM(Shells, Ammo) {
+#ifdef GAMEQC
+    this.m_model    =   MDL_Shells_ITEM;
+#endif
+    this.m_name     =   "shells";
+    this.m_icon     =   "ammo_shells";
+#ifdef SVQC
+    this.m_botvalue =   500;
+    this.m_itemid   =   IT_SHELLS;
 #endif
+}
index f8669e0e08503baa95529bef70f2424944ceb159..cca0b541843613470a8d49e0e530283bc2d356de 100644 (file)
@@ -1,89 +1 @@
 #include "armor.qh"
-#ifdef SVQC
-    #include <common/t_items.qh>
-#endif
-
-#ifndef MENUQC
-MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3"));
-SOUND(ArmorSmall, "misc/armor1");
-#endif
-
-REGISTER_ITEM(ArmorSmall, Armor) {
-#ifndef MENUQC
-    this.m_model                =   MDL_ArmorSmall_ITEM;
-    this.m_sound                =   SND_ArmorSmall;
-#endif
-    this.m_name                 =   "5 Armor";
-    this.m_icon                 =   "armor";
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
-    this.m_itemid               =   IT_ARMOR_SHARD;
-    this.m_respawntime          =   GET(g_pickup_respawntime_short);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_short);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3"));
-SOUND(ArmorMedium, "misc/armor10");
-#endif
-
-REGISTER_ITEM(ArmorMedium, Armor) {
-#ifndef MENUQC
-    this.m_model                =   MDL_ArmorMedium_ITEM;
-    this.m_sound                =   SND_ArmorMedium;
-#endif
-    this.m_name                 =   "25 Armor";
-    this.m_icon                 =   "armor";
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_MID;
-    this.m_itemid               =   IT_ARMOR;
-    this.m_respawntime          =   GET(g_pickup_respawntime_medium);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_medium);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3"));
-SOUND(ArmorLarge, "misc/armor17_5");
-#endif
-
-REGISTER_ITEM(ArmorLarge, Armor) {
-#ifndef MENUQC
-    this.m_model                =   MDL_ArmorLarge_ITEM;
-    this.m_sound                =   SND_ArmorLarge;
-#endif
-    this.m_name                 =   "50 Armor";
-    this.m_icon                 =   "armor";
-    this.m_color                =   '0 1 0';
-    this.m_waypoint             =   _("Large armor");
-#ifdef SVQC
-    this.m_botvalue             =   20000; // FIXME: higher than BOT_PICKUP_RATING_HIGH?
-    this.m_itemid               =   IT_ARMOR;
-    this.m_respawntime          =   GET(g_pickup_respawntime_long);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3"));
-SOUND(ArmorMega, "misc/armor25");
-#endif
-
-REGISTER_ITEM(ArmorMega, Armor) {
-#ifndef MENUQC
-    this.m_model                =   MDL_ArmorMega_ITEM;
-    this.m_sound                =   SND_ArmorMega;
-#endif
-    this.m_name                 =   "100 Armor";
-    this.m_icon                 =   "item_large_armor";
-    this.m_color                =   '0 1 0';
-    this.m_waypoint             =   _("Mega armor");
-    this.m_waypointblink        =   2;
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
-    this.m_itemid               =   IT_ARMOR;
-    this.m_respawntime          =   GET(g_pickup_respawntime_long);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
-#endif
-}
index adb93e7c94eb8010c2ca8de8da34e6da1e7b7f2f..4b6b9fdf6c4c8554599bdcc9e3e14830ea699fd3 100644 (file)
-#ifndef ARMOR_H
-#define ARMOR_H
+#pragma once
+
 #include "pickup.qh"
 CLASS(Armor, Pickup)
 #ifdef SVQC
-    ATTRIB(Armor, m_mins, vector, '-16 -16 0')
-    ATTRIB(Armor, m_maxs, vector, '16 16 48')
-    ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
+    ATTRIB(Armor, m_mins, vector, '-16 -16 0');
+    ATTRIB(Armor, m_maxs, vector, '16 16 48');
+    ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc);
 #endif
 ENDCLASS(Armor)
+
+#ifdef SVQC
+    #include <common/t_items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3"));
+SOUND(ArmorSmall, Item_Sound("armor1"));
+#endif
+
+REGISTER_ITEM(ArmorSmall, Armor) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_ArmorSmall_ITEM;
+    this.m_sound                =   SND_ArmorSmall;
+#endif
+    this.m_name                 =   "5 Armor";
+    this.m_icon                 =   "armor";
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
+    this.m_itemid               =   IT_ARMOR_SHARD;
+    this.m_respawntime          =   GET(g_pickup_respawntime_short);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_short);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3"));
+SOUND(ArmorMedium, Item_Sound("armor10"));
+#endif
+
+REGISTER_ITEM(ArmorMedium, Armor) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_ArmorMedium_ITEM;
+    this.m_sound                =   SND_ArmorMedium;
+#endif
+    this.m_name                 =   "25 Armor";
+    this.m_icon                 =   "armor";
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_MID;
+    this.m_itemid               =   IT_ARMOR;
+    this.m_respawntime          =   GET(g_pickup_respawntime_medium);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_medium);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(ArmorBig_ITEM, Item_Model("item_armor_big.md3"));
+SOUND(ArmorBig, Item_Sound("armor17_5"));
+#endif
+
+REGISTER_ITEM(ArmorBig, Armor) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_ArmorBig_ITEM;
+    this.m_sound                =   SND_ArmorBig;
+#endif
+    this.m_name                 =   "50 Armor";
+    this.m_icon                 =   "armor";
+    this.m_color                =   '0 1 0';
+    this.m_waypoint             =   _("Big armor");
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
+    this.m_itemid               =   IT_ARMOR;
+    this.m_respawntime          =   GET(g_pickup_respawntime_long);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3"));
+SOUND(ArmorMega, Item_Sound("armor25"));
+#endif
+
+REGISTER_ITEM(ArmorMega, Armor) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_ArmorMega_ITEM;
+    this.m_sound                =   SND_ArmorMega;
+#endif
+    this.m_name                 =   "100 Armor";
+    this.m_icon                 =   "item_large_armor";
+    this.m_color                =   '0 1 0';
+    this.m_waypoint             =   _("Mega armor");
+    this.m_waypointblink        =   2;
+#ifdef SVQC
+    this.m_maxs                 =   '16 16 70';
+    this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
+    this.m_itemid               =   IT_ARMOR;
+    this.m_respawntime          =   GET(g_pickup_respawntime_long);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
 #endif
+}
index 93ba2f36e2ab1669bd3b707db541b9a85eac0bd3..49a34c1c8ee280f159139311ae4b245c91a23c4f 100644 (file)
@@ -1,89 +1 @@
 #include "health.qh"
-#ifdef SVQC
-    #include <common/t_items.qh>
-#endif
-
-#ifndef MENUQC
-MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3"));
-SOUND(HealthSmall, "misc/minihealth");
-#endif
-
-REGISTER_ITEM(HealthSmall, Health) {
-#ifndef MENUQC
-    this.m_model                =   MDL_HealthSmall_ITEM;
-    this.m_sound                =   SND_HealthSmall;
-#endif
-    this.m_name                 =   "5 Health";
-    this.m_icon                 =   "health";
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
-    this.m_itemid               =   IT_5HP;
-    this.m_respawntime          =   GET(g_pickup_respawntime_short);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_short);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3"));
-SOUND(HealthMedium, "misc/mediumhealth");
-#endif
-
-REGISTER_ITEM(HealthMedium, Health) {
-#ifndef MENUQC
-    this.m_model                =   MDL_HealthMedium_ITEM;
-    this.m_sound                =   SND_HealthMedium;
-#endif
-    this.m_name                 =   "25 Health";
-    this.m_icon                 =   "health";
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_MID;
-    this.m_itemid               =   IT_25HP;
-    this.m_respawntime          =   GET(g_pickup_respawntime_short);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_short);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3"));
-SOUND(HealthLarge, "misc/mediumhealth");
-#endif
-
-REGISTER_ITEM(HealthLarge, Health) {
-#ifndef MENUQC
-    this.m_model                =   MDL_HealthLarge_ITEM;
-    this.m_sound                =   SND_HealthLarge;
-#endif
-    this.m_name                 =   "50 Health";
-    this.m_icon                 =   "health";
-    this.m_color                =   '1 0 0';
-    this.m_waypoint             =   _("Large health");
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_MID;
-    this.m_itemid               =   IT_25HP;
-    this.m_respawntime          =   GET(g_pickup_respawntime_medium);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_medium);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(HealthMega_ITEM, Item_Model("g_h100.md3"));
-SOUND(HealthMega, "misc/megahealth");
-#endif
-
-REGISTER_ITEM(HealthMega, Health) {
-#ifndef MENUQC
-    this.m_model                =   MDL_HealthMega_ITEM;
-    this.m_sound                =   SND_HealthMega;
-#endif
-    this.m_name                 =   "100 Health";
-    this.m_icon                 =   "item_mega_health";
-    this.m_color                =   '1 0 0';
-    this.m_waypoint             =   _("Mega health");
-    this.m_waypointblink        =   2;
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
-    this.m_itemid               =   IT_HEALTH;
-    this.m_respawntime          =   GET(g_pickup_respawntime_long);
-    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
-#endif
-}
index f7bfb5c9386d48e3d0d972fd04db2bc714769dd9..f7915987e3b01a55fcc864ce3517e7590372acfa 100644 (file)
-#ifndef HEALTH_H
-#define HEALTH_H
+#pragma once
+
 #include "pickup.qh"
 CLASS(Health, Pickup)
 #ifdef SVQC
-    ATTRIB(Health, m_mins, vector, '-16 -16 0')
-    ATTRIB(Health, m_maxs, vector, '16 16 48')
-    ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
+    ATTRIB(Health, m_mins, vector, '-16 -16 0');
+    ATTRIB(Health, m_maxs, vector, '16 16 48');
+    ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc);
 #endif
 ENDCLASS(Health)
+
+#ifdef SVQC
+    #include <common/t_items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3"));
+SOUND(HealthSmall, Item_Sound("minihealth"));
+#endif
+
+REGISTER_ITEM(HealthSmall, Health) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_HealthSmall_ITEM;
+    this.m_sound                =   SND_HealthSmall;
+#endif
+    this.m_name                 =   "5 Health";
+    this.m_icon                 =   "health";
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
+    this.m_itemid               =   IT_5HP;
+    this.m_respawntime          =   GET(g_pickup_respawntime_short);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_short);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3"));
+SOUND(HealthMedium, Item_Sound("mediumhealth"));
+#endif
+
+REGISTER_ITEM(HealthMedium, Health) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_HealthMedium_ITEM;
+    this.m_sound                =   SND_HealthMedium;
+#endif
+    this.m_name                 =   "25 Health";
+    this.m_icon                 =   "health";
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_MID;
+    this.m_itemid               =   IT_25HP;
+    this.m_respawntime          =   GET(g_pickup_respawntime_short);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_short);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(HealthBig_ITEM, Item_Model("g_h50.md3"));
+SOUND(HealthBig, Item_Sound("mediumhealth"));
+#endif
+
+REGISTER_ITEM(HealthBig, Health) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_HealthBig_ITEM;
+    this.m_sound                =   SND_HealthBig;
+#endif
+    this.m_name                 =   "50 Health";
+    this.m_icon                 =   "health";
+    this.m_color                =   '1 0 0';
+    this.m_waypoint             =   _("Big health");
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_MID;
+    this.m_itemid               =   IT_25HP;
+    this.m_respawntime          =   GET(g_pickup_respawntime_medium);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_medium);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(HealthMega_ITEM, Item_Model("g_h100.md3"));
+SOUND(HealthMega, Item_Sound("megahealth"));
+#endif
+
+REGISTER_ITEM(HealthMega, Health) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_HealthMega_ITEM;
+    this.m_sound                =   SND_HealthMega;
+#endif
+    this.m_name                 =   "100 Health";
+    this.m_icon                 =   "item_mega_health";
+    this.m_color                =   '1 0 0';
+    this.m_waypoint             =   _("Mega health");
+    this.m_waypointblink        =   2;
+#ifdef SVQC
+    this.m_maxs                 =   '16 16 70';
+    this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
+    this.m_itemid               =   IT_HEALTH;
+    this.m_respawntime          =   GET(g_pickup_respawntime_long);
+    this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
 #endif
+}
index 7fd29e299f57f0034fde2b701fdfe949666f4c34..ec09d5c45d62d904c6e4a2ada5ec570ae3b5c4bc 100644 (file)
@@ -1,66 +1 @@
-#ifdef SVQC
-    #include <common/t_items.qh>
-#endif
-
-#include "ammo.qh"
-#include "powerup.qh"
-
-#ifndef SVQC
-.int m_itemid;
-#endif
-
-#ifndef MENUQC
-MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3"));
-#endif
-
-REGISTER_ITEM(Jetpack, Powerup) {
-#ifndef MENUQC
-    this.m_model                =   MDL_Jetpack_ITEM;
-    this.m_itemid               =   IT_JETPACK;
-#endif
-    this.m_name                 =   "Jet pack";
-    this.m_icon                 =   "jetpack";
-    this.m_color                =   '0.5 0.5 0.5';
-    this.m_waypoint             =   _("Jet Pack");
-    this.m_waypointblink        =   2;
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
-    this.m_pickupevalfunc       =   commodity_pickupevalfunc;
-#endif
-}
-
-#ifndef MENUQC
-MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3"));
-#endif
-
-REGISTER_ITEM(JetpackFuel, Ammo) {
-#ifndef MENUQC
-    this.m_model    =   MDL_JetpackFuel_ITEM;
-#endif
-    this.m_name     =   "Fuel";
-    this.m_icon     =   "ammo_fuel";
-#ifdef SVQC
-    this.m_botvalue =   BOT_PICKUP_RATING_LOW;
-    this.m_itemid   =   IT_FUEL;
-#endif
-}
-
-#ifndef MENUQC
-MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3"));
-#endif
-
-REGISTER_ITEM(JetpackRegen, Powerup) {
-#ifndef MENUQC
-    this.m_model                =   MDL_JetpackRegen_ITEM;
-#endif
-    this.m_name                 =   "Fuel regenerator";
-    this.m_icon                 =   "fuelregen";
-    this.m_color                =   '1 0.5 0';
-    this.m_waypoint             =   _("Fuel regen");
-    this.m_waypointblink        =   2;
-#ifdef SVQC
-    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
-    this.m_itemid               =   IT_FUEL_REGEN;
-    this.m_pickupevalfunc       =   commodity_pickupevalfunc;
-#endif
-}
+#include "jetpack.qh"
diff --git a/qcsrc/common/items/item/jetpack.qh b/qcsrc/common/items/item/jetpack.qh
new file mode 100644 (file)
index 0000000..8334c79
--- /dev/null
@@ -0,0 +1,68 @@
+#pragma once
+
+#ifdef SVQC
+    #include <common/t_items.qh>
+#endif
+
+#include "ammo.qh"
+#include "powerup.qh"
+
+#ifndef SVQC
+.int m_itemid;
+#endif
+
+#ifdef GAMEQC
+MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3"));
+#endif
+
+REGISTER_ITEM(Jetpack, Powerup) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_Jetpack_ITEM;
+    this.m_itemid               =   IT_JETPACK;
+#endif
+    this.m_name                 =   "Jet pack";
+    this.m_icon                 =   "jetpack";
+    this.m_color                =   '0.5 0.5 0.5';
+    this.m_waypoint             =   _("Jet Pack");
+    this.m_waypointblink        =   2;
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
+    this.m_pickupevalfunc       =   commodity_pickupevalfunc;
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3"));
+#endif
+
+REGISTER_ITEM(JetpackFuel, Ammo) {
+#ifdef GAMEQC
+    this.m_model    =   MDL_JetpackFuel_ITEM;
+#endif
+    this.m_name     =   "Fuel";
+    this.m_icon     =   "ammo_fuel";
+#ifdef SVQC
+    this.m_botvalue =   BOT_PICKUP_RATING_LOW;
+    this.m_itemid   =   IT_FUEL;
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3"));
+#endif
+
+REGISTER_ITEM(JetpackRegen, Powerup) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_JetpackRegen_ITEM;
+#endif
+    this.m_name                 =   "Fuel regenerator";
+    this.m_icon                 =   "fuelregen";
+    this.m_color                =   '1 0.5 0';
+    this.m_waypoint             =   _("Fuel regen");
+    this.m_waypointblink        =   2;
+#ifdef SVQC
+    this.m_botvalue             =   BOT_PICKUP_RATING_LOW;
+    this.m_itemid               =   IT_FUEL_REGEN;
+    this.m_pickupevalfunc       =   commodity_pickupevalfunc;
+#endif
+}
index 08f7ff9c3d6b23c5c67cbfc6ee7595e9bd906625..204a49921d5f2b84fbc3f30aaff98d692dcbf9e7 100644 (file)
@@ -1,35 +1,59 @@
-#ifndef PICKUP_H
-#define PICKUP_H
+#pragma once
+
+#ifdef SVQC
+PROPERTY(float, g_pickup_respawntime_weapon)
+PROPERTY(float, g_pickup_respawntime_superweapon)
+PROPERTY(float, g_pickup_respawntime_ammo)
+PROPERTY(float, g_pickup_respawntime_short)
+PROPERTY(float, g_pickup_respawntime_medium)
+PROPERTY(float, g_pickup_respawntime_long)
+PROPERTY(float, g_pickup_respawntime_powerup)
+PROPERTY(float, g_pickup_respawntimejitter_weapon)
+PROPERTY(float, g_pickup_respawntimejitter_superweapon)
+PROPERTY(float, g_pickup_respawntimejitter_ammo)
+PROPERTY(float, g_pickup_respawntimejitter_short)
+PROPERTY(float, g_pickup_respawntimejitter_medium)
+PROPERTY(float, g_pickup_respawntimejitter_long)
+PROPERTY(float, g_pickup_respawntimejitter_powerup)
+#endif
+
+// pickup ratings for bot logic
+const int BOT_PICKUP_RATING_LOW  =  2500;
+const int BOT_PICKUP_RATING_MID  =  5000;
+const int BOT_PICKUP_RATING_HIGH = 10000;
+
 #include <common/items/inventory.qh>
 #include <common/items/item.qh>
+#include <common/t_items.qh>
+
 CLASS(Pickup, GameItem)
-#ifndef MENUQC
-    ATTRIB(Pickup, m_model, Model, NULL)
-    ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP)
+#ifdef GAMEQC
+    ATTRIB(Pickup, m_model, Model);
+    ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP);
 #endif
-    ATTRIB(Pickup, m_name, string, string_null)
+    ATTRIB(Pickup, m_name, string);
     METHOD(Pickup, show, void(Pickup this))
     {
         TC(Pickup, this);
         LOG_INFOF("%s: %s\n", etos(this), this.m_name);
     }
+    ATTRIB(Pickup, m_itemid, int, 0);
 #ifdef SVQC
-    ATTRIB(Pickup, m_mins, vector, '-16 -16 0')
-    ATTRIB(Pickup, m_maxs, vector, '16 16 32')
-    ATTRIB(Pickup, m_botvalue, int, 0)
-    ATTRIB(Pickup, m_itemflags, int, 0)
-    ATTRIB(Pickup, m_itemid, int, 0)
+    ATTRIB(Pickup, m_mins, vector, '-16 -16 0');
+    ATTRIB(Pickup, m_maxs, vector, '16 16 32');
+    ATTRIB(Pickup, m_botvalue, int, 0);
+    ATTRIB(Pickup, m_itemflags, int, 0);
     float generic_pickupevalfunc(entity player, entity item);
-    ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc)
-    ATTRIB(Pickup, m_respawntime, float(), func_null)
-    ATTRIB(Pickup, m_respawntimejitter, float(), func_null)
+    ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc);
+    ATTRIB(Pickup, m_respawntime, float());
+    ATTRIB(Pickup, m_respawntimejitter, float());
     float Item_GiveTo(entity item, entity player);
     METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player))
     {
         TC(Pickup, this);
         bool b = Item_GiveTo(item, player);
         if (b) {
-            LOG_TRACEF("entity %i picked up %s\n", player, this.m_name);
+            LOG_DEBUGF("entity %i picked up %s", player, this.m_name);
             player.inventory.inv_items[this.m_id]++;
             Inventory_update(player);
         }
@@ -38,5 +62,3 @@ CLASS(Pickup, GameItem)
     bool ITEM_HANDLE(Pickup, Pickup this, entity item, entity player);
 #endif
 ENDCLASS(Pickup)
-
-#endif
index 375f958a16ddb798c93ab5de7fdfe48f404dabbd..7c7405b75bb4e34dcad03f2260454c637f19ef48 100644 (file)
@@ -1,37 +1 @@
 #include "powerup.qh"
-
-#ifndef MENUQC
-MODEL(Strength_ITEM, Item_Model("g_strength.md3"));
-SOUND(Strength, "misc/powerup");
-#endif
-
-REGISTER_ITEM(Strength, Powerup) {
-#ifndef MENUQC
-    this.m_model            =   MDL_Strength_ITEM;
-    this.m_sound            =   SND_Strength;
-#endif
-    this.m_name             =   "Strength Powerup";
-    this.m_icon             =   "strength";
-    this.m_color            =   '0 0 1';
-    this.m_waypoint         =   _("Strength");
-    this.m_waypointblink    =   2;
-    this.m_itemid           =   IT_STRENGTH;
-}
-
-#ifndef MENUQC
-MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
-SOUND(Shield, "misc/powerup_shield");
-#endif
-
-REGISTER_ITEM(Shield, Powerup) {
-#ifndef MENUQC
-    this.m_model            =   MDL_Shield_ITEM;
-    this.m_sound            =   SND_Shield;
-#endif
-    this.m_name             =   "Shield";
-    this.m_icon             =   "shield";
-    this.m_color            =   '1 0 1';
-    this.m_waypoint         =   _("Shield");
-    this.m_waypointblink    =   2;
-    this.m_itemid           =   IT_INVINCIBLE;
-}
index 0a3d907288a203cee53c8e61575de30ebd0cfb87..80b1e6affc0a12747c9d2fd0b14c4ef25659da32 100644 (file)
@@ -8,11 +8,51 @@
 #include "pickup.qh"
 CLASS(Powerup, Pickup)
 #ifdef SVQC
-    ATTRIB(Powerup, m_mins, vector, '-16 -16 0')
-    ATTRIB(Powerup, m_maxs, vector, '16 16 48')
-    ATTRIB(Powerup, m_botvalue, int, 100000)
-    ATTRIB(Powerup, m_itemflags, int, FL_POWERUP)
-    ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup))
-    ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup))
+    ATTRIB(Powerup, m_mins, vector, '-16 -16 0');
+    ATTRIB(Powerup, m_maxs, vector, '16 16 80');
+    ATTRIB(Powerup, m_botvalue, int, 100000);
+    ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
+    ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
+    ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup));
 #endif
 ENDCLASS(Powerup)
+
+#ifdef GAMEQC
+MODEL(Strength_ITEM, Item_Model("g_strength.md3"));
+SOUND(Strength, Item_Sound("powerup"));
+#endif
+
+REGISTER_ITEM(Strength, Powerup) {
+#ifdef GAMEQC
+    this.m_model            =   MDL_Strength_ITEM;
+    this.m_sound            =   SND_Strength;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
+#endif
+    this.m_name             =   "Strength Powerup";
+    this.m_icon             =   "strength";
+    this.m_color            =   '0 0 1';
+    this.m_waypoint         =   _("Strength");
+    this.m_waypointblink    =   2;
+    this.m_itemid           =   IT_STRENGTH;
+}
+
+#ifdef GAMEQC
+MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
+SOUND(Shield, Item_Sound("powerup_shield"));
+#endif
+
+REGISTER_ITEM(Shield, Powerup) {
+#ifdef GAMEQC
+    this.m_model            =   MDL_Shield_ITEM;
+    this.m_sound            =   SND_Shield;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_SHIELD_RESPAWN;
+#endif
+    this.m_name             =   "Shield";
+    this.m_icon             =   "shield";
+    this.m_color            =   '1 0 1';
+    this.m_waypoint         =   _("Shield");
+    this.m_waypointblink    =   2;
+    this.m_itemid           =   IT_INVINCIBLE;
+}
index 2ec902f68556d121bdceb8122adbef84f4edc999..2af3d90823e9dcddee7f187889ca50e2da4eddf2 100644 (file)
@@ -1,13 +1,12 @@
+#include "mapinfo.qh"
 #if defined(CSQC)
     #include "../client/defs.qh"
     #include "util.qh"
-    #include <common/weapons/all.qh>
-    #include "mapinfo.qh"
+    #include <common/weapons/_all.qh>
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include "util.qh"
-    #include <common/monsters/all.qh>
-    #include "mapinfo.qh"
+    #include <common/monsters/_mod.qh>
 #endif
 
 // generic string stuff
@@ -150,7 +149,11 @@ float _MapInfo_FilterList_cmp(float i, float j, entity pass)
        return strcasecmp(a, b);
 }
 
-float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
+float MapInfo_FilterGametype(Gametype pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
+{
+       return _MapInfo_FilterGametype(pGametype.m_flags, pFeatures, pFlagsRequired, pFlagsForbidden, pAbortOnGenerate);
+}
+float _MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
 {
        float i, j;
        if (!_MapInfo_filtered_allocated)
@@ -161,10 +164,10 @@ float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, i
        MapInfo_count = 0;
        for(i = 0, j = -1; i < _MapInfo_globcount; ++i)
        {
-               if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, 0) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.
+               if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, NULL) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.
                        if(pAbortOnGenerate)
                        {
-                               LOG_TRACE("Autogenerated a .mapinfo, doing the rest later.\n");
+                               LOG_TRACE("Autogenerated a .mapinfo, doing the rest later.");
                                MapInfo_progress = i / _MapInfo_globcount;
                                return 0;
                        }
@@ -249,7 +252,7 @@ string unquote(string s)
 
 float MapInfo_Get_ByID(float i)
 {
-       if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0))
+       if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, NULL))
                return 1;
        return 0;
 }
@@ -265,7 +268,6 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
        float i;
        float inWorldspawn;
        float r;
-       float twoBaseModes;
        float diameter, spawnpoints;
        float spawnplaces;
 
@@ -345,21 +347,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                        }
                        else if(k == "classname")
                        {
-                               if(v == "dom_controlpoint")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
-                               else if(v == "item_flag_team2")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
-                               else if(v == "team_CTF_blueflag")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
-                               else if(v == "invasion_spawnpoint")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_INVASION;
-                               else if(v == "target_assault_roundend")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
-                               else if(v == "onslaught_generator")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
-                               else if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_NEXBALL;
-                               else if(v == "info_player_team1")
+                               if(v == "info_player_team1")
                                        ++spawnpoints;
                                else if(v == "info_player_team2")
                                        ++spawnpoints;
@@ -367,10 +355,6 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                                        ++spawnpoints;
                                else if(v == "info_player_deathmatch")
                                        ++spawnpoints;
-                               else if(v == "trigger_race_checkpoint")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RACE;
-                               else if(v == "target_startTimer")
-                                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS;
                                else if(v == "weapon_nex")
                                        { }
                                else if(v == "weapon_railgun")
@@ -385,47 +369,39 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                                        MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS;
                                else if(v == "target_music" || v == "trigger_music")
                                        _MapInfo_Map_worldspawn_music = string_null; // don't use regular BGM
+                               else
+                                       FOREACH(Gametypes, true, it.m_generate_mapinfo(it, v));
                        }
                }
        }
        if(inWorldspawn)
        {
-               LOG_MAPWARN(fn, " ended still in worldspawn, BUG\n");
+               LOG_WARN(fn, " ended still in worldspawn, BUG");
                return 0;
        }
        diameter = vlen(mapMaxs - mapMins);
 
-       twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT | MAPINFO_TYPE_RACE | MAPINFO_TYPE_NEXBALL);
-       if(twoBaseModes && (MapInfo_Map_supportedGametypes == twoBaseModes))
+       int twoBaseModes = 0;
+       FOREACH(Gametypes, it.m_isTwoBaseMode(), twoBaseModes |= it.m_flags);
+       if(twoBaseModes && (twoBaseModes &= MapInfo_Map_supportedGametypes))
        {
-               // we have a CTF-only or Assault-only map. Don't add other modes then,
-               // as the map is too symmetric for them.
+               // we have a symmetrical map, don't add the modes without bases
        }
        else
        {
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;      // DM always works
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;             // LMS always works
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY;                // Keepaway always works
-
-               if(spawnpoints >= 8  && diameter > 4096) {
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG;
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA;
-               }
-               if(spawnpoints >= 12 && diameter > 5120)
-                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
+               FOREACH(Gametypes, it.m_isAlwaysSupported(it, spawnpoints, diameter), MapInfo_Map_supportedGametypes |= it.m_flags);
        }
 
-       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE)
+       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE.m_flags)
        if(!spawnplaces)
        {
-               MapInfo_Map_supportedGametypes &= ~MAPINFO_TYPE_RACE;
-               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS;
+               MapInfo_Map_supportedGametypes &= ~MAPINFO_TYPE_RACE.m_flags;
+               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS.m_flags;
        }
 
        LOG_TRACE("-> diameter ",    ftos(diameter));
        LOG_TRACE(";  spawnpoints ", ftos(spawnpoints));
-       LOG_TRACE(";  modes ",       ftos(MapInfo_Map_supportedGametypes), "\n");
+       LOG_TRACE(";  modes ",       ftos(MapInfo_Map_supportedGametypes));
 
        fclose(fh);
 
@@ -447,7 +423,7 @@ void _MapInfo_Map_Reset()
        MapInfo_Map_maxs = '0 0 0';
 }
 
-string _MapInfo_GetDefault(float t)
+string _MapInfo_GetDefault(Gametype t)
 {
        switch(t)
        {
@@ -472,11 +448,11 @@ string _MapInfo_GetDefault(float t)
        }
 }
 
-void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int load_default)
+void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisType, int load_default)
 {
        string sa;
-       MapInfo_Map_supportedGametypes |= pThisType;
-       if(!(pThisType & pWantedType))
+       MapInfo_Map_supportedGametypes |= pThisType.m_flags;
+       if(!(pThisType.m_flags & pWantedType.m_flags))
                return;
 
        if(load_default)
@@ -499,55 +475,13 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo
                cvar_set("timelimit", sa);
        s = cdr(s);
 
-       if(pWantedType == MAPINFO_TYPE_TEAM_DEATHMATCH)
-       {
-               sa = car(s);
-               if(sa != "")
-                       cvar_set("g_tdm_teams", sa);
-               s = cdr(s);
-       }
-
-       if(pWantedType == MAPINFO_TYPE_KEYHUNT)
-       {
-               sa = car(s);
-               if(sa != "")
-                       cvar_set("g_keyhunt_teams", sa);
-               s = cdr(s);
-       }
-
-       if(pWantedType == MAPINFO_TYPE_CA)
-       {
-               sa = car(s);
-               if(sa != "")
-                       cvar_set("g_ca_teams", sa);
-               s = cdr(s);
-       }
-
-       if(pWantedType == MAPINFO_TYPE_FREEZETAG)
-       {
-               sa = car(s);
-               if(sa != "")
-                       cvar_set("g_freezetag_teams", sa);
-               s = cdr(s);
-       }
-
-       if(pWantedType == MAPINFO_TYPE_CTF)
+       if(pWantedType.m_setTeams)
        {
                sa = car(s);
                if(sa != "")
-                       cvar_set("fraglimit", sa);
-               s = cdr(s);
-       }
-
-       /* keepaway wuz here
-       if(pWantedType == MAPINFO_TYPE_KEEPAWAY)
-       {
-               sa = car(s);
-               if(sa != "")
-                       cvar_set("fraglimit", sa);
+                       pWantedType.m_setTeams(sa);
                s = cdr(s);
        }
-       */
 
        // rc = timelimit timelimit_qualification laps laps_teamplay
        if(pWantedType == MAPINFO_TYPE_RACE)
@@ -569,19 +503,6 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo
                s = cdr(s);
        }
 
-       if(pWantedType == MAPINFO_TYPE_CTS)
-       {
-               sa = car(s);
-
-               // this is the skill of the map
-               // not parsed by anything yet
-               // for map databases
-               //if(sa != "")
-               //      cvar_set("fraglimit", sa);
-
-               s = cdr(s);
-       }
-
        if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_CTS) // these modes don't use fraglimit
        {
                cvar_set("leadlimit", "0");
@@ -595,22 +516,20 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo
        }
 }
 
-string _MapInfo_GetDefaultEx(float t)
+string _MapInfo_GetDefaultEx(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.model2);
-       return "";
+       return t ? t.model2 : "";
 }
 
-float _MapInfo_GetTeamPlayBool(float t)
+float _MapInfo_GetTeamPlayBool(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.team);
-       return false;
+       return t ? t.team : false;
 }
 
-void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
+void _MapInfo_Map_ApplyGametypeEx(string s, Gametype pWantedType, Gametype pThisType)
 {
-       MapInfo_Map_supportedGametypes |= pThisType;
-       if (!(pThisType & pWantedType))
+       MapInfo_Map_supportedGametypes |= pThisType.m_flags;
+       if (!(pThisType.m_flags & pWantedType.m_flags))
                return;
 
        // reset all the cvars to their defaults
@@ -628,7 +547,7 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
                if (sa == "") continue;
                int p = strstrofs(sa, "=", 0);
                if (p < 0) {
-                       LOG_MAPWARNF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa);
+                       LOG_WARNF("Invalid gametype setting in mapinfo for gametype %s: %s", MapInfo_Type_ToString(pWantedType), sa);
                        continue;
                }
                string k = substring(sa, 0, p);
@@ -668,7 +587,7 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
                }
                FOREACH(Gametypes, true, handled |= it.m_parse_mapinfo(k, v));
                if (!handled)
-            LOG_MAPWARNF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa);
+            LOG_WARNF("Invalid gametype setting in mapinfo for gametype %s: %s", MapInfo_Type_ToString(pWantedType), sa);
        }
 
        if (pWantedType == MAPINFO_TYPE_RACE && cvar("g_race_teams") >= 2)
@@ -683,18 +602,12 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType)
        }
 }
 
-Gametype MapInfo_Type(int t)
-{
-       FOREACH(Gametypes, it.items == t, return it);
-       return NULL;
-}
-
-int MapInfo_Type_FromString(string t)
+Gametype MapInfo_Type_FromString(string t)
 {
 #define deprecate(from, to) MACRO_BEGIN { \
        if (t == #from) { \
                string replacement = #to; \
-               LOG_MAPWARNF("MapInfo_Type_FromString (probably %s): using deprecated name '%s'. Should use '%s'.\n", MapInfo_Map_bspname, t, replacement); \
+               LOG_WARNF("MapInfo_Type_FromString (probably %s): using deprecated name '%s'. Should use '%s'.", MapInfo_Map_bspname, t, replacement); \
                t = replacement; \
        } \
 } MACRO_END
@@ -704,31 +617,25 @@ int MapInfo_Type_FromString(string t)
        deprecate(invasion, inv);
        deprecate(assault, as);
        deprecate(race, rc);
-       if (t == "all") return MAPINFO_TYPE_ALL;
-       FOREACH(Gametypes, it.mdl == t, return it.items);
-       return 0;
+       FOREACH(Gametypes, it.mdl == t, return it);
+       return NULL;
 #undef deprecate
 }
 
-string MapInfo_Type_Description(float t)
+string MapInfo_Type_Description(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.gametype_description);
-       return "";
+       return t ? t.gametype_description : "";
 }
 
-string MapInfo_Type_ToString(float t)
+string MapInfo_Type_ToString(Gametype t)
 {
-       if(t == MAPINFO_TYPE_ALL)
-               return "all";
-       FOREACH(Gametypes, it.items == t, return it.mdl);
-       return "";
+       return t ? t.mdl : "";
 }
 
-string MapInfo_Type_ToText(float t)
+string MapInfo_Type_ToText(Gametype t)
 {
-       FOREACH(Gametypes, it.items == t, return it.message);
        /* xgettext:no-c-format */
-       return _("@!#%'n Tuba Throwing");
+       return t ? t.message : _("@!#%'n Tuba Throwing");
 }
 
 void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse)
@@ -770,7 +677,7 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s,
                {
                        fh = fopen(s, FILE_READ);
                        if(fh < 0)
-                               LOG_MAPWARN("Map ", pFilename, " references not existing config file ", s, "\n");
+                               LOG_WARN("Map ", pFilename, " references not existing config file ", s);
                        else
                        {
                                for (;;)
@@ -799,23 +706,23 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s,
                        }
                }
                else
-                       LOG_MAPWARN("Map ", pFilename, " uses too many levels of inclusion\n");
+                       LOG_WARN("Map ", pFilename, " uses too many levels of inclusion");
        }
        else if(t == "")
-               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored");
        else if (!cvar_value_issafe(t))
-               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored");
        else if (!cvar_value_issafe(s))
-               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored");
        else if(matchacl(MAPINFO_SETTEMP_ACL_SYSTEM, t) <= 0)
-               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
+               LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored");
        else if(matchacl(acl, t) <= 0)
-               LOG_MAPWARN("Map ", pFilename, " contains a denied setting, ignored\n");
+               LOG_WARN("Map ", pFilename, " contains a denied setting, ignored");
        else
        {
                if(type == 0) // server set
                {
-                       LOG_TRACE("Applying temporary setting ", t, " := ", s, "\n");
+                       LOG_TRACE("Applying temporary setting ", t, " := ", s);
                        if(cvar("g_campaign"))
                                cvar_set(t, s); // this is a wrapper and is always temporary anyway; no need to backup old values then
                        else
@@ -823,7 +730,7 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s,
                }
                else
                {
-                       LOG_TRACE("Applying temporary client setting ", t, " := ", s, "\n");
+                       LOG_TRACE("Applying temporary client setting ", t, " := ", s);
                        MapInfo_Map_clientstuff = strcat(
                                        MapInfo_Map_clientstuff, "cl_cmd settemp \"", t, "\" \"", s, "\"\n"
                                        );
@@ -857,7 +764,7 @@ float MapInfo_isRedundant(string fn, string t)
 }
 
 // load info about a map by name into the MapInfo_Map_* globals
-float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int pGametypeToSet)
+float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gametype pGametypeToSet)
 {
        string fn;
        string s, t;
@@ -870,11 +777,11 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
 
        if(strstrofs(pFilename, "/", 0) >= 0)
        {
-               LOG_MAPWARN("Invalid character in map name, ignored\n");
+               LOG_WARN("Invalid character in map name, ignored");
                return 0;
        }
 
-       if(pGametypeToSet == 0)
+       if(pGametypeToSet == NULL)
                if(MapInfo_Cache_Retrieve(pFilename))
                        return 1;
 
@@ -939,9 +846,9 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                        if(MapInfo_Map_flags & MAPINFO_FLAG_FRUSTRATING)
                                fputs(fh, "frustrating\n");
 
-                       for(i = 1; i <= MapInfo_Map_supportedGametypes; i *= 2)
-                               if(MapInfo_Map_supportedGametypes & i)
-                                       fputs(fh, sprintf("gametype %s // defaults: %s\n", MapInfo_Type_ToString(i), _MapInfo_GetDefaultEx(i)));
+                       FOREACH(Gametypes, MapInfo_Map_supportedGametypes & it.m_flags, {
+                               fputs(fh, sprintf("gametype %s // defaults: %s\n", MapInfo_Type_ToString(it), _MapInfo_GetDefaultEx(it)));
+                       });
 
                        if(fexists(strcat("scripts/", pFilename, ".arena")))
                                fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n");
@@ -960,7 +867,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                                error("... but I just wrote it!");
                }
 
-               LOG_MAPWARN("autogenerated mapinfo file ", fn, " has been loaded; please edit that file and move it to maps/", pFilename, ".mapinfo\n");
+               LOG_WARN("autogenerated mapinfo file ", fn, " has been loaded; please edit that file and move it to maps/", pFilename, ".mapinfo");
        }
 
        _MapInfo_Map_Reset();
@@ -999,7 +906,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                        else if(t == "monsters") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS;
                        else if(t == "new_toys") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;
                        else
-                               LOG_MAPWARN("Map ", pFilename, " supports unknown feature ", t, ", ignored\n");
+                               LOG_WARN("Map ", pFilename, " supports unknown feature ", t, ", ignored");
                }
                else if(t == "hidden")
                {
@@ -1025,21 +932,21 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                else if(t == "type")
                {
                        t = car(s); s = cdr(s);
-                       f = MapInfo_Type_FromString(t);
-                       LOG_MAPWARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.\n");
+                       Gametype f = MapInfo_Type_FromString(t);
+                       LOG_WARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.");
                        if(f)
                                _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f, true);
                        else
-                               LOG_MAPWARN("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
+                               LOG_WARN("Map ", pFilename, " supports unknown game type ", t, ", ignored");
                }
                else if(t == "gametype")
                {
                        t = car(s); s = cdr(s);
-                       f = MapInfo_Type_FromString(t);
+                       Gametype f = MapInfo_Type_FromString(t);
                        if(f)
                                _MapInfo_Map_ApplyGametypeEx (s, pGametypeToSet, f);
                        else
-                               LOG_MAPWARN("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
+                               LOG_WARN("Map ", pFilename, " supports unknown game type ", t, ", ignored");
                }
                else if(t == "size")
                {
@@ -1050,16 +957,16 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                        t = car(s); s = cdr(s); d = stof(t);
                        t = car(s); s = cdr(s); e = stof(t);
                        if(s == "")
-                               LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");
+                               LOG_WARN("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z");
                        else
                        {
                                t = car(s); s = cdr(s); f = stof(t);
                                if(s != "")
-                                       LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");
+                                       LOG_WARN("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z");
                                else
                                {
                                        if(a >= d || b >= e || c >= f)
-                                               LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs\n");
+                                               LOG_WARN("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs");
                                        else
                                        {
                                                MapInfo_Map_mins.x = a;
@@ -1075,37 +982,41 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                else if(t == "settemp_for_type")
                {
                        t = car(s); s = cdr(s);
-                       if((f = MapInfo_Type_FromString(t)))
+                       bool all = t == "all";
+                       Gametype f = NULL;
+                       if(all || (f = MapInfo_Type_FromString(t)))
                        {
-                               if(f & pGametypeToSet)
+                               if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags)
                                {
                                        _MapInfo_Parse_Settemp(pFilename, acl, 0, s, 1);
                                }
                        }
                        else
                        {
-                               LOG_MAPWARN("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n");
+                               LOG_WARN("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored");
                        }
                }
                else if(t == "clientsettemp_for_type")
                {
                        t = car(s); s = cdr(s);
-                       if((f = MapInfo_Type_FromString(t)))
+                       bool all = t == "all";
+                       Gametype f = NULL;
+                       if(all || (f = MapInfo_Type_FromString(t)))
                        {
-                               if(f & pGametypeToSet)
+                               if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags)
                                {
                                        _MapInfo_Parse_Settemp(pFilename, acl, 1, s, 1);
                                }
                        }
                        else
                        {
-                               LOG_MAPWARN("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored\n");
+                               LOG_WARN("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored");
                        }
                }
                else if(t == "fog")
                {
                        if (!cvar_value_issafe(s))
-                               LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful fog setting, ignored\n");
+                               LOG_WARN("Map ", pFilename, " contains a potentially harmful fog setting, ignored");
                        else
                                MapInfo_Map_fog = s;
                }
@@ -1121,7 +1032,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                        if(pGametypeToSet)
                        {
                                if (!cvar_value_issafe(t))
-                                       LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n");
+                                       LOG_WARN("Map ", pFilename, " contains a potentially harmful cdtrack, ignored");
                                else
                                        MapInfo_Map_clientstuff = strcat(
                                                MapInfo_Map_clientstuff, "cd loop \"", t, "\"\n"
@@ -1129,7 +1040,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
                        }
                }
                else
-                       LOG_MAPWARN("Map ", pFilename, " provides unknown info item ", t, ", ignored\n");
+                       LOG_WARN("Map ", pFilename, " provides unknown info item ", t, ", ignored");
        }
        fclose(fh);
 
@@ -1143,24 +1054,24 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p
        MapInfo_Cache_Store();
        if(MapInfo_Map_supportedGametypes != 0)
                return r;
-       LOG_MAPWARN("Map ", pFilename, " supports no game types, ignored\n");
+       LOG_WARN("Map ", pFilename, " supports no game types, ignored");
        return 0;
 }
-float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, int pGametypeToSet)
+int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametypeToSet)
 {
-       float r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet);
+       int r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet);
 
        if(cvar("g_tdm_on_dm_maps"))
        {
                // if this is set, all DM maps support TDM too
-               if (!(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH))
-                       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)
+               if (!(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags))
+                       if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)
                                _MapInfo_Map_ApplyGametypeEx ("", pGametypeToSet, MAPINFO_TYPE_TEAM_DEATHMATCH);
        }
 
        if(pGametypeToSet)
        {
-               if(!(MapInfo_Map_supportedGametypes & pGametypeToSet))
+               if(!(MapInfo_Map_supportedGametypes & pGametypeToSet.m_flags))
                {
                        error("Can't select the requested game type. This should never happen as the caller should prevent it!\n");
                        //_MapInfo_Map_ApplyGametypeEx("", pGametypeToSet, MAPINFO_TYPE_DEATHMATCH);
@@ -1233,20 +1144,21 @@ int MapInfo_CurrentFeatures()
        return req;
 }
 
-int MapInfo_CurrentGametype()
+Gametype MapInfo_CurrentGametype()
 {
-       int prev = cvar("gamecfg");
-       FOREACH(Gametypes, cvar(it.netname) && it.items != prev, return it.items);
-       if (prev) return prev;
-       return MAPINFO_TYPE_DEATHMATCH;
+       Gametype prev = Gametypes_from(cvar("gamecfg"));
+       FOREACH(Gametypes, cvar(it.netname) && it != prev, return it);
+       return prev ? prev : MAPINFO_TYPE_DEATHMATCH;
 }
 
-float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise
+float _MapInfo_CheckMap(string s, bool gametype_only) // returns 0 if the map can't be played with the current settings, 1 otherwise
 {
-       if(!MapInfo_Get_ByName(s, 1, 0))
+       if(!MapInfo_Get_ByName(s, 1, NULL))
                return 0;
-       if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0)
+       if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype().m_flags) == 0)
                return 0;
+       if (gametype_only)
+               return 1;
        if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures())
                return 0;
        return 1;
@@ -1255,14 +1167,14 @@ float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with t
 float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise
 {
        float r;
-       r = _MapInfo_CheckMap(s);
+       r = _MapInfo_CheckMap(s, false);
        MapInfo_ClearTemps();
        return r;
 }
 
-void MapInfo_SwitchGameType(int t)
+void MapInfo_SwitchGameType(Gametype t)
 {
-       FOREACH(Gametypes, true, cvar_set(it.netname, (it.items == t) ? "1" : "0"));
+       FOREACH(Gametypes, true, cvar_set(it.netname, (it == t) ? "1" : "0"));
 }
 
 void MapInfo_LoadMap(string s, float reinit)
@@ -1272,7 +1184,7 @@ void MapInfo_LoadMap(string s, float reinit)
        //if(!MapInfo_CheckMap(s))
        //{
        //      print("EMERGENCY: can't play the selected map in the given game mode. Falling back to DM.\n");
-       //      MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH);
+       //      MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH.m_flags);
        //}
 
        cvar_settemp_restore();
@@ -1282,7 +1194,7 @@ void MapInfo_LoadMap(string s, float reinit)
                localcmd(strcat("\nchangelevel ", s, "\n"));
 }
 
-string MapInfo_ListAllowedMaps(float type, float pRequiredFlags, float pForbiddenFlags)
+string MapInfo_ListAllowedMaps(Gametype type, float pRequiredFlags, float pForbiddenFlags)
 {
        string out;
        float i;
@@ -1304,7 +1216,7 @@ string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
 
        // to make absolutely sure:
        MapInfo_Enumerate();
-       MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, pRequiredFlags, pForbiddenFlags, 0);
+       _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, pRequiredFlags, pForbiddenFlags, 0);
 
        out = "";
        for(i = 0; i < MapInfo_count; ++i)
@@ -1315,49 +1227,52 @@ string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
        return substring(out, 1, strlen(out) - 1);
 }
 
-void MapInfo_LoadMapSettings_SaveGameType(float t)
+void MapInfo_LoadMapSettings_SaveGameType(Gametype t)
 {
        MapInfo_SwitchGameType(t);
-       cvar_set("gamecfg", ftos(t));
+       cvar_set("gamecfg", ftos(t.m_id));
        MapInfo_LoadedGametype = t;
 }
 
 void MapInfo_LoadMapSettings(string s) // to be called from worldspawn
 {
-       float t;
-
-       t = MapInfo_CurrentGametype();
+       Gametype t = MapInfo_CurrentGametype();
        MapInfo_LoadMapSettings_SaveGameType(t);
 
-       if(!_MapInfo_CheckMap(s)) // with underscore, it keeps temps
+       if(!_MapInfo_CheckMap(s, true)) // with underscore, it keeps temps
        {
                if(cvar("g_mapinfo_allow_unsupported_modes_and_let_stuff_break"))
                {
-                       LOG_SEVERE("can't play the selected map in the given game mode. Working with only the override settings.\n");
+                       LOG_SEVERE("can't play the selected map in the given game mode. Working with only the override settings.");
                        _MapInfo_Map_ApplyGametypeEx("", t, t);
                        return; // do not call Get_ByName!
                }
 
                if(MapInfo_Map_supportedGametypes == 0)
                {
-                       LOG_SEVERE("Mapinfo system is not functional at all. Assuming deathmatch.\n");
-                       MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH;
+                       LOG_SEVERE("Mapinfo system is not functional at all. Assuming deathmatch.");
+                       MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH.m_flags;
                        MapInfo_LoadMapSettings_SaveGameType(MAPINFO_TYPE_DEATHMATCH);
                        _MapInfo_Map_ApplyGametypeEx("", MAPINFO_TYPE_DEATHMATCH, MAPINFO_TYPE_DEATHMATCH);
                        return; // do not call Get_ByName!
                }
 
-               t = 1;
+               int _t = 1;
                while(!(MapInfo_Map_supportedGametypes & 1))
                {
-                       t *= 2;
-                       MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes / 2);
+                       _t <<= 1;
+                       MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes >> 1);
                }
+               Gametype t_prev = t;
+               FOREACH(Gametypes, it.m_flags == _t, { t = it; break; });
 
                // t is now a supported mode!
-               LOG_WARNING("can't play the selected map in the given game mode. Falling back to a supported mode.\n");
+               LOG_WARNF("can't play the selected map in the given game mode (%s). Falling back to a supported mode (%s).", t_prev.mdl, t.mdl);
                MapInfo_LoadMapSettings_SaveGameType(t);
        }
+       if(!_MapInfo_CheckMap(s, false)) { // with underscore, it keeps temps
+               LOG_WARNF("the selected map lacks features required by current settings; playing anyway.");
+       }
        MapInfo_Get_ByName(s, 1, t);
 }
 
@@ -1390,7 +1305,7 @@ int MapInfo_ForbiddenFlags()
 {
        int f = MAPINFO_FLAG_FORBIDDEN;
 
-#ifndef MENUQC
+#ifdef GAMEQC
        if (!cvar("g_maplist_allow_hidden"))
 #endif
                f |= MAPINFO_FLAG_HIDDEN;
index b7115ef552522fd2786a3749166430e9a7ae8ea8..a3e000d33351fde6a8f9edb97808375d434670cd 100644 (file)
@@ -1,32 +1,62 @@
-#ifndef MAPINFO_H
-#define MAPINFO_H
+#pragma once
 
-bool autocvar_developer_mapper;
+#include "util.qh"
 
-#define LOG_MAPWARN(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNING(__VA_ARGS__); } MACRO_END
-#define LOG_MAPWARNF(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNINGF(__VA_ARGS__); } MACRO_END
+// info about a map that MapInfo loads
+string MapInfo_Map_bspname;
+string MapInfo_Map_title;
+string MapInfo_Map_titlestring; // either bspname: title or just title, depending on whether bspname is redundant
+string MapInfo_Map_description;
+string MapInfo_Map_author;
+string MapInfo_Map_clientstuff; // not in cache, only for map load
+string MapInfo_Map_fog; // not in cache, only for map load
+int MapInfo_Map_supportedGametypes;
+int MapInfo_Map_supportedFeatures;
+int MapInfo_Map_flags;
+vector MapInfo_Map_mins; // these are '0 0 0' if not supported!
+vector MapInfo_Map_maxs; // these are '0 0 0' if not specified!
 
-#include "util.qh"
+int MAPINFO_TYPE_ALL;
+.int m_flags;
 
 CLASS(Gametype, Object)
-    ATTRIB(Gametype, m_id, int, 0)
+    ATTRIB(Gametype, m_id, int, 0);
     /** game type ID */
-    ATTRIB(Gametype, items, int, 0)
+    ATTRIB(Gametype, items, int, 0);
     /** game type name as in cvar (with g_ prefix) */
-    ATTRIB(Gametype, netname, string, string_null)
+    ATTRIB(Gametype, netname, string);
     /** game type short name */
-    ATTRIB(Gametype, mdl, string, string_null)
+    ATTRIB(Gametype, mdl, string);
     /** human readable name */
-    ATTRIB(Gametype, message, string, string_null)
+    ATTRIB(Gametype, message, string);
     /** does this gametype support teamplay? */
-    ATTRIB(Gametype, team, bool, false)
+    ATTRIB(Gametype, team, bool, false);
     /** game type defaults */
-    ATTRIB(Gametype, model2, string, string_null)
+    ATTRIB(Gametype, model2, string);
     /** game type description */
-    ATTRIB(Gametype, gametype_description, string, string_null)
+    ATTRIB(Gametype, gametype_description, string);
+#ifdef CSQC
+    ATTRIB(Gametype, m_modicons, void(vector pos, vector mySize));
+    ATTRIB(Gametype, m_modicons_reset, void());
+#endif
 
-    ATTRIB(Gametype, m_mutators, string, string_null)
-    ATTRIB(Gametype, m_parse_mapinfo, bool(string k, string v), func_null)
+    ATTRIB(Gametype, m_mutators, string);
+    METHOD(Gametype, m_parse_mapinfo, bool(string k, string v))
+    {
+        return false;
+    }
+    METHOD(Gametype, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        TC(Gametype, this);
+    }
+    METHOD(Gametype, m_isTwoBaseMode, bool())
+    {
+        return false;
+    }
+    METHOD(Gametype, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        return false;
+    }
 
     METHOD(Gametype, describe, string(Gametype this))
     {
@@ -40,150 +70,412 @@ CLASS(Gametype, Object)
         returns(this.message, strcat("gametype_", this.mdl));
     }
 
-    CONSTRUCTOR(Gametype, string hname, string sname, string g_name, bool gteamplay, string mutators, string defaults, string gdescription)
+    METHOD(Gametype, gametype_init, void(Gametype this, string hname, string sname, string g_name, bool gteamplay, string mutators, string defaults, string gdescription))
     {
-        CONSTRUCT(Gametype);
         this.netname = g_name;
         this.mdl = sname;
         this.message = hname;
         this.team = gteamplay;
-        this.m_mutators = mutators;
+        this.m_mutators = cons(sname, mutators);
         this.model2 = defaults;
         this.gametype_description = gdescription;
+
+        // same as `1 << m_id`
+        MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1);
     }
 ENDCLASS(Gametype)
 
-REGISTRY(Gametypes, BITS(4))
+REGISTRY(Gametypes, 24)
 #define Gametypes_from(i) _Gametypes_from(i, NULL)
 REGISTER_REGISTRY(Gametypes)
 REGISTRY_CHECK(Gametypes)
-int MAPINFO_TYPE_ALL;
-#define REGISTER_GAMETYPE(hname, sname, g_name, NAME, gteamplay, mutators, defaults, gdescription)          \
-    int MAPINFO_TYPE_##NAME;                                                                                \
-    bool NAME##_mapinfo(string k, string v) { return = false; }                                             \
-    REGISTER(Gametypes, MAPINFO_TYPE, g_name, m_id,                                      \
-        NEW(Gametype, hname, #sname, #g_name, gteamplay, #sname " " mutators, defaults, gdescription)       \
-    ) {                                                                                                     \
-        /* same as `1 << m_id` */                                                                           \
-        MAPINFO_TYPE_##NAME = MAPINFO_TYPE_ALL + 1; MAPINFO_TYPE_ALL |= MAPINFO_TYPE_##NAME;                \
-        this.items = MAPINFO_TYPE_##NAME;                                                                   \
-        this.m_parse_mapinfo = NAME##_mapinfo;                                                              \
-    }                                                                                                       \
-    [[accumulate]] bool NAME##_mapinfo(string k, string v)
-
-#define IS_GAMETYPE(NAME) \
-    (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
-
-REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,false,"","timelimit=20 pointlimit=30 leadlimit=0",_("Score as many frags as you can"));
-
-REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,false,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
-
-REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,false,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"))
-{
-    if (!k) {
-       cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit"));
+#define REGISTER_GAMETYPE(NAME, inst) REGISTER(Gametypes, MAPINFO_TYPE, NAME, m_id, inst)
+
+#define IS_GAMETYPE(NAME) (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
+
+CLASS(Deathmatch, Gametype)
+    INIT(Deathmatch)
+    {
+        this.gametype_init(this, _("Deathmatch"),"dm","g_dm",false,"","timelimit=20 pointlimit=30 leadlimit=0",_("Score as many frags as you can"));
+    }
+    METHOD(Deathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        return true;
+    }
+ENDCLASS(Deathmatch)
+REGISTER_GAMETYPE(DEATHMATCH, NEW(Deathmatch));
+
+CLASS(LastManStanding, Gametype)
+    INIT(LastManStanding)
+    {
+        this.gametype_init(this, _("Last Man Standing"),"lms","g_lms",false,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
+    }
+    METHOD(LastManStanding, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
         return true;
     }
-    switch (k) {
-        case "qualifying_timelimit":
-            cvar_set("g_race_qualifying_timelimit", v);
+ENDCLASS(LastManStanding)
+REGISTER_GAMETYPE(LMS, NEW(LastManStanding));
+
+#ifdef CSQC
+void HUD_Mod_Race(vector pos, vector mySize);
+#endif
+CLASS(Race, Gametype)
+    INIT(Race)
+    {
+        this.gametype_init(this, _("Race"),"rc","g_race",false,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"));
+    }
+    METHOD(Race, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit"));
             return true;
+        }
+        switch (k) {
+            case "qualifying_timelimit":
+                cvar_set("g_race_qualifying_timelimit", v);
+                return true;
+        }
+        return false;
+    }
+    METHOD(Race, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "trigger_race_checkpoint")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
     }
-}
+    METHOD(Race, m_isTwoBaseMode, bool())
+    {
+        return true;
+    }
+#ifdef CSQC
+    ATTRIB(Race, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race);
+#endif
+ENDCLASS(Race)
+REGISTER_GAMETYPE(RACE, NEW(Race));
 #define g_race IS_GAMETYPE(RACE)
 
-REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"cloaked","timelimit=20",_("Race for fastest time."));
+CLASS(RaceCTS, Gametype)
+    INIT(RaceCTS)
+    {
+        this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,"cloaked","timelimit=20",_("Race for fastest time."));
+    }
+    METHOD(RaceCTS, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "target_startTimer")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+    METHOD(RaceCTS, m_setTeams, void(string sa))
+    {
+        // this is the skill of the map
+        // not parsed by anything yet
+        // for map databases
+        //  cvar_set("fraglimit", sa);
+    }
+#ifdef CSQC
+    ATTRIB(RaceCTS, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race);
+#endif
+ENDCLASS(RaceCTS)
+REGISTER_GAMETYPE(CTS, NEW(RaceCTS));
 #define g_cts IS_GAMETYPE(CTS)
 
-REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,true,"","timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team"))
-{
-    if (!k) {
-        cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams"));
-        return true;
+CLASS(TeamDeathmatch, Gametype)
+    INIT(TeamDeathmatch)
+    {
+        this.gametype_init(this, _("Team Deathmatch"),"tdm","g_tdm",true,"","timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team"));
+    }
+    METHOD(TeamDeathmatch, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams"));
+            return true;
+        }
+        switch (k) {
+            case "teams":
+                cvar_set("g_tdm_teams", v);
+                return true;
+        }
+        return false;
     }
-    switch (k) {
-        case "teams":
-            cvar_set("g_tdm_teams", v);
+    METHOD(TeamDeathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        if(spawnpoints >= 8 && diameter > 4096)
             return true;
+        return false;
+    }
+    METHOD(TeamDeathmatch, m_setTeams, void(string sa))
+    {
+        cvar_set("g_tdm_teams", sa);
     }
-}
+ENDCLASS(TeamDeathmatch)
+REGISTER_GAMETYPE(TEAM_DEATHMATCH, NEW(TeamDeathmatch));
 #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
 
-REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team"));
+#ifdef CSQC
+void HUD_Mod_CTF(vector pos, vector mySize);
+void HUD_Mod_CTF_Reset();
+#endif
+CLASS(CaptureTheFlag, Gametype)
+    INIT(CaptureTheFlag)
+    {
+        this.gametype_init(this, _("Capture the Flag"),"ctf","g_ctf",true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team"));
+    }
+    METHOD(CaptureTheFlag, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "item_flag_team2" || v == "team_CTF_blueflag")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+    METHOD(CaptureTheFlag, m_isTwoBaseMode, bool())
+    {
+        return true;
+    }
+    METHOD(CaptureTheFlag, m_setTeams, void(string sa))
+    {
+        cvar_set("fraglimit", sa);
+    }
+#ifdef CSQC
+    ATTRIB(CaptureTheFlag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CTF);
+    ATTRIB(CaptureTheFlag, m_modicons_reset, void(), HUD_Mod_CTF_Reset);
+#endif
+ENDCLASS(CaptureTheFlag)
+REGISTER_GAMETYPE(CTF, NEW(CaptureTheFlag));
 #define g_ctf IS_GAMETYPE(CTF)
 
-REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round"))
-{
-    if (!k) {
-        cvar_set("g_ca_teams", cvar_defstring("g_ca_teams"));
-        return true;
+#ifdef CSQC
+void HUD_Mod_CA(vector pos, vector mySize);
+#endif
+CLASS(ClanArena, Gametype)
+    INIT(ClanArena)
+    {
+        this.gametype_init(this, _("Clan Arena"),"ca","g_ca",true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round"));
     }
-    switch (k) {
-        case "teams":
-            cvar_set("g_ca_teams", v);
+    METHOD(ClanArena, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_ca_teams", cvar_defstring("g_ca_teams"));
             return true;
+        }
+        switch (k) {
+            case "teams":
+                cvar_set("g_ca_teams", v);
+                return true;
+        }
+        return false;
     }
-}
+    METHOD(ClanArena, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        if(spawnpoints >= 8 && diameter > 4096)
+            return true;
+        return false;
+    }
+    METHOD(ClanArena, m_setTeams, void(string sa))
+    {
+        cvar_set("g_ca_teams", sa);
+    }
+#ifdef CSQC
+    ATTRIB(ClanArena, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA);
+#endif
+ENDCLASS(ClanArena)
+REGISTER_GAMETYPE(CA, NEW(ClanArena));
 #define g_ca IS_GAMETYPE(CA)
 
-REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win"))
-{
-    if (!k) {
-        cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams"));
-        return true;
+#ifdef CSQC
+void HUD_Mod_Dom(vector pos, vector mySize);
+#endif
+CLASS(Domination, Gametype)
+    INIT(Domination)
+    {
+        this.gametype_init(this, _("Domination"),"dom","g_domination",true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win"));
     }
-    switch (k) {
-        case "teams":
-            cvar_set("g_domination_default_teams", v);
+    METHOD(Domination, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams"));
             return true;
+        }
+        switch (k) {
+            case "teams":
+                cvar_set("g_domination_default_teams", v);
+                return true;
+        }
+        return false;
     }
-}
+    METHOD(Domination, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "dom_controlpoint")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+#ifdef CSQC
+    ATTRIB(Domination, m_modicons, void(vector pos, vector mySize), HUD_Mod_Dom);
+#endif
+ENDCLASS(Domination)
+REGISTER_GAMETYPE(DOMINATION, NEW(Domination));
 
-REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"))
-{
-    if (!k) {
-       cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams"));
-       return true;
+#ifdef CSQC
+void HUD_Mod_KH(vector pos, vector mySize);
+#endif
+CLASS(KeyHunt, Gametype)
+    INIT(KeyHunt)
+    {
+        this.gametype_init(this, _("Key Hunt"),"kh","g_keyhunt",true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"));
     }
-    switch (k) {
-        case "teams":
-            cvar_set("g_keyhunt_teams", v);
+    METHOD(KeyHunt, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams"));
             return true;
+        }
+        switch (k) {
+            case "teams":
+                cvar_set("g_keyhunt_teams", v);
+                return true;
+        }
+        return false;
+    }
+    METHOD(KeyHunt, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        if(spawnpoints >= 12 && diameter > 5120)
+            return true;
+        return false;
+    }
+    METHOD(KeyHunt, m_setTeams, void(string sa))
+    {
+        cvar_set("g_keyhunt_teams", sa);
     }
-}
+#ifdef CSQC
+    ATTRIB(KeyHunt, m_modicons, void(vector pos, vector mySize), HUD_Mod_KH);
+#endif
+ENDCLASS(KeyHunt)
+REGISTER_GAMETYPE(KEYHUNT, NEW(KeyHunt));
 
-REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,true,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
+CLASS(Assault, Gametype)
+    INIT(Assault)
+    {
+        this.gametype_init(this, _("Assault"),"as","g_assault",true,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
+    }
+    METHOD(Assault, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "target_assault_roundend")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+    METHOD(Assault, m_isTwoBaseMode, bool())
+    {
+        return true;
+    }
+ENDCLASS(Assault)
+REGISTER_GAMETYPE(ASSAULT, NEW(Assault));
 #define g_assault IS_GAMETYPE(ASSAULT)
 
-REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,true,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
+CLASS(Onslaught, Gametype)
+    INIT(Onslaught)
+    {
+        this.gametype_init(this, _("Onslaught"),"ons","g_onslaught",true,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
+    }
+    METHOD(Onslaught, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "onslaught_generator")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+ENDCLASS(Onslaught)
+REGISTER_GAMETYPE(ONSLAUGHT, NEW(Onslaught));
 
-REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean"));
+#ifdef CSQC
+void HUD_Mod_NexBall(vector pos, vector mySize);
+#endif
+CLASS(NexBall, Gametype)
+    INIT(NexBall)
+    {
+        this.gametype_init(this, _("Nexball"),"nb","g_nexball",true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean"));
+    }
+    METHOD(NexBall, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+    METHOD(NexBall, m_isTwoBaseMode, bool())
+    {
+        return true;
+    }
+#ifdef CSQC
+    ATTRIB(NexBall, m_modicons, void(vector pos, vector mySize), HUD_Mod_NexBall);
+#endif
+ENDCLASS(NexBall)
+REGISTER_GAMETYPE(NEXBALL, NEW(NexBall));
 #define g_nexball IS_GAMETYPE(NEXBALL)
 
-REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them, freeze the most enemies to win"))
-{
-    if (!k) {
-        cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams"));
-        return true;
+CLASS(FreezeTag, Gametype)
+    INIT(FreezeTag)
+    {
+        this.gametype_init(this, _("Freeze Tag"),"ft","g_freezetag",true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them, freeze the most enemies to win"));
+    }
+    METHOD(FreezeTag, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams"));
+            return true;
+        }
+        switch (k) {
+            case "teams":
+                cvar_set("g_freezetag_teams", v);
+                return true;
+        }
+        return false;
     }
-    switch (k) {
-        case "teams":
-            cvar_set("g_freezetag_teams", v);
+    METHOD(FreezeTag, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        if(spawnpoints >= 8 && diameter > 4096)
             return true;
+        return false;
+    }
+    METHOD(FreezeTag, m_setTeams, void(string sa))
+    {
+        cvar_set("g_freezetag_teams", sa);
     }
-}
+#ifdef CSQC
+    ATTRIB(FreezeTag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA);
+#endif
+ENDCLASS(FreezeTag)
+REGISTER_GAMETYPE(FREEZETAG, NEW(FreezeTag));
 #define g_freezetag IS_GAMETYPE(FREEZETAG)
 
-REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,true,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
+#ifdef CSQC
+void HUD_Mod_Keepaway(vector pos, vector mySize);
+#endif
+CLASS(Keepaway, Gametype)
+    INIT(Keepaway)
+    {
+        this.gametype_init(this, _("Keepaway"),"ka","g_keepaway",false,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
+    }
+    METHOD(Keepaway, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        return true;
+    }
+#ifdef CSQC
+    ATTRIB(Keepaway, m_modicons, void(vector pos, vector mySize), HUD_Mod_Keepaway);
+#endif
+ENDCLASS(Keepaway)
+REGISTER_GAMETYPE(KEEPAWAY, NEW(Keepaway));
 
-REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,false,"","pointlimit=50 teams=0",_("Survive against waves of monsters"))
-{
-    switch (k) {
-        case "teams":
-            cvar_set("g_invasion_teams", v);
-            return true;
+CLASS(Invasion, Gametype)
+    INIT(Invasion)
+    {
+        this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,"","pointlimit=50 teams=0",_("Survive against waves of monsters"));
     }
-}
+    METHOD(Invasion, m_parse_mapinfo, bool(string k, string v))
+    {
+        switch (k) {
+            case "teams":
+                cvar_set("g_invasion_teams", v);
+                return true;
+        }
+        return false;
+    }
+    METHOD(Invasion, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "invasion_spawnpoint")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+ENDCLASS(Invasion)
+REGISTER_GAMETYPE(INVASION, NEW(Invasion));
 
 const int MAPINFO_FEATURE_WEAPONS       = 1; // not defined for instagib-only maps
 const int MAPINFO_FEATURE_VEHICLES      = 2;
@@ -197,30 +489,17 @@ const int MAPINFO_FLAG_NOAUTOMAPLIST    = 8; // do not include when automaticall
 
 float MapInfo_count;
 
-// info about a map that MapInfo loads
-string MapInfo_Map_bspname;
-string MapInfo_Map_title;
-string MapInfo_Map_titlestring; // either bspname: title or just title, depending on whether bspname is redundant
-string MapInfo_Map_description;
-string MapInfo_Map_author;
-string MapInfo_Map_clientstuff; // not in cache, only for map load
-string MapInfo_Map_fog; // not in cache, only for map load
-int MapInfo_Map_supportedGametypes;
-int MapInfo_Map_supportedFeatures;
-int MapInfo_Map_flags;
-vector MapInfo_Map_mins; // these are '0 0 0' if not supported!
-vector MapInfo_Map_maxs; // these are '0 0 0' if not specified!
-
 // load MapInfo_count; generate mapinfo for maps that miss them, and clear the
 // cache; you need to call MapInfo_FilterGametype afterwards!
 void MapInfo_Enumerate();
 
 // filter the info by game type mask (updates MapInfo_count)
 float MapInfo_progress;
-float MapInfo_FilterGametype(float gametype, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
+float MapInfo_FilterGametype(Gametype gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
+float _MapInfo_FilterGametype(int gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
 void MapInfo_FilterString(string sf); // filter _MapInfo_filtered (created by MapInfo_FilterGametype) with keyword
 int MapInfo_CurrentFeatures(); // retrieves currently required features from cvars
-int MapInfo_CurrentGametype(); // retrieves current gametype from cvars
+Gametype MapInfo_CurrentGametype(); // retrieves current gametype from cvars
 int MapInfo_ForbiddenFlags(); // retrieves current flags from cvars
 int MapInfo_RequiredFlags(); // retrieves current flags from cvars
 
@@ -229,7 +508,7 @@ float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
 string MapInfo_BSPName_ByID(float i);
 
 // load info about a map by name into the MapInfo_Map_* globals
-float MapInfo_Get_ByName(string s, float allowGenerate, float gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file
+int MapInfo_Get_ByName(string s, float allowGenerate, Gametype gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file
 
 // look for a map by a prefix, returns the actual map name on success, string_null on failure or ambigous match
 string MapInfo_FindName_match; // the name of the map that was found
@@ -242,23 +521,22 @@ float MapInfo_CheckMap(string s); // returns 0 if the map can't be played with t
 void MapInfo_LoadMap(string s, float reinit);
 
 // list all maps for the current game type
-string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden);
+string MapInfo_ListAllowedMaps(Gametype type, float pFlagsRequired, float pFlagsForbidden);
 // list all allowed maps (for any game type)
 string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
 
 // gets a gametype from a string
-string _MapInfo_GetDefaultEx(float t);
-float _MapInfo_GetTeamPlayBool(float t);
-Gametype MapInfo_Type(int t);
-float MapInfo_Type_FromString(string t);
-string MapInfo_Type_Description(float t);
-string MapInfo_Type_ToString(float t);
-string MapInfo_Type_ToText(float t);
-void MapInfo_SwitchGameType(int t);
+string _MapInfo_GetDefaultEx(Gametype t);
+float _MapInfo_GetTeamPlayBool(Gametype t);
+Gametype MapInfo_Type_FromString(string t);
+string MapInfo_Type_Description(Gametype t);
+string MapInfo_Type_ToString(Gametype t);
+string MapInfo_Type_ToText(Gametype t);
+void MapInfo_SwitchGameType(Gametype t);
 
 // to be called from worldspawn to set up cvars
 void MapInfo_LoadMapSettings(string s);
-float MapInfo_LoadedGametype; // game type that was active during map load
+Gametype MapInfo_LoadedGametype; // game type that was active during map load
 
 void MapInfo_Cache_Destroy(); // disable caching
 void MapInfo_Cache_Create(); // enable caching
@@ -270,4 +548,3 @@ void MapInfo_Shutdown(); // call this in the shutdown handler
 
 #define MAPINFO_SETTEMP_ACL_USER cvar_string("g_mapinfo_settemp_acl")
 #define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* +*"
-#endif
index 43ad69de595d8cace9a34f16d672eeb15baa0815..4171f475cd747c5c7bea37c8e026615dbbea11b1 100644 (file)
@@ -1,5 +1,7 @@
 // generated file; do not modify
-#include <common/minigames/cl_minigames_hud.qc>
+#ifdef CSQC
+    #include <common/minigames/cl_minigames_hud.qc>
+#endif
 #include <common/minigames/minigames.qc>
 #ifdef CSQC
     #include <common/minigames/cl_minigames.qc>
@@ -7,3 +9,5 @@
 #ifdef SVQC
     #include <common/minigames/sv_minigames.qc>
 #endif
+
+#include <common/minigames/minigame/_mod.inc>
index 36f5de2f157fad15c25f288f95a3529363ca686a..e0daf8f6775f01fa6c363b300b55f0c547f07987 100644 (file)
@@ -1,3 +1,13 @@
 // generated file; do not modify
-#include <common/minigames/cl_minigames_hud.qh>
+#ifdef CSQC
+    #include <common/minigames/cl_minigames_hud.qh>
+#endif
 #include <common/minigames/minigames.qh>
+#ifdef CSQC
+    #include <common/minigames/cl_minigames.qh>
+#endif
+#ifdef SVQC
+    #include <common/minigames/sv_minigames.qh>
+#endif
+
+#include <common/minigames/minigame/_mod.qh>
index 3d3ef42dff444d3d57dc273cf451204602ff979c..42f8c514e9bf0166a53bbfdc7c0cad5ccec2f5d9 100644 (file)
@@ -74,8 +74,8 @@ MINIGAME_SIMPLELINKED_ENTITIES
 
 void minigame_autoclean_entity(entity e)
 {
-       LOG_DEBUG("CL Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")\n");
-       remove(e);
+       LOG_DEBUG("CL Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")");
+       delete(e);
 }
 
 void HUD_MinigameMenu_CurrentButton();
@@ -121,7 +121,7 @@ void activate_minigame(entity minigame)
 
        if ( !minigame.descriptor || minigame.classname != "minigame" )
        {
-               LOG_TRACE("Trying to activate unregistered minigame ",minigame.netname," in client\n");
+               LOG_TRACE("Trying to activate unregistered minigame ",minigame.netname," in client");
                return;
        }
 
@@ -174,7 +174,7 @@ void minigame_read_owner(entity this)
                this.owner = find(this.owner,netname,owner_name);
        while ( this.owner && this.owner.classname != "minigame" );
        if ( !this.owner )
-               LOG_TRACE("Got a minigame entity without a minigame!\n");
+               LOG_TRACE("Got a minigame entity without a minigame!");
 }
 NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
 {
@@ -196,7 +196,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
                        this.entremove = minigame_entremove;
                        this.descriptor = minigame_get_descriptor(ReadString_Raw());
                        if ( !this.descriptor )
-                               LOG_TRACE("Got a minigame without a client-side descriptor!\n");
+                               LOG_TRACE("Got a minigame without a client-side descriptor!");
                        else
                                this.minigame_event = this.descriptor.minigame_event;
                }
@@ -212,7 +212,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
                        minigame_read_owner(this);
                        float ent = ReadLong();
                        this.minigame_playerslot = ent;
-                       LOG_DEBUG("Player: ",entcs_GetName(ent-1),"\n");
+                       LOG_DEBUG("Player: ",entcs_GetName(ent-1));
 
                        activate = (ent == player_localnum+1 && this.owner && this.owner != active_minigame);
 
@@ -226,6 +226,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
                {
                        minigame_self = this;
                        activate_minigame(this.owner);
+                       minigame_self = this; // set it again (needed before, but may also be reset)
                }
        }
        MINIGAME_SIMPLELINKED_ENTITIES
@@ -237,7 +238,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
        {
                LOG_DEBUG("CL Reading entity: ",ftos(etof(this)),
                        " classname:",this.classname," enttype:",ftos(this.enttype) );
-               LOG_DEBUG(" sf:",ftos(sf)," netname:",this.netname,"\n\n");
+               LOG_DEBUG(" sf:",ftos(sf)," netname:",this.netname);
        }
        return true;
 }
index cc24d4bf0c69256717525a81ced1e28360ac5941..a0f6195d1232f6ec6ca994d4514de840b099c58a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CL_MINIGAMES_H
-#define CL_MINIGAMES_H
+#pragma once
 
 // Get a square in the center of the avaliable area
 // \note macro to pass by reference pos and mySize
@@ -85,12 +84,6 @@ entity active_minigame;
 // minigame_player representing this client
 entity minigame_self;
 
-// Whethere there's an active minigame
-float minigame_isactive()
-{
-       return active_minigame != NULL;
-}
-
 // Execute a minigame command
 #define minigame_cmd(...) minigame_cmd_workaround(0,__VA_ARGS__)
 void minigame_cmd_workaround(float dummy, string...cmdargc);
@@ -101,7 +94,7 @@ void minigame_prompt();
 
 float HUD_MinigameMenu_IsOpened();
 void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger);
-float HUD_Minigame_Showpanels();
+
 // Adds a game-specific entry to the menu
 void HUD_MinigameMenu_CustomEntry(entity parent, string message, string event_arg);
 
@@ -128,5 +121,3 @@ REGISTRY_CHECK(Minigames)
                this.minigame_event = name##_client_event; \
     } \
     REGISTER_INIT(MINIGAME_##name)
-
-#endif
index f64ed44c84e800b919c901c501c134dbb3df4f8a..1a6d4ff52272e39e4204c9e892545e6688536f63 100644 (file)
@@ -35,7 +35,7 @@ void HUD_MinigameBoard ()
        if ( !hud_minigame )
                return;
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
 
        vector pos, mySize;
@@ -61,7 +61,7 @@ void HUD_MinigameStatus ()
        if ( !hud_minigame )
                return;
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
 
        vector pos, mySize;
@@ -143,7 +143,7 @@ void HUD_MinigameMenu_EraseEntry ( entity e )
        if ( HUD_MinigameMenu_activeitem == e )
                HUD_MinigameMenu_activeitem = NULL;
 
-       remove(e);
+       delete(e);
 }
 
 // Minigame menu options: create entry
@@ -193,7 +193,7 @@ bool HUD_MinigameMenu_Click_ExpandCollapse(entity this)
                        if ( e.flags & 2 )
                                HUD_MinigameMenu_Click(e);
                        this.list_next = e.list_next;
-                       remove(e);
+                       delete(e);
                }
                if ( this.list_next )
                        this.list_next.list_prev = this;
@@ -351,7 +351,7 @@ void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger)
                for ( e = HUD_MinigameMenu_entries; e != NULL; e = p )
                {
                        p = e.list_next;
-                       remove(e);
+                       delete(e);
                }
                HUD_MinigameMenu_entries = NULL;
                HUD_MinigameMenu_last_entry = NULL;
@@ -429,7 +429,7 @@ void HUD_MinigameMenu_MouseInput()
 {
        panel = HUD_PANEL(MINIGAME_MENU);
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        if(panel_bg_padding)
        {
@@ -475,10 +475,10 @@ void HUD_MinigameMenu ()
        if ( !HUD_MinigameMenu_IsOpened() )
                return;
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
        HUD_Scale_Disable();
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
 
        if(panel_bg_padding)
        {
@@ -545,7 +545,7 @@ void HUD_MinigameHelp()
        if ( !help_message )
                return;
 
-       HUD_Panel_UpdateCvars();
+       HUD_Panel_LoadCvars();
 
 
        vector pos, mySize;
@@ -575,7 +575,7 @@ float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary
        {
                mousepos_x = nPrimary;
                mousepos_y = nSecondary;
-               if ( minigame_isactive() && HUD_mouse_over(HUD_PANEL(MINIGAME_BOARD)) )
+               if ( active_minigame && HUD_mouse_over(HUD_PANEL(MINIGAME_BOARD)) )
                        active_minigame.minigame_event(active_minigame,"mouse_moved",mousepos);
                return true;
 
@@ -598,16 +598,16 @@ float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary
                }
 
                // allow some binds
-               string con_keys;
-               con_keys = findkeysforcommand("toggleconsole", 0);
+               string con_keys = findkeysforcommand("toggleconsole", 0);
                int keys = tokenize(con_keys); // findkeysforcommand returns data for this
-               for (int i = 0; i < keys; ++i)
+               int i;
+               for (i = 0; i < keys; ++i)
                {
                        if(nPrimary == stof(argv(i)))
                                return false;
                }
 
-               if ( minigame_isactive() && ( bInputType == 0 || bInputType == 1 ) )
+               if ( active_minigame && ( bInputType == 0 || bInputType == 1 ) )
                {
                        string device = "";
                        string action = bInputType == 0 ? "pressed" : "released";
@@ -682,21 +682,11 @@ void HUD_Minigame_Mouse()
        if( !HUD_MinigameMenu_IsOpened() || autocvar__hud_configure || mv_active )
                return;
 
-       if(!autocvar_hud_cursormode)
-       {
-               mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
-
-               mousepos_x = bound(0, mousepos_x, vid_conwidth);
-               mousepos_y = bound(0, mousepos_y, vid_conheight);
-       }
+       if (!autocvar_hud_cursormode)
+               update_mousepos();
 
        if ( HUD_MinigameMenu_IsOpened() && HUD_mouse_over(HUD_PANEL(MINIGAME_MENU)) )
                HUD_MinigameMenu_MouseInput();
 
        draw_cursor_normal(mousepos, '1 1 1', panel_fg_alpha);
 }
-
-bool HUD_Minigame_Showpanels()
-{
-       return (HUD_MinigameMenu_IsOpened() && minigame_isactive());
-}
index f170c590dea2037745df88de968f9c5ca75ad092..ef44ea025f5633635496415635e9ce6665293075 100644 (file)
@@ -1,7 +1,4 @@
-#ifndef CL_MINIGAMES_HUD_H
-#define CL_MINIGAMES_HUD_H
+#pragma once
 
 float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary);
 void HUD_Minigame_Mouse();
-
-#endif
index db25c2959e02470d85a6b5d3b23b022f24488d6a..09fae55d80ef2f08ab17d8097633646b1a641069 100644 (file)
@@ -1,3 +1,4 @@
+#include "bd.qh"
 REGISTER_MINIGAME(bd, "Bulldozer");
 
 const int BD_TURN_MOVE  = 0x0100; // player must move the bulldozer
@@ -16,7 +17,7 @@ const int BD_TILE_SIZE = 20;
 
 const int BD_TEAMS = 1;
 
-.vector bd_dir;
+.int bd_dir;
 
 .int bd_moves;
 
@@ -41,7 +42,14 @@ const int BD_TILE_BRICK7 = 10;
 const int BD_TILE_BRICK8 = 11;
 const int BD_TILE_LAST = 11;
 
+const int BD_DIR_UP = 0;
+const int BD_DIR_DN = 1;
+const int BD_DIR_LF = 2;
+const int BD_DIR_RT = 3;
+
+#ifdef SVQC
 string autocvar_sv_minigames_bulldozer_startlevel = "level1";
+#endif
 
 // find same game piece given its tile name
 entity bd_find_piece(entity minig, string tile, bool check_target)
@@ -91,6 +99,44 @@ void bd_check_winner(entity minig)
        }
 }
 
+vector bd_get_dir(int bdir)
+{
+       switch(bdir)
+       {
+               case BD_DIR_UP: return '0 1 0'; // up
+               default:
+               case BD_DIR_DN: return '0 -1 0'; // down
+               case BD_DIR_LF: return '-1 0 0'; // left
+               case BD_DIR_RT: return '1 0 0'; // right
+       }
+}
+
+string bd_get_dir_name(int bdir)
+{
+       switch(bdir)
+       {
+               case BD_DIR_UP: return "u"; // up
+               default:
+               case BD_DIR_DN: return "d"; // down
+               case BD_DIR_LF: return "l"; // left
+               case BD_DIR_RT: return "r"; // right
+       }
+}
+
+int bd_dir_fromname(string bdir)
+{
+       if(bdir == "up" || bdir == "u")
+               return BD_DIR_UP; // up
+       if(bdir == "down" || bdir == "dn" || bdir == "d")
+               return BD_DIR_DN; /// down
+       if(bdir == "left" || bdir == "lt" || bdir == "l")
+               return BD_DIR_LF; // left
+       if(bdir == "right" || bdir == "rt" || bdir == "r")
+               return BD_DIR_RT; // right
+
+       return BD_DIR_DN; // down
+}
+
 bool bd_canfill(int ttype)
 {
        switch(ttype)
@@ -110,14 +156,16 @@ bool bd_canfill(int ttype)
 
 bool bd_move_dozer(entity minigame, entity dozer)
 {
-       if(!dozer.bd_dir_x && !dozer.bd_dir_y)
-               return false; // nope!
+       //if(!dozer.bd_dir)
+               //return false; // nope!
 
        int myx = minigame_tile_letter(dozer.netname);
        int myy = minigame_tile_number(dozer.netname);
 
-       myx += dozer.bd_dir_x;
-       myy += dozer.bd_dir_y;
+       vector dir = bd_get_dir(dozer.bd_dir);
+
+       myx += dir.x;
+       myy += dir.y;
 
        string newpos = minigame_tile_buildname(myx, myy);
        entity hit = bd_find_piece(minigame, newpos, false);
@@ -143,8 +191,8 @@ bool bd_move_dozer(entity minigame, entity dozer)
                        int tx = minigame_tile_letter(hit.netname);
                        int ty = minigame_tile_number(hit.netname);
 
-                       tx += dozer.bd_dir_x;
-                       ty += dozer.bd_dir_y;
+                       tx += dir.x;
+                       ty += dir.y;
 
                        testpos = minigame_tile_buildname(tx, ty);
                        entity testhit = bd_find_piece(minigame, testpos, false);
@@ -181,24 +229,15 @@ void bd_move(entity minigame, entity player, string dir)
                                return; // should not happen... TODO: end match?
                        }
 
-                       int dxs = 0, dys = 0;
                        string thedir = strtolower(dir);
-                       if(thedir == "up" || thedir == "u") { dxs = 0; dys = 1; }
-                       if(thedir == "down" || thedir == "dn" || thedir == "d") { dxs = 0; dys = -1; }
-                       if(thedir == "left" || thedir == "lt" || thedir == "l") { dxs = -1; dys = 0; }
-                       if(thedir == "right" || thedir == "rt" || thedir == "r") { dxs = 1; dys = 0; }
-
-                       int dx = bound(-1, dxs, 1);
-                       int dy = bound(-1, dys, 1);
+                       int bdir = bd_dir_fromname(thedir);
 
                        int moved = 0;
                        entity e = NULL;
                        while ( ( e = findentity(e,owner,minigame) ) )
                                if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER )
                                {
-                                       e.bd_dir_x = dx;
-                                       e.bd_dir_y = dy;
-                                       e.bd_dir_z = 0;
+                                       e.bd_dir = bdir;
 
                                        if(bd_move_dozer(minigame, e))
                                                ++moved;
@@ -230,19 +269,10 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st
 
                        if(found_piece.bd_tiletype == BD_TILE_DOZER && thedir != "")
                        {
-                               int dxs = 0, dys = 0;
                                string newdir = strtolower(thedir);
-                               if(newdir == "up" || newdir == "u") { dxs = 0; dys = 1; }
-                               if(newdir == "down" || newdir == "dn" || newdir == "d") { dxs = 0; dys = -1; }
-                               if(newdir == "left" || newdir == "lt" || newdir == "l") { dxs = -1; dys = 0; }
-                               if(newdir == "right" || newdir == "rt" || newdir == "r") { dxs = 1; dys = 0; }
-
-                               int dx = bound(-1, dxs, 1);
-                               int dy = bound(-1, dys, 1);
+                               int bdir = bd_dir_fromname(newdir);
 
-                               found_piece.bd_dir_x = dx;
-                               found_piece.bd_dir_y = dy;
-                               found_piece.bd_dir_z = 0;
+                               found_piece.bd_dir = bdir;
                                minigame_server_sendflags(found_piece,MINIG_SF_UPDATE); // update anyway
                                return;
                        }
@@ -259,7 +289,7 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st
                                        return; // how?!
 
                                if(piece.netname) { strunzone(piece.netname); }
-                               remove(piece);
+                               delete(piece);
                                minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
                                return;
                        }
@@ -268,7 +298,7 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st
                        piece.team = 1;
                        piece.netname = strzone(pos);
                        piece.bd_tiletype = thetile;
-                       piece.bd_dir = '0 -1 0';
+                       piece.bd_dir = 0;
                        minigame_server_sendflags(piece,MINIG_SF_UPDATE);
 
                        minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
@@ -313,12 +343,12 @@ void bd_unfill_recurse(entity minigame, entity player, int thetype, int letter,
        if(targ && thetype == targ.bd_tiletype)
        {
                if(targ.netname) { strunzone(targ.netname); }
-               remove(targ);
+               delete(targ);
        }
        else if(piece && thetype == piece.bd_tiletype)
        {
                if(piece.netname) { strunzone(piece.netname); }
-               remove(piece);
+               delete(piece);
        }
        else return;
 
@@ -393,7 +423,7 @@ void bd_setup_pieces(entity minigame)
                if(e.classname == "minigame_board_piece")
                {
                        if(e.netname) { strunzone(e.netname); }
-                       remove(e);
+                       delete(e);
                }
 
        bd_load_level(minigame);
@@ -464,7 +494,7 @@ string bd_save_piece(entity minigame, entity e)
 
        bd_string = strcat(bd_string, "\"", e.netname, "\" ");
        bd_string = strcat(bd_string, ftos(e.bd_tiletype), " ");
-       bd_string = strcat(bd_string, sprintf("\"%.9v\"", e.bd_dir));
+       bd_string = strcat(bd_string, ftos(e.bd_dir));
 
        return bd_string;
 }
@@ -477,6 +507,16 @@ void bd_set_nextlevel(entity minigame, string s)
        minigame.bd_nextlevel = strzone(argv(2));
 }
 
+int bd_fix_dir(vector dir)
+{
+       if(dir.x == 0 && dir.y == 1) { return BD_DIR_UP; } // up
+       if(dir.x == 0 && dir.y == -1) { return BD_DIR_DN; } // down
+       if(dir.x == -1 && dir.y == 0) { return BD_DIR_LF; } // left
+       if(dir.x == 1 && dir.y == 0) { return BD_DIR_RT; } // right
+
+       return BD_DIR_DN; // down if all else fails
+}
+
 entity bd_load_piece(entity minigame, string s)
 {
        // separate pieces between the ; symbols
@@ -486,12 +526,12 @@ entity bd_load_piece(entity minigame, string s)
 
        entity e = msle_spawn(minigame,"minigame_board_piece");
        e.team = 1;
-       e.bd_dir = '0 -1 0';
+       e.bd_dir = 0;
 
        int argv_num = 0;
        e.netname = strzone(argv(argv_num)); ++argv_num;
        e.bd_tiletype = stof(argv(argv_num)); ++argv_num;
-       e.bd_dir = stov(argv(argv_num)); ++argv_num;
+       e.bd_dir = stoi(argv(argv_num)); ++argv_num;
 
        minigame_server_sendflags(e,MINIG_SF_ALL);
 
@@ -631,7 +671,7 @@ int bd_server_event(entity minigame, string event, ...)
                        if(e.classname == "minigame_board_piece")
                        {
                                if(e.netname) { strunzone(e.netname); }
-                               remove(e);
+                               delete(e);
                        }
 
                        if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); }
@@ -686,12 +726,7 @@ int bd_server_event(entity minigame, string event, ...)
 
                                WriteByte(MSG_ENTITY,sent.bd_tiletype);
 
-                               int dx = sent.bd_dir_x;
-                               int dy = sent.bd_dir_y;
-                               if(dx == -1) dx = 2;
-                               if(dy == -1) dy = 2;
-                               WriteByte(MSG_ENTITY,dx);
-                               WriteByte(MSG_ENTITY,dy);
+                               WriteByte(MSG_ENTITY,sent.bd_dir);
                        }
                        else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES))
                                WriteShort(MSG_ENTITY,sent.bd_moves);
@@ -792,12 +827,17 @@ void bd_hud_board(vector pos, vector mySize)
                        tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
                        tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
 
-                       vector thedir = e.bd_dir;
+                       int bdir = e.bd_dir;
                        float theang = 0;
 
-                       if(thedir_y == -1) { theang = M_PI; }
-                       if(thedir_x == 1) { theang = M_PI/2; }
-                       if(thedir_x == -1) { theang = M_PI*3/2; }
+                       switch(bdir)
+                       {
+                               case BD_DIR_UP: theang = 0; break;
+                               default:
+                               case BD_DIR_DN: theang = M_PI; break;
+                               case BD_DIR_LF: theang = M_PI * 3 / 2; break;
+                               case BD_DIR_RT: theang = M_PI / 2; break; 
+                       }
 
                        drawrotpic(tile_pos, theang, minigame_texture("bd/dozer"),
                                                tile_size, tile_size/2, '1 1 1',
@@ -854,7 +894,7 @@ void bd_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void bd_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
@@ -985,14 +1025,15 @@ bool bd_change_dozer_angle(entity minigame)
        if(!dozer || dozer.bd_tiletype != BD_TILE_DOZER)
                return false;
 
-       string thedir = "";
-       vector dir = dozer.bd_dir;
-       if(dir.x == 0 && dir.y == 0) { thedir = "r"; }
-
-       if(dir.x == 0 && dir.y == 1) { thedir = "r"; }
-       if(dir.x == 0 && dir.y ==-1) { thedir = "l"; }
-       if(dir.x ==-1 && dir.y == 0) { thedir = "u"; }
-       if(dir.x == 1 && dir.y == 0) { thedir = "d"; }
+       switch(dozer.bd_dir)
+       {
+               case BD_DIR_UP: dozer.bd_dir = BD_DIR_LF; break; // up -> left
+               default:
+               case BD_DIR_DN: dozer.bd_dir = BD_DIR_RT; break; // down -> right
+               case BD_DIR_LF: dozer.bd_dir = BD_DIR_DN; break; // left -> down
+               case BD_DIR_RT: dozer.bd_dir = BD_DIR_UP; break; // right -> up
+       }
+       string thedir = bd_get_dir_name(dozer.bd_dir);
 
        bd_editor_make_move(minigame, thedir);
        return true;
@@ -1128,15 +1169,7 @@ int bd_client_event(entity minigame, string event, ...)
 
                                        sent.bd_tiletype = ReadByte();
 
-                                       int dx = ReadByte();
-                                       int dy = ReadByte();
-
-                                       if(dx == 2) dx = -1;
-                                       if(dy == 2) dy = -1;
-
-                                       sent.bd_dir_x = dx;
-                                       sent.bd_dir_y = dy;
-                                       sent.bd_dir_z = 0;
+                                       sent.bd_dir = ReadByte();
                                }
                        }
                        else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES))
diff --git a/qcsrc/common/minigames/minigame/bd.qh b/qcsrc/common/minigames/minigame/bd.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 3c2beacb6fc20c3f1e0c87ecfbb93cab7ff41e39..2a6cb3d475ded6748f10715c5e5e9118e9ce6675 100644 (file)
@@ -1,3 +1,4 @@
+#include "c4.qh"
 REGISTER_MINIGAME(c4, "Connect Four");
 
 const float C4_TURN_PLACE = 0x0100; // player has to place a piece on the board
@@ -201,7 +202,7 @@ int c4_server_event(entity minigame, string event, ...)
                        if(e.classname == "minigame_board_piece")
                        {
                                if(e.netname) { strunzone(e.netname); }
-                               remove(e);
+                               delete(e);
                        }
                        return false;
                }
@@ -322,7 +323,7 @@ void c4_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void c4_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
diff --git a/qcsrc/common/minigames/minigame/c4.qh b/qcsrc/common/minigames/minigame/c4.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 8f8e3da2eab0719526230920994af4c81407a1e3..4184c2638251f8df15f3b43a4b594166cba73221 100644 (file)
@@ -1,3 +1,4 @@
+#include "nmm.qh"
 REGISTER_MINIGAME(nmm, "Nine Men's Morris");
 
 const int NMM_TURN_PLACE = 0x0100; // player has to place a piece on the board
@@ -123,7 +124,7 @@ void nmm_kill_tiles(entity minig)
                        strunzone(e.netname);
                        strunzone(e.nmm_tile_hmill);
                        strunzone(e.nmm_tile_vmill);
-                       remove(e);
+                       delete(e);
                }
 }
 
@@ -369,7 +370,7 @@ int nmm_server_event(entity minigame, string event, ...)
                                minigame.SendFlags |= MINIG_SF_UPDATE;
                        }
                        else
-                               LOG_TRACE("Invalid move: ",...(2,string),"\n");
+                               LOG_TRACE("Invalid move: ", ...(2, string));
                        return 1;
                }
        }
@@ -496,9 +497,8 @@ void nmm_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void nmm_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
-
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
        pos_y += ts_y;
diff --git a/qcsrc/common/minigames/minigame/nmm.qh b/qcsrc/common/minigames/minigame/nmm.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 602cad22d3d103b277ec0d3d00369e6943d15678..dd57fb5c66886c846458d3991ce1eef96f1707c2 100644 (file)
@@ -1,3 +1,4 @@
+#include "pong.qh"
 REGISTER_MINIGAME(pong, "Pong");
 
 // minigame flags
@@ -442,8 +443,8 @@ int pong_server_event(entity minigame, string event, ...)
                                                                paddle.realowner.classname == "pong_ai" )
                                                        {
                                                                minigame.pong_paddles[i] = NULL;
-                                                               remove(paddle.realowner);
-                                                               remove(paddle);
+                                                               delete(paddle.realowner);
+                                                               delete(paddle);
                                                                return true;
                                                        }
                                                }
@@ -542,7 +543,7 @@ void pong_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void pong_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
diff --git a/qcsrc/common/minigames/minigame/pong.qh b/qcsrc/common/minigames/minigame/pong.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 7d4048d61b58bcd2de5c21520286fb6ec3105a31..4ae9fb06b21245ff233831d8a3ff7a280e477439 100644 (file)
@@ -1,3 +1,4 @@
+#include "pp.qh"
 REGISTER_MINIGAME(pp, "Push-Pull");
 
 const int PP_TURN_PLACE = 0x0100; // player has to place a piece on the board
@@ -121,7 +122,7 @@ void pp_move(entity minigame, entity player, string pos )
                        if(existing)
                        {
                                if(existing.netname) { strunzone(existing.netname); }
-                               remove(existing);
+                               delete(existing);
                        }
 
                        entity piece = msle_spawn(minigame,"minigame_board_piece");
@@ -186,7 +187,7 @@ void pp_next_match(entity minigame, entity player)
                entity e = NULL;
                while ( ( e = findentity(e,owner,minigame) ) )
                        if ( e.classname == "minigame_board_piece" )
-                               remove(e);
+                               delete(e);
                minigame.pp_team1_score = 0;
                minigame.pp_team2_score = 0;
 
@@ -215,7 +216,7 @@ int pp_server_event(entity minigame, string event, ...)
                        if(e.classname == "minigame_board_piece")
                        {
                                if(e.netname) { strunzone(e.netname); }
-                               remove(e);
+                               delete(e);
                        }
                        return false;
                }
@@ -270,8 +271,7 @@ int pp_server_event(entity minigame, string event, ...)
 
 string pp_curr_pos; // identifier of the tile under the mouse
 vector pp_boardpos; // HUD board position
-vector pp_boardsize;// HUD board size
-.int pp_checkwin; // Used to optimize checks to display a win
+vector pp_boardsize; // HUD board size
 
 // Required function, draw the game board
 void pp_hud_board(vector pos, vector mySize)
@@ -374,7 +374,7 @@ void pp_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void pp_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
diff --git a/qcsrc/common/minigames/minigame/pp.qh b/qcsrc/common/minigames/minigame/pp.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 2a612c599a8317acdbd846d5505c1dded4a9ed81..cd5c001e7adf95a30802b03bf468f857692bc9df 100644 (file)
@@ -1,3 +1,4 @@
+#include "ps.qh"
 REGISTER_MINIGAME(ps, "Peg Solitaire");
 
 const float PS_TURN_MOVE  = 0x0100; // player has to click on a piece on the board
@@ -139,7 +140,7 @@ bool ps_move_piece(entity minigame, entity piece, string pos, int leti, int numb
                return false;
 
        if(middle.netname) { strunzone(middle.netname); }
-       remove(middle);
+       delete(middle);
 
        if(piece.netname) { strunzone(piece.netname); }
        piece.netname = strzone(pos);
@@ -232,7 +233,7 @@ int ps_server_event(entity minigame, string event, ...)
                        if(e.classname == "minigame_board_piece")
                        {
                                if(e.netname) { strunzone(e.netname); }
-                               remove(e);
+                               delete(e);
                        }
                        return false;
                }
@@ -429,7 +430,7 @@ void ps_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void ps_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
diff --git a/qcsrc/common/minigames/minigame/ps.qh b/qcsrc/common/minigames/minigame/ps.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 617ed957ca635f84081282dbeee62f664fb67ea9..3aedeb01632db8081faf98e3308738b51dcd45e5 100644 (file)
@@ -1,3 +1,4 @@
+#include "snake.qh"
 REGISTER_MINIGAME(snake, "Snake"); // SNAAAAKE
 
 const float SNAKE_TURN_MOVE  = 0x0100; // the snake is moving, player must control it
@@ -20,7 +21,9 @@ bool autocvar_sv_minigames_snake_wrap = false;
 float autocvar_sv_minigames_snake_delay_initial = 0.7;
 float autocvar_sv_minigames_snake_delay_multiplier = 50;
 float autocvar_sv_minigames_snake_delay_min = 0.1;
+#ifdef SVQC
 int autocvar_sv_minigames_snake_lives = 3;
+#endif
 
 .int snake_score;
 
@@ -94,7 +97,7 @@ void snake_new_mouse(entity minigame)
        {
                string pos = minigame_tile_buildname(i, j);
                if(!snake_find_piece(minigame, pos))
-                       RandomSelection_Add(NULL, 0, pos, 1, 1);
+                       RandomSelection_AddString(pos, 1, 1);
        }
 
        entity piece = msle_spawn(minigame,"minigame_board_piece");
@@ -174,7 +177,7 @@ void minigame_setup_snake(entity minigame, int pteam)
        {
                string pos = minigame_tile_buildname(i, j);
                if(!snake_find_piece(minigame, pos))
-                       RandomSelection_Add(NULL, 0, pos, 1, 1);
+                       RandomSelection_AddString(pos, 1, 1);
        }
 
        entity piece = msle_spawn(minigame,"minigame_board_piece");
@@ -287,7 +290,7 @@ void snake_eat_team(entity minigame, int pteam)
                if ( e.classname == "minigame_board_piece" && e.cnt && e.team == pteam )
                {
                        if(e.netname) { strunzone(e.netname); }
-                       remove(e);
+                       delete(e);
                }
 
        if(minigame.snake_lives[pteam] <= 0)
@@ -354,7 +357,7 @@ void snake_move_head(entity minigame, entity head)
        if(ate_mouse)
        {
                if(hit.netname) { strunzone(hit.netname); }
-               remove(hit);
+               delete(hit);
 
                snake_new_mouse(minigame);
        }
@@ -425,7 +428,7 @@ int snake_server_event(entity minigame, string event, ...)
                        if(e.classname == "minigame_board_piece")
                        {
                                if(e.netname) { strunzone(e.netname); }
-                               remove(e);
+                               delete(e);
                        }
                        return false;
                }
@@ -673,17 +676,16 @@ void snake_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void snake_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
-       vector ts;
-       ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
+       HUD_Panel_DrawBg();
+       vector ts = minigame_drawstring_wrapped(mySize.x, pos, active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
-       ts_y += hud_fontsize_y;
-       pos_y += ts_y;
-       mySize_y -= ts_y;
+       ts.y += hud_fontsize.y;
+       pos.y += ts.y;
+       mySize.y -= ts.y;
 
        vector player_fontsize = hud_fontsize * 1.75;
-       ts_y = ( mySize_y - SNAKE_TEAMS*player_fontsize_y ) / SNAKE_TEAMS;
-       ts_x = mySize_x;
+       ts.y = player_fontsize.y + (mySize.y - SNAKE_TEAMS * player_fontsize.y) / SNAKE_TEAMS;
+       ts.x = mySize_x;
        vector mypos;
 
        entity e;
@@ -691,22 +693,26 @@ void snake_hud_status(vector pos, vector mySize)
        {
                if ( e.classname == "minigame_player" )
                {
-                       mypos = pos;
-                       mypos_y  += (e.team-1) * (player_fontsize_y + ts_y);
+                       mypos = pos + eY * (e.team - 1) * ts.y;
 
-                       drawfill(mypos, ts, snake_teamcolor(e.team), 0.25, DRAWFLAG_ADDITIVE);
+                       if (e == minigame_self)
+                       {
+                               const vector hl_size = '1 1 0';
+                               drawfill(mypos + hl_size, ts - 2 * hl_size, snake_teamcolor(e.team), 0.25 * panel_fg_alpha, DRAWFLAG_ADDITIVE);
+                               drawborderlines(hl_size.x, mypos + hl_size, ts - 2 * hl_size, snake_teamcolor(e.team), panel_fg_alpha, DRAWFLAG_NORMAL);
+                       }
+                       else
+                               drawfill(mypos, ts, snake_teamcolor(e.team), 0.25 * panel_fg_alpha, DRAWFLAG_ADDITIVE);
 
-                       minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
-                               entcs_GetName(e.minigame_playerslot-1),
+                       minigame_drawcolorcodedstring_trunc(mySize.x - hud_fontsize.x * 0.5, mypos + eX * hud_fontsize.x * 0.25,
+                               entcs_GetName(e.minigame_playerslot - 1),
                                player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
 
-                       drawstring(mypos+eY*player_fontsize_y,ftos(e.snake_score),'48 48 0' * (SNAKE_TEAMS * 0.1),
-                                          '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-                       drawstring(mypos+(eY*player_fontsize_y) + (eX*player_fontsize_x),strcat("1UP: ", ftos(active_minigame.snake_lives[e.team])),'48 48 0' * (SNAKE_TEAMS * 0.1),
-                                                        '1 0.44 0.54', panel_fg_alpha, DRAWFLAG_NORMAL);
-
-                       if ( e == minigame_self )
-                               drawborderlines(1, mypos, ts, snake_teamcolor(e.team), 1, DRAWFLAG_NORMAL);
+                       mypos.y += player_fontsize.y;
+                       drawstring_aspect(mypos, ftos(e.snake_score), ts - eY * player_fontsize.y - eX * ts.x * (3 / 4),
+                                                               '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+                       drawstring_aspect(mypos + eX * ts.x * (1 / 4), strcat("1UP: ", ftos(active_minigame.snake_lives[e.team])), ts - eY * player_fontsize.y - eX * ts.x * (1 / 4),
+                                                               '1 0.44 0.54', panel_fg_alpha, DRAWFLAG_NORMAL);
                }
        }
 }
diff --git a/qcsrc/common/minigames/minigame/snake.qh b/qcsrc/common/minigames/minigame/snake.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index b5248467f5908d8aca373cca211c87cc6f204726..c5a658054fe0d1e55192363b7e12cfd0abe9112b 100644 (file)
@@ -1,3 +1,4 @@
+#include "ttt.qh"
 REGISTER_MINIGAME(ttt, "Tic Tac Toe");
 
 const int TTT_TURN_PLACE = 0x0100; // player has to place a piece on the board
@@ -123,7 +124,7 @@ void ttt_next_match(entity minigame, entity player)
                entity e = NULL;
                while ( ( e = findentity(e,owner,minigame) ) )
                        if ( e.classname == "minigame_board_piece" )
-                               remove(e);
+                               delete(e);
        }
 }
 
@@ -147,7 +148,7 @@ int ttt_server_event(entity minigame, string event, ...)
                        if(e.classname == "minigame_board_piece")
                        {
                                if(e.netname) { strunzone(e.netname); }
-                               remove(e);
+                               delete(e);
                        }
                        return false;
                }
@@ -272,7 +273,7 @@ void ttt_hud_board(vector pos, vector mySize)
 // Required function, draw the game status panel
 void ttt_hud_status(vector pos, vector mySize)
 {
-       HUD_Panel_DrawBg(1);
+       HUD_Panel_DrawBg();
        vector ts;
        ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
                hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
@@ -417,11 +418,11 @@ int ttt_ai_random(int piecemask)
        for ( int i = 0; i < 9; i++ )
        {
                if ( piecemask & f )
-                       RandomSelection_Add(NULL, f, string_null, 1, 1);
+                       RandomSelection_AddFloat(f, 1, 1);
                f <<= 1;
        }
 
-       LOG_TRACE(sprintf("TTT AI: selected %x from %x\n",
+       LOG_TRACE(sprintf("TTT AI: selected %x from %x",
                        RandomSelection_chosen_float, piecemask) );
        return RandomSelection_chosen_float;
 }
@@ -439,7 +440,7 @@ int ttt_ai_block3 ( int piecemask, int piecemask_free )
        r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A3,TTT_AI_POSFLAG_B3,TTT_AI_POSFLAG_C3);
        r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A1,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C3);
        r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A3,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C1);
-       LOG_TRACE(sprintf("TTT AI: possible 3 in a rows in %x: %x (%x)\n",piecemask,r, r&piecemask_free));
+       LOG_TRACE(sprintf("TTT AI: possible 3 in a rows in %x: %x (%x)",piecemask,r, r&piecemask_free));
        r &= piecemask_free;
        return ttt_ai_random(r);
 }
@@ -452,15 +453,15 @@ string ttt_ai_choose_simple(int piecemask_self, int piecemask_opponent, int piec
 {
        int move = 0;
 
-       LOG_TRACE("TTT AI: checking winning move\n");
+       LOG_TRACE("TTT AI: checking winning move");
        if (( move = ttt_ai_block3(piecemask_self,piecemask_free) ))
                return ttt_ai_piece_flag2pos(move); // place winning move
 
-       LOG_TRACE("TTT AI: checking opponent's winning move\n");
+       LOG_TRACE("TTT AI: checking opponent's winning move");
        if (( move = ttt_ai_block3(piecemask_opponent,piecemask_free) ))
                return ttt_ai_piece_flag2pos(move); // block opponent
 
-       LOG_TRACE("TTT AI: random move\n");
+       LOG_TRACE("TTT AI: random move");
        return ttt_ai_piece_flag2pos(ttt_ai_random(piecemask_free));
 }
 
@@ -510,12 +511,12 @@ void ttt_aimove(entity minigame)
                }
 
                // TODO multiple AI difficulties
-               LOG_TRACE(sprintf("TTT AI: self: %x opponent: %x free: %x\n",
+               LOG_TRACE(sprintf("TTT AI: self: %x opponent: %x free: %x",
                                piecemask_self, piecemask_opponent, piecemask_free));
                pos = ttt_ai_choose_simple(piecemask_self, piecemask_opponent, piecemask_free);
-               LOG_TRACE("TTT AI: chosen move: ",pos,"\n\n");
+               LOG_TRACE("TTT AI: chosen move: ", pos);
                if ( !pos )
-                       LOG_TRACE("Tic Tac Toe AI has derped!\n");
+                       LOG_TRACE("Tic Tac Toe AI has derped!");
                else
                        ttt_move(minigame,aiplayer,pos);
        }
diff --git a/qcsrc/common/minigames/minigame/ttt.qh b/qcsrc/common/minigames/minigame/ttt.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 7425149f389d9b484c38d089ebef97c2b750f6df..284001a0a22e97997b107a386601d9f75207345f 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MINIGAMES_H
-#define MINIGAMES_H
+#pragma once
 
 // previous node in a doubly linked list
 .entity list_prev;
@@ -121,5 +120,3 @@ entity msle_spawn(entity minigame_session, string class_name);
 
 int msle_id(string class_name);
 string msle_classname(int id);
-
-#endif
index 689ce83d0295968e7e1ede2e3923fdeb4ed53465..5f1d1280b9f5a35dd85e2e4d99b6127767ee93c2 100644 (file)
@@ -1,3 +1,4 @@
+#include "sv_minigames.qh"
 #include "minigames.qh"
 
 void player_clear_minigame(entity player)
@@ -5,9 +6,9 @@ void player_clear_minigame(entity player)
        player.active_minigame = NULL;
        player.minigame_players = NULL;
        if ( IS_PLAYER(player) )
-               player.movetype = MOVETYPE_WALK;
+               set_movetype(player, MOVETYPE_WALK);
        else
-               player.movetype = MOVETYPE_FLY_WORLDONLY;
+               set_movetype(player, MOVETYPE_FLY_WORLDONLY);
        player.team_forced = 0;
 }
 
@@ -27,7 +28,7 @@ void minigame_rmplayer(entity minigame_session, entity player)
                GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":",
                        ftos(etof(player)),":",player.netname));
                minigame_session.minigame_players = p.list_next;
-               remove ( p );
+               delete ( p );
                player_clear_minigame(player);
        }
        else
@@ -40,7 +41,7 @@ void minigame_rmplayer(entity minigame_session, entity player)
                                GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":",
                                        ftos(etof(player)),":",player.netname));
                                p.list_next = e.list_next;
-                               remove(e);
+                               delete(e);
                                player_clear_minigame(player);
                                return;
                        }
@@ -112,11 +113,11 @@ void minigame_resend(entity minigame)
        }
 }
 
-bool minigame_CheckSend(entity this)
+bool minigame_CheckSend(entity this, entity client)
 {
        entity e;
        for ( e = this.owner.minigame_players; e != NULL; e = e.list_next )
-               if ( e.minigame_players == other )
+               if ( e.minigame_players == client )
                        return true;
        return false;
 }
@@ -153,7 +154,7 @@ int minigame_addplayer(entity minigame_session, entity player)
 
                minigame_resend(minigame_session);
        }
-       else { remove(player_pointer); }
+       else { delete(player_pointer); }
        GameLogEcho(strcat(":minigame:join",(mgteam?"":"fail"),":",minigame_session.netname,":",
                ftos(etof(player)),":",player.netname));
 
@@ -176,7 +177,7 @@ entity start_minigame(entity player, string minigame )
                GameLogEcho(strcat(":minigame:start:",minig.netname));
                if ( ! minigame_addplayer(minig,player) )
                {
-                       LOG_TRACE("Minigame ",minig.netname," rejected the first player join!\n");
+                       LOG_TRACE("Minigame ",minig.netname," rejected the first player join!");
                        end_minigame(minig);
                        return NULL;
                }
@@ -235,8 +236,8 @@ void end_minigame(entity minigame_session)
        while( (e = findentity(e, owner, minigame_session)) )
                if ( e.minigame_autoclean )
                {
-                       LOG_TRACE("SV Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")\n");
-                       remove(e);
+                       LOG_TRACE("SV Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")");
+                       delete(e);
                }
 
        entity p;
@@ -244,11 +245,11 @@ void end_minigame(entity minigame_session)
        {
                p = e.list_next;
                player_clear_minigame(e.minigame_players);
-               remove(e);
+               delete(e);
        }
 
        strunzone(minigame_session.netname);
-       remove(minigame_session);
+       delete(minigame_session);
 }
 
 void end_minigames()
index 70c9bf80581c05e797ae76b79cc3d2a69ebc649b..b5015a1f40fd3e452d83ce2d23ed5a510ce63c4c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SV_MINIGAMES_H
-#define SV_MINIGAMES_H
+#pragma once
 
 /// Create a new minigame session
 /// \return minigame session entity
@@ -24,7 +23,7 @@ void end_minigames();
 
 // Only sends entities to players who joined the minigame
 // Use on customizeentityforclient for gameplay entities
-bool minigame_CheckSend(entity this);
+bool minigame_CheckSend(entity this, entity client);
 
 // Check for minigame impulses
 bool MinigameImpulse(entity this, int imp);
@@ -59,5 +58,3 @@ REGISTRY_CHECK(Minigames)
                this.minigame_event = name##_server_event; \
     } \
     REGISTER_INIT(MINIGAME_##name)
-
-#endif
index f3bfd64c7532a833757eed9545782933b72272aa..ce98629411fd0cd6c3ce798a813acf5dd0fd602f 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MODELS_ALL_H
-#define MODELS_ALL_H
+#pragma once
 
 #include "model.qh"
 
@@ -22,5 +21,3 @@ PRECACHE(Models) {
 
 MODEL(Null, "null");
 #include "all.inc"
-
-#endif
index 1c34a2547ee07117b1f52d5707bbebd3b1c80eae..7a1e7d73c2a471c802a1cf496d9a68e15c0d49e9 100644 (file)
@@ -1,11 +1,10 @@
-#ifndef MODEL_H
-#define MODEL_H
+#pragma once
 
 #define setmodel(e, m) _setmodel((e), (m).model_str())
 
 CLASS(Model, Object)
-    ATTRIB(Model, m_id, int, 0)
-    ATTRIB(Model, model_str, string(), func_null)
+    ATTRIB(Model, m_id, int, 0);
+    ATTRIB(Model, model_str, string());
     CONSTRUCTOR(Model, string() path)
     {
         CONSTRUCT(Model);
@@ -16,12 +15,10 @@ CLASS(Model, Object)
         TC(Model, this);
         string s = this.model_str();
         if (s != "" && s != "null" && !fexists(s)) {
-            LOG_WARNINGF("Missing model: \"%s\"\n", s);
+            LOG_WARNF("Missing model: \"%s\"", s);
             return;
         }
-        LOG_DEBUGF("precache_model(\"%s\")\n", s);
+        profile(sprintf("precache_model(\"%s\")", s));
         precache_model(s);
     }
 ENDCLASS(Model)
-
-#endif
index f9d80b3f4e36f6de4e97cc4e84fe442b559e8e5b..e7f1e9734d4269ce360bb65ebe2271b5c4a0ae08 100644 (file)
@@ -1,4 +1,11 @@
 // generated file; do not modify
 #include <common/monsters/all.qc>
-#include <common/monsters/spawn.qc>
-#include <common/monsters/sv_monsters.qc>
+#ifdef SVQC
+    #include <common/monsters/sv_monsters.qc>
+#endif
+#ifdef SVQC
+    #include <common/monsters/sv_spawn.qc>
+       #include <common/monsters/spawner.qc>
+#endif
+
+#include <common/monsters/monster/_mod.inc>
index 48427f94f7dbd2c779f5e939a90d2b3ccb122b90..55204bd1a770ec46b66e48222b6d0ae3fcbf62e7 100644 (file)
@@ -1,4 +1,10 @@
 // generated file; do not modify
 #include <common/monsters/all.qh>
-#include <common/monsters/spawn.qh>
-#include <common/monsters/sv_monsters.qh>
+#ifdef SVQC
+    #include <common/monsters/sv_monsters.qh>
+#endif
+#ifdef SVQC
+    #include <common/monsters/sv_spawn.qh>
+#endif
+
+#include <common/monsters/monster/_mod.qh>
index fa3f65193ae5725bc0e4a87806433875344a23f3..9dc09ca394b47166bc4d717901d0aa17f6465664 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MONSTERS_ALL_C
-#define MONSTERS_ALL_C
+#include "all.qh"
 
 string M_Model(string m_mdl)
 {
@@ -11,16 +10,3 @@ string M_Model(string m_mdl)
        return output;
 #endif
 }
-
-#include "all.qh"
-
-#define IMPLEMENTATION
-#include "monster/_mod.inc"
-#undef IMPLEMENTATION
-
-#ifdef SVQC
-#include "spawn.qc"
-#include "sv_monsters.qc"
-#endif
-
-#endif
index 1e23f3287940218b3c8a882033d3f12b42c88fd2..c9e5ad37ba6448ed85374378affde2e871a6197c 100644 (file)
@@ -1,7 +1,4 @@
-#ifndef MONSTERS_ALL_H
-#define MONSTERS_ALL_H
-
-#include "monster.qh"
+#pragma once
 
 string M_Model(string m_mdl);
 
@@ -14,9 +11,6 @@ const int MON_FIRST = 1;
 #define MON_LAST (Monsters_COUNT - 1)
 #define REGISTER_MONSTER(id, inst) REGISTER(Monsters, MON, id, monsterid, inst)
 
-REGISTER_MONSTER(Null, NEW(Monster));
-
-
-#include "monster/_mod.inc"
+#include "monster.qh"
 
-#endif
+REGISTER_MONSTER(Null, NEW(Monster));
index de24caa01a2ded2092bfd8689b2a4924f849fe4d..ab644988aa4d1192e60fde95bb97f28da642c0d3 100644 (file)
@@ -1,34 +1,18 @@
-#ifndef MONSTER_H
-#define MONSTER_H
-
-#ifdef SVQC
-#include "sv_monsters.qh"
-#include <server/g_damage.qh>
-#include <server/bot/bot.qh>
-#include <server/weapons/common.qh>
-#include <server/weapons/tracing.qh>
-#include <server/weapons/weaponsystem.qh>
-#include <common/mutators/mutator/waypoints/waypointsprites.qh>
-#include <lib/warpzone/server.qh>
-#endif
-
-#ifndef MENUQC
-#include "../animdecide.qh"
-#include "../anim.qh"
-vector animfixfps(entity e, vector a, vector b);
-#endif
+#pragma once
 
 // special spawn flags
-const int MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died
-const int MONSTER_TYPE_FLY = 32;
-const int MONSTER_TYPE_SWIM = 64;
-const int MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced
-const int MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster
-const int MON_FLAG_RANGED = 512; // monster shoots projectiles
-const int MON_FLAG_MELEE = 1024;
-const int MON_FLAG_CRUSH = 2048; // monster can be stomped in special modes
-const int MON_FLAG_RIDE = 4096; // monster can be ridden in special modes
-const int MONSTER_SIZE_QUAKE = 8192;
+const int MONSTER_RESPAWN_DEATHPOINT = BIT(4); // re-spawn where we died
+const int MONSTER_TYPE_FLY = BIT(5);
+const int MONSTER_TYPE_SWIM = BIT(6);
+const int MONSTER_SIZE_BROKEN = BIT(7); // TODO: remove when bad models are replaced
+const int MON_FLAG_SUPERMONSTER = BIT(8); // incredibly powerful monster
+const int MON_FLAG_RANGED = BIT(9); // monster shoots projectiles
+const int MON_FLAG_MELEE = BIT(10);
+const int MON_FLAG_CRUSH = BIT(11); // monster can be stomped in special modes
+const int MON_FLAG_RIDE = BIT(12); // monster can be ridden in special modes
+const int MONSTER_SIZE_QUAKE = BIT(13);
+const int MONSTER_TYPE_PASSIVE = BIT(14); // doesn't target or chase enemies
+const int MONSTER_TYPE_UNDEAD = BIT(15); // monster is by most definitions a zombie (doesn't fully die unless gibbed)
 
 // entity properties of monsterinfo:
 .bool(int, entity actor, entity targ) monster_attackfunc;
@@ -39,26 +23,23 @@ const int MONSTER_SIZE_QUAKE = 8192;
 .vector anim_melee1;
 .vector anim_melee2;
 .vector anim_melee3;
-.vector anim_pain3;
-.vector anim_pain4;
-.vector anim_pain5;
 .vector anim_walk;
 .vector anim_spawn;
 
 CLASS(Monster, Object)
-    ATTRIB(Monster, monsterid, int, 0)
+    ATTRIB(Monster, monsterid, int, 0);
     /** attributes */
-    ATTRIB(Monster, spawnflags, int, 0)
+    ATTRIB(Monster, spawnflags, int, 0);
     /** human readable name */
-    ATTRIB(Monster, monster_name, string, "Monster")
+    ATTRIB(Monster, monster_name, string, "Monster");
     /** short name */
-    ATTRIB(Monster, netname, string, "")
+    ATTRIB(Monster, netname, string, "");
     /** model */
-    ATTRIB(Monster, m_model, entity, NULL)
+    ATTRIB(Monster, m_model, entity);
     /** hitbox size */
-    ATTRIB(Monster, mins, vector, '-0 -0 -0')
+    ATTRIB(Monster, mins, vector, '-0 -0 -0');
     /** hitbox size */
-    ATTRIB(Monster, maxs, vector, '0 0 0')
+    ATTRIB(Monster, maxs, vector, '0 0 0');
 
     /** (SERVER) setup monster data */
     METHOD(Monster, mr_setup, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
@@ -75,4 +56,20 @@ CLASS(Monster, Object)
 
 ENDCLASS(Monster)
 
+
+#ifdef SVQC
+#include "sv_monsters.qh"
+#include <server/g_damage.qh>
+#include <server/bot/api.qh>
+#include <server/weapons/common.qh>
+#include <server/weapons/tracing.qh>
+#include <server/weapons/weaponsystem.qh>
+#include <common/mutators/mutator/waypoints/waypointsprites.qh>
+#include <lib/warpzone/server.qh>
+#endif
+
+#ifdef GAMEQC
+#include "../animdecide.qh"
+#include "../anim.qh"
+vector animfixfps(entity e, vector a, vector b);
 #endif
index 44d7121fcd42c6def17b5fd4b4c5350e98cbdd0b..748f9a9bd639130f695a0d5f936c93492f016d32 100644 (file)
@@ -1,41 +1,4 @@
-#ifndef MAGE_H
-#define MAGE_H
-
-#ifndef MENUQC
-MODEL(MON_MAGE, M_Model("mage.dpm"));
-#endif
-
-CLASS(Mage, Monster)
-    ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED);
-    ATTRIB(Mage, mins, vector, '-36 -36 -24');
-    ATTRIB(Mage, maxs, vector, '36 36 50');
-#ifndef MENUQC
-    ATTRIB(Mage, m_model, Model, MDL_MON_MAGE);
-#endif
-    ATTRIB(Mage, netname, string, "mage");
-    ATTRIB(Mage, monster_name, string, _("Mage"));
-ENDCLASS(Mage)
-
-REGISTER_MONSTER(MAGE, NEW(Mage)) {
-#ifndef MENUQC
-    this.mr_precache(this);
-#endif
-}
-
-#include <common/weapons/all.qh>
-#include <common/items/all.qc>
-
-CLASS(MageSpike, PortoLaunch)
-/* flags     */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
-/* impulse   */ ATTRIB(MageSpike, impulse, int, 9);
-/* refname   */ ATTRIB(MageSpike, netname, string, "magespike");
-/* wepname   */ ATTRIB(MageSpike, m_name, string, _("Mage spike"));
-ENDCLASS(MageSpike)
-REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike));
-
-#endif
-
-#ifdef IMPLEMENTATION
+#include "mage.qh"
 
 #ifdef SVQC
 
@@ -49,7 +12,7 @@ METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, .entity weapon
     if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
         if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range;
         actor.enemy = Monster_FindTarget(actor);
-        W_SetupShot_Dir(actor, v_forward, false, 0, SND_MageSpike_FIRE, CH_WEAPON_B, 0);
+        W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MageSpike_FIRE, CH_WEAPON_B, 0);
        if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
         M_Mage_Attack_Spike(actor, w_shotdir);
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
@@ -146,7 +109,7 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ)
        return false;
 }
 
-void M_Mage_Attack_Spike_Explode(entity this)
+void M_Mage_Attack_Spike_Explode(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
 
@@ -155,16 +118,16 @@ void M_Mage_Attack_Spike_Explode(entity this)
        this.realowner.mage_spike = NULL;
 
        Send_Effect(EFFECT_EXPLOSION_SMALL, this.origin, '0 0 0', 1);
-       RadiusDamage (this, this.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), NULL, NULL, 0, DEATH_MONSTER_MAGE.m_id, other);
+       RadiusDamage (this, this.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), NULL, NULL, 0, DEATH_MONSTER_MAGE.m_id, directhitentity);
 
-       remove (this);
+       delete (this);
 }
 
-void M_Mage_Attack_Spike_Touch(entity this)
+void M_Mage_Attack_Spike_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
-       M_Mage_Attack_Spike_Explode(this);
+       M_Mage_Attack_Spike_Explode(this, toucher);
 }
 
 .float wait;
@@ -174,7 +137,7 @@ void M_Mage_Attack_Spike_Think(entity this)
 {
        if (time > this.ltime || (this.enemy && this.enemy.health <= 0) || this.owner.health <= 0) {
                this.projectiledeathtype |= HITTYPE_SPLASH;
-               M_Mage_Attack_Spike_Explode(this);
+               M_Mage_Attack_Spike_Explode(this, NULL);
        }
 
        float spd = vlen(this.velocity);
@@ -199,7 +162,7 @@ void M_Mage_Attack_Spike_Think(entity this)
                // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
                if ((autocvar_g_monster_mage_attack_spike_smart) && vdist(eorg - this.origin, >, autocvar_g_monster_mage_attack_spike_smart_mindist))
                {
-                       // Is it a better idea (shorter distance) to trace to the target itthis?
+                       // Is it a better idea (shorter distance) to trace to the target itself?
                        if ( vlen2(this.origin + olddir * this.wait) < vlen2(eorg - this.origin))
                                traceline(this.origin, this.origin + olddir * this.wait, false, this);
                        else
@@ -233,8 +196,10 @@ void M_Mage_Attack_Spike(entity this, vector dir)
        missile.ltime = time + 7;
        missile.nextthink = time;
        missile.solid = SOLID_BBOX;
-       missile.movetype = MOVETYPE_FLYMISSILE;
+       set_movetype(missile, MOVETYPE_FLYMISSILE);
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        setorigin(missile, this.origin + v_forward * 14 + '0 0 30' + v_right * -14);
        setsize(missile, '0 0 0', '0 0 0');
        missile.velocity = dir * 400;
@@ -249,52 +214,51 @@ void M_Mage_Attack_Spike(entity this, vector dir)
 
 void M_Mage_Defend_Heal(entity this)
 {
-       entity head;
        float washealed = false;
 
-       for(head = findradius(this.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(M_Mage_Defend_Heal_Check(this, head))
+       FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_mage_heal_range, M_Mage_Defend_Heal_Check(this, it),
        {
                washealed = true;
                string fx = "";
-               if(IS_PLAYER(head))
+               if(IS_PLAYER(it))
                {
                        switch(this.skin)
                        {
                                case 0:
-                                       if(head.health < autocvar_g_balance_health_regenstable) head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable);
+                                       if(it.health < autocvar_g_balance_health_regenstable) it.health = bound(0, it.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable);
                                        fx = EFFECT_HEALING.eent_eff_name;
                                        break;
                                case 1:
-                                       if(head.ammo_cells) head.ammo_cells = bound(head.ammo_cells, head.ammo_cells + 1, g_pickup_cells_max);
-                                       if(head.ammo_plasma) head.ammo_plasma = bound(head.ammo_plasma, head.ammo_plasma + 1, g_pickup_plasma_max);
-                                       if(head.ammo_rockets) head.ammo_rockets = bound(head.ammo_rockets, head.ammo_rockets + 1, g_pickup_rockets_max);
-                                       if(head.ammo_shells) head.ammo_shells = bound(head.ammo_shells, head.ammo_shells + 2, g_pickup_shells_max);
-                                       if(head.ammo_nails) head.ammo_nails = bound(head.ammo_nails, head.ammo_nails + 5, g_pickup_nails_max);
+                                       if(it.ammo_cells) it.ammo_cells = bound(it.ammo_cells, it.ammo_cells + 1, g_pickup_cells_max);
+                                       if(it.ammo_plasma) it.ammo_plasma = bound(it.ammo_plasma, it.ammo_plasma + 1, g_pickup_plasma_max);
+                                       if(it.ammo_rockets) it.ammo_rockets = bound(it.ammo_rockets, it.ammo_rockets + 1, g_pickup_rockets_max);
+                                       if(it.ammo_shells) it.ammo_shells = bound(it.ammo_shells, it.ammo_shells + 2, g_pickup_shells_max);
+                                       if(it.ammo_nails) it.ammo_nails = bound(it.ammo_nails, it.ammo_nails + 5, g_pickup_nails_max);
                                        fx = "ammoregen_fx";
                                        break;
                                case 2:
-                                       if(head.armorvalue < autocvar_g_balance_armor_regenstable)
+                                       if(it.armorvalue < autocvar_g_balance_armor_regenstable)
                                        {
-                                               head.armorvalue = bound(0, head.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable);
+                                               it.armorvalue = bound(0, it.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable);
                                                fx = "armorrepair_fx";
                                        }
                                        break;
                                case 3:
-                                       head.health = bound(0, head.health - ((head == this)  ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable);
+                                       it.health = bound(0, it.health - ((it == this)  ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable);
                                        fx = EFFECT_RAGE.eent_eff_name;
                                        break;
                        }
 
-                       Send_Effect_(fx, head.origin, '0 0 0', 1);
+                       Send_Effect_(fx, it.origin, '0 0 0', 1);
                }
                else
                {
-                       Send_Effect(EFFECT_HEALING, head.origin, '0 0 0', 1);
-                       head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health);
-                       if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE) && head.sprite)
-                               WaypointSprite_UpdateHealth(head.sprite, head.health);
+                       Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
+                       it.health = bound(0, it.health + (autocvar_g_monster_mage_heal_allies), it.max_health);
+                       if(!(it.spawnflags & MONSTERFLAG_INVINCIBLE) && it.sprite)
+                               WaypointSprite_UpdateHealth(it.sprite, it.health);
                }
-       }
+       });
 
        if(washealed)
        {
@@ -407,7 +371,7 @@ bool M_Mage_Attack(int attack_type, entity actor, entity targ)
        return false;
 }
 
-spawnfunc(monster_mage) { Monster_Spawn(this, MON_MAGE.monsterid); }
+spawnfunc(monster_mage) { Monster_Spawn(this, true, MON_MAGE.monsterid); }
 
 #endif // SVQC
 
@@ -417,10 +381,9 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor))
     TC(Mage, thismon);
     bool need_help = false;
 
-    FOREACH_ENTITY_FLOAT(iscreature, true,
+    FOREACH_CLIENT(IS_PLAYER(it) && it != actor,
     {
-        if(it != actor)
-        if(vdist(it.origin - actor.origin, <=, autocvar_g_monster_mage_heal_range))
+       if(vdist(it.origin - actor.origin, <=, autocvar_g_monster_mage_heal_range))
         if(M_Mage_Defend_Heal_Check(actor, it))
         {
             need_help = true;
@@ -428,6 +391,19 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor))
         }
     });
 
+    if(!need_help)
+    {
+       IL_EACH(g_monsters, it != actor,
+       {
+               if(vdist(it.origin - actor.origin, <=, autocvar_g_monster_mage_heal_range))
+               if(M_Mage_Defend_Heal_Check(actor, it))
+               {
+                   need_help = true;
+                   break;
+               }
+       });
+    }
+
     if(actor.health < (autocvar_g_monster_mage_heal_minhealth) || need_help)
     if(time >= actor.attack_finished_single[0])
     if(random() < 0.5)
@@ -459,7 +435,7 @@ METHOD(Mage, mr_death, bool(Mage this, entity actor))
 }
 
 #endif
-#ifndef MENUQC
+#ifdef GAMEQC
 METHOD(Mage, mr_anim, bool(Mage this, entity actor))
 {
     TC(Mage, this);
@@ -475,7 +451,7 @@ METHOD(Mage, mr_anim, bool(Mage this, entity actor))
 #endif
 #ifdef SVQC
 .float speed;
-spawnfunc(item_health_large);
+spawnfunc(item_health_big);
 METHOD(Mage, mr_setup, bool(Mage this, entity actor))
 {
     TC(Mage, this);
@@ -485,7 +461,7 @@ METHOD(Mage, mr_setup, bool(Mage this, entity actor))
     if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_mage_speed_stop); }
     if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_mage_damageforcescale); }
 
-    actor.monster_loot = spawnfunc_item_health_large;
+    actor.monster_loot = spawnfunc_item_health_big;
     actor.monster_attackfunc = M_Mage_Attack;
 
     return true;
@@ -497,5 +473,3 @@ METHOD(Mage, mr_precache, bool(Mage this))
     return true;
 }
 #endif
-
-#endif
diff --git a/qcsrc/common/monsters/monster/mage.qh b/qcsrc/common/monsters/monster/mage.qh
new file mode 100644 (file)
index 0000000..d78ee5e
--- /dev/null
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_MAGE, M_Model("mage.dpm"));
+#endif
+
+CLASS(Mage, Monster)
+    ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED);
+    ATTRIB(Mage, mins, vector, '-36 -36 -24');
+    ATTRIB(Mage, maxs, vector, '36 36 50');
+#ifdef GAMEQC
+    ATTRIB(Mage, m_model, Model, MDL_MON_MAGE);
+#endif
+    ATTRIB(Mage, netname, string, "mage");
+    ATTRIB(Mage, monster_name, string, _("Mage"));
+ENDCLASS(Mage)
+
+REGISTER_MONSTER(MAGE, NEW(Mage)) {
+#ifdef GAMEQC
+    this.mr_precache(this);
+#endif
+}
+
+#include <common/weapons/_all.qh>
+#include <common/items/_mod.qh>
+
+CLASS(MageSpike, PortoLaunch)
+/* flags     */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(MageSpike, impulse, int, 9);
+/* refname   */ ATTRIB(MageSpike, netname, string, "magespike");
+/* wepname   */ ATTRIB(MageSpike, m_name, string, _("Mage spike"));
+ENDCLASS(MageSpike)
+REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike));
index 7a11b139e446ca1847e261ce06fa0d19feaf5f81..aae268666f1e72d501150f8e0fff59fa9c786272 100644 (file)
@@ -1,30 +1,4 @@
-#ifndef SHAMBLER_H
-#define SHAMBLER_H
-
-#ifndef MENUQC
-MODEL(MON_SHAMBLER, M_Model("shambler.mdl"));
-#endif
-
-CLASS(Shambler, Monster)
-    ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
-    ATTRIB(Shambler, mins, vector, '-41 -41 -31');
-    ATTRIB(Shambler, maxs, vector, '41 41 65');
-#ifndef MENUQC
-    ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER);
-#endif
-    ATTRIB(Shambler, netname, string, "shambler");
-    ATTRIB(Shambler, monster_name, string, _("Shambler"));
-ENDCLASS(Shambler)
-
-REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) {
-#ifndef MENUQC
-    this.mr_precache(this);
-#endif
-}
-
-#endif
-
-#ifdef IMPLEMENTATION
+#include "shambler.qh"
 
 #ifdef SVQC
 float autocvar_g_monster_shambler_health;
@@ -78,28 +52,27 @@ void M_Shambler_Attack_Swing(entity this)
 
 #include <common/effects/qc/all.qh>
 
-void M_Shambler_Attack_Lightning_Explode(entity this)
+void M_Shambler_Attack_Lightning_Explode(entity this, entity directhitentity)
 {
-       entity head;
-
        sound(this, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
        Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.velocity = '0 0 0';
 
-       if(this.movetype == MOVETYPE_NONE)
+       if(this.move_movetype == MOVETYPE_NONE)
                this.velocity = this.oldvelocity;
 
-       RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, other);
+       RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius),
+                                       NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, directhitentity);
 
-       for(head = findradius(this.origin, (autocvar_g_monster_shambler_attack_lightning_radius_zap)); head; head = head.chain) if(head != this.realowner) if(head.takedamage)
+       FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_shambler_attack_lightning_radius_zap, it != this.realowner && it.takedamage,
        {
-               te_csqc_lightningarc(this.origin, head.origin);
-               Damage(head, this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_ZAP.m_id, head.origin, '0 0 0');
-       }
+               te_csqc_lightningarc(this.origin, it.origin);
+               Damage(it, this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_ZAP.m_id, it.origin, '0 0 0');
+       });
 
        setthink(this, SUB_Remove);
        this.nextthink = time + 0.2;
@@ -107,7 +80,7 @@ void M_Shambler_Attack_Lightning_Explode(entity this)
 
 void M_Shambler_Attack_Lightning_Explode_use(entity this, entity actor, entity trigger)
 {
-       M_Shambler_Attack_Lightning_Explode(this);
+       M_Shambler_Attack_Lightning_Explode(this, trigger);
 }
 
 void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -124,11 +97,11 @@ void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity at
                W_PrepareExplosionByDamage(this, attacker, adaptor_think2use);
 }
 
-void M_Shambler_Attack_Lightning_Touch(entity this)
+void M_Shambler_Attack_Lightning_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
-       this.use(this, NULL, NULL);
+       this.use(this, NULL, toucher);
 }
 
 void M_Shambler_Attack_Lightning_Think(entity this)
@@ -136,8 +109,7 @@ void M_Shambler_Attack_Lightning_Think(entity this)
        this.nextthink = time;
        if (time > this.cnt)
        {
-               other = NULL;
-               M_Shambler_Attack_Lightning_Explode(this);
+               M_Shambler_Attack_Lightning_Explode(this, NULL);
                return;
        }
 }
@@ -152,7 +124,7 @@ void M_Shambler_Attack_Lightning(entity this)
        gren.owner = gren.realowner = this;
        gren.bot_dodge = true;
        gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage);
-       gren.movetype = MOVETYPE_BOUNCE;
+       set_movetype(gren, MOVETYPE_BOUNCE);
        PROJECTILE_MAKETRIGGER(gren);
        gren.projectiledeathtype = DEATH_MONSTER_SHAMBLER_ZAP.m_id;
        setorigin(gren, CENTER_OR_VIEWOFS(this));
@@ -170,11 +142,14 @@ void M_Shambler_Attack_Lightning(entity this)
        gren.damageforcescale = 0;
        gren.event_damage = M_Shambler_Attack_Lightning_Damage;
        gren.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false);
 
        gren.angles = vectoangles (gren.velocity);
        gren.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, gren);
+       IL_PUSH(g_bot_dodge, gren);
 
        CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true);
 }
@@ -226,7 +201,7 @@ bool M_Shambler_Attack(int attack_type, entity actor, entity targ)
        return false;
 }
 
-spawnfunc(monster_shambler) { Monster_Spawn(this, MON_SHAMBLER.monsterid); }
+spawnfunc(monster_shambler) { Monster_Spawn(this, true, MON_SHAMBLER.monsterid); }
 #endif // SVQC
 
 #ifdef SVQC
@@ -251,7 +226,7 @@ METHOD(Shambler, mr_death, bool(Shambler this, entity actor))
     return true;
 }
 #endif
-#ifndef MENUQC
+#ifdef GAMEQC
 METHOD(Shambler, mr_anim, bool(Shambler this, entity actor))
 {
     TC(Shambler, this);
@@ -298,5 +273,3 @@ METHOD(Shambler, mr_precache, bool(Shambler this))
     return true;
 }
 #endif
-
-#endif
diff --git a/qcsrc/common/monsters/monster/shambler.qh b/qcsrc/common/monsters/monster/shambler.qh
new file mode 100644 (file)
index 0000000..f7a3ce3
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_SHAMBLER, M_Model("shambler.mdl"));
+#endif
+
+CLASS(Shambler, Monster)
+    ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
+    ATTRIB(Shambler, mins, vector, '-41 -41 -31');
+    ATTRIB(Shambler, maxs, vector, '41 41 65');
+#ifdef GAMEQC
+    ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER);
+#endif
+    ATTRIB(Shambler, netname, string, "shambler");
+    ATTRIB(Shambler, monster_name, string, _("Shambler"));
+ENDCLASS(Shambler)
+
+REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) {
+#ifdef GAMEQC
+    this.mr_precache(this);
+#endif
+}
index 9338657082f25eb168607ac338858783607f99f1..a122865321f0a5a69897245c38f69b9df00a4b11 100644 (file)
@@ -1,40 +1,4 @@
-#ifndef SPIDER_H
-#define SPIDER_H
-
-#ifndef MENUQC
-MODEL(MON_SPIDER, M_Model("spider.dpm"));
-#endif
-
-CLASS(Spider, Monster)
-    ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE);
-    ATTRIB(Spider, mins, vector, '-18 -18 -25');
-    ATTRIB(Spider, maxs, vector, '18 18 30');
-#ifndef MENUQC
-    ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER);
-#endif
-    ATTRIB(Spider, netname, string, "spider");
-    ATTRIB(Spider, monster_name, string, _("Spider"));
-ENDCLASS(Spider)
-
-REGISTER_MONSTER(SPIDER, NEW(Spider)) {
-#ifndef MENUQC
-    this.mr_precache(this);
-#endif
-}
-
-#include <common/weapons/all.qh>
-
-CLASS(SpiderAttack, PortoLaunch)
-/* flags     */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
-/* impulse   */ ATTRIB(SpiderAttack, impulse, int, 9);
-/* refname   */ ATTRIB(SpiderAttack, netname, string, "spider");
-/* wepname   */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack"));
-ENDCLASS(SpiderAttack)
-REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack));
-
-#endif
-
-#ifdef IMPLEMENTATION
+#include "spider.qh"
 
 #ifdef SVQC
 
@@ -106,7 +70,7 @@ METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, .entity
                        actor.anim_finished = time + 1;
                }
         if (isPlayer) actor.enemy = Monster_FindTarget(actor);
-        W_SetupShot_Dir(actor, v_forward, false, 0, SND_SpiderAttack_FIRE, CH_WEAPON_B, 0);
+        W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_SpiderAttack_FIRE, CH_WEAPON_B, 0);
        if (!isPlayer) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
                M_Spider_Attack_Web(actor);
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
@@ -143,10 +107,12 @@ void M_Spider_Attack_Web_Explode(entity this)
                Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1);
                RadiusDamage(this, this.realowner, 0, 0, 25, NULL, NULL, 25, this.projectiledeathtype, NULL);
 
-               for(entity e = findradius(this.origin, 25); e; e = e.chain) if(e != this) if(e.takedamage && !IS_DEAD(e)) if(e.health > 0) if(e.monsterid != MON_SPIDER.monsterid)
-                       e.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime);
+               FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && it.monsterid != MON_SPIDER.monsterid,
+               {
+                       it.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime);
+               });
 
-               remove(this);
+               delete(this);
        }
 }
 
@@ -155,9 +121,9 @@ void M_Spider_Attack_Web_Explode_use(entity this, entity actor, entity trigger)
        M_Spider_Attack_Web_Explode(this);
 }
 
-void M_Spider_Attack_Web_Touch(entity this)
+void M_Spider_Attack_Web_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        M_Spider_Attack_Web_Explode(this);
 }
@@ -183,7 +149,7 @@ void M_Spider_Attack_Web(entity this)
 
        //proj.glow_size = 50;
        //proj.glow_color = 45;
-       proj.movetype = MOVETYPE_BOUNCE;
+       set_movetype(proj, MOVETYPE_BOUNCE);
        W_SetupProjVelocity_Explicit(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, false);
        settouch(proj, M_Spider_Attack_Web_Touch);
        setsize(proj, '-4 -4 -4', '4 4 4');
@@ -192,7 +158,10 @@ void M_Spider_Attack_Web(entity this)
        proj.health = 500;
        proj.event_damage = func_null;
        proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, proj);
 
        proj.bouncefactor = 0.3;
        proj.bouncestop = 0.05;
@@ -222,7 +191,7 @@ bool M_Spider_Attack(int attack_type, entity actor, entity targ)
        return false;
 }
 
-spawnfunc(monster_spider) { Monster_Spawn(this, MON_SPIDER.monsterid); }
+spawnfunc(monster_spider) { Monster_Spawn(this, true, MON_SPIDER.monsterid); }
 #endif // SVQC
 
 #ifdef SVQC
@@ -246,7 +215,7 @@ METHOD(Spider, mr_death, bool(Spider this, entity actor))
     return true;
 }
 #endif
-#ifndef MENUQC
+#ifdef GAMEQC
 METHOD(Spider, mr_anim, bool(Spider this, entity actor))
 {
     TC(Spider, this);
@@ -282,5 +251,3 @@ METHOD(Spider, mr_precache, bool(Spider this))
     return true;
 }
 #endif
-
-#endif
diff --git a/qcsrc/common/monsters/monster/spider.qh b/qcsrc/common/monsters/monster/spider.qh
new file mode 100644 (file)
index 0000000..c54eb3a
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_SPIDER, M_Model("spider.dpm"));
+#endif
+
+CLASS(Spider, Monster)
+    ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE);
+    ATTRIB(Spider, mins, vector, '-18 -18 -25');
+    ATTRIB(Spider, maxs, vector, '18 18 30');
+#ifdef GAMEQC
+    ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER);
+#endif
+    ATTRIB(Spider, netname, string, "spider");
+    ATTRIB(Spider, monster_name, string, _("Spider"));
+ENDCLASS(Spider)
+
+REGISTER_MONSTER(SPIDER, NEW(Spider)) {
+#ifdef GAMEQC
+    this.mr_precache(this);
+#endif
+}
+
+#include <common/weapons/_all.qh>
+
+CLASS(SpiderAttack, PortoLaunch)
+/* flags     */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(SpiderAttack, impulse, int, 9);
+/* refname   */ ATTRIB(SpiderAttack, netname, string, "spider");
+/* wepname   */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack"));
+ENDCLASS(SpiderAttack)
+REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack));
index 001b2e24d84a5bf3216d97282f1a241669999c82..cd53ff26f75aa2d483de2e55730fd56cce824962 100644 (file)
@@ -1,40 +1,4 @@
-#ifndef WYVERN_H
-#define WYVERN_H
-
-#ifndef MENUQC
-MODEL(MON_WYVERN, M_Model("wizard.mdl"));
-#endif
-
-CLASS(Wyvern, Monster)
-    ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE);
-    ATTRIB(Wyvern, mins, vector, '-20 -20 -58');
-    ATTRIB(Wyvern, maxs, vector, '20 20 20');
-#ifndef MENUQC
-    ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN);
-#endif
-    ATTRIB(Wyvern, netname, string, "wyvern");
-    ATTRIB(Wyvern, monster_name, string, _("Wyvern"));
-ENDCLASS(Wyvern)
-
-REGISTER_MONSTER(WYVERN, NEW(Wyvern)) {
-#ifndef MENUQC
-    this.mr_precache(this);
-#endif
-}
-
-#include <common/weapons/all.qh>
-
-CLASS(WyvernAttack, PortoLaunch)
-/* flags     */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
-/* impulse   */ ATTRIB(WyvernAttack, impulse, int, 9);
-/* refname   */ ATTRIB(WyvernAttack, netname, string, "wyvern");
-/* wepname   */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack"));
-ENDCLASS(WyvernAttack)
-REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack));
-
-#endif
-
-#ifdef IMPLEMENTATION
+#include "wyvern.qh"
 
 #ifdef SVQC
 
@@ -46,7 +10,7 @@ float autocvar_g_monster_wyvern_attack_fireball_radius;
 float autocvar_g_monster_wyvern_attack_fireball_speed;
 
 void M_Wyvern_Attack_Fireball_Explode(entity this);
-void M_Wyvern_Attack_Fireball_Touch(entity this);
+void M_Wyvern_Attack_Fireball_Touch(entity this, entity toucher);
 
 SOUND(WyvernAttack_FIRE, W_Sound("electro_fire"));
 METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity weaponentity, int fire))
@@ -54,7 +18,7 @@ METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity
     TC(WyvernAttack, thiswep);
     if (fire & 1)
     if (time > actor.attack_finished_single[0] || weapon_prepareattack(thiswep, actor, weaponentity, false, 1.2)) {
-        if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, SND_WyvernAttack_FIRE, CH_WEAPON_B, 0);
+        if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_WyvernAttack_FIRE, CH_WEAPON_B, 0);
                if (IS_MONSTER(actor)) {
                        actor.attack_finished_single[0] = time + 1.2;
                        actor.anim_finished = time + 1.2;
@@ -64,11 +28,13 @@ METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity
                entity missile = spawn();
                missile.owner = missile.realowner = actor;
                missile.solid = SOLID_TRIGGER;
-               missile.movetype = MOVETYPE_FLYMISSILE;
+               set_movetype(missile, MOVETYPE_FLYMISSILE);
                missile.projectiledeathtype = DEATH_MONSTER_WYVERN.m_id;
                setsize(missile, '-6 -6 -6', '6 6 6');
                setorigin(missile, actor.origin + actor.view_ofs + v_forward * 14);
                missile.flags = FL_PROJECTILE;
+        IL_PUSH(g_projectiles, missile);
+        IL_PUSH(g_bot_dodge, missile);
                missile.velocity = w_shotdir * (autocvar_g_monster_wyvern_attack_fireball_speed);
                missile.avelocity = '300 300 300';
                missile.nextthink = time + 5;
@@ -107,18 +73,17 @@ void M_Wyvern_Attack_Fireball_Explode(entity this)
 
        RadiusDamage(this, own, autocvar_g_monster_wyvern_attack_fireball_damage, autocvar_g_monster_wyvern_attack_fireball_edgedamage, autocvar_g_monster_wyvern_attack_fireball_force, NULL, NULL, autocvar_g_monster_wyvern_attack_fireball_radius, this.projectiledeathtype, NULL);
 
-       FOREACH_ENTITY_FLOAT(takedamage, DAMAGE_AIM,
+       FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM,
        {
-               if(vdist(it.origin - this.origin, <=, autocvar_g_monster_wyvern_attack_fireball_radius))
-                       Fire_AddDamage(it, own, 5 * MONSTER_SKILLMOD(own), autocvar_g_monster_wyvern_attack_fireball_damagetime, this.projectiledeathtype);
+               Fire_AddDamage(it, own, 5 * MONSTER_SKILLMOD(own), autocvar_g_monster_wyvern_attack_fireball_damagetime, this.projectiledeathtype);
        });
 
-       remove(this);
+       delete(this);
 }
 
-void M_Wyvern_Attack_Fireball_Touch(entity this)
+void M_Wyvern_Attack_Fireball_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        M_Wyvern_Attack_Fireball_Explode(this);
 }
@@ -141,7 +106,7 @@ bool M_Wyvern_Attack(int attack_type, entity actor, entity targ)
        return false;
 }
 
-spawnfunc(monster_wyvern) { Monster_Spawn(this, MON_WYVERN.monsterid); }
+spawnfunc(monster_wyvern) { Monster_Spawn(this, true, MON_WYVERN.monsterid); }
 #endif // SVQC
 
 #ifdef SVQC
@@ -169,7 +134,7 @@ METHOD(Wyvern, mr_death, bool(Wyvern this, entity actor))
     return true;
 }
 #endif
-#ifndef MENUQC
+#ifdef GAMEQC
 METHOD(Wyvern, mr_anim, bool(Wyvern this, entity actor))
 {
     TC(Wyvern, this);
@@ -206,5 +171,3 @@ METHOD(Wyvern, mr_precache, bool(Wyvern this))
     return true;
 }
 #endif
-
-#endif
diff --git a/qcsrc/common/monsters/monster/wyvern.qh b/qcsrc/common/monsters/monster/wyvern.qh
new file mode 100644 (file)
index 0000000..0af84c1
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_WYVERN, M_Model("wizard.mdl"));
+#endif
+
+CLASS(Wyvern, Monster)
+    ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE);
+    ATTRIB(Wyvern, mins, vector, '-20 -20 -58');
+    ATTRIB(Wyvern, maxs, vector, '20 20 20');
+#ifdef GAMEQC
+    ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN);
+#endif
+    ATTRIB(Wyvern, netname, string, "wyvern");
+    ATTRIB(Wyvern, monster_name, string, _("Wyvern"));
+ENDCLASS(Wyvern)
+
+REGISTER_MONSTER(WYVERN, NEW(Wyvern)) {
+#ifdef GAMEQC
+    this.mr_precache(this);
+#endif
+}
+
+#include <common/weapons/_all.qh>
+
+CLASS(WyvernAttack, PortoLaunch)
+/* flags     */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
+/* impulse   */ ATTRIB(WyvernAttack, impulse, int, 9);
+/* refname   */ ATTRIB(WyvernAttack, netname, string, "wyvern");
+/* wepname   */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack"));
+ENDCLASS(WyvernAttack)
+REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack));
index 1eafd4bfd103897ef3993192221ec9a1f9c12237..8bbb300c76b6fc12cfae5ee55309c242d75a8115 100644 (file)
@@ -1,30 +1,4 @@
-#ifndef ZOMBIE_H
-#define ZOMBIE_H
-
-#ifndef MENUQC
-MODEL(MON_ZOMBIE, M_Model("zombie.dpm"));
-#endif
-
-CLASS(Zombie, Monster)
-    ATTRIB(Zombie, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RIDE);
-    ATTRIB(Zombie, mins, vector, '-18 -18 -25');
-    ATTRIB(Zombie, maxs, vector, '18 18 47');
-#ifndef MENUQC
-    ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE);
-#endif
-    ATTRIB(Zombie, netname, string, "zombie");
-    ATTRIB(Zombie, monster_name, string, _("Zombie"));
-ENDCLASS(Zombie)
-
-REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) {
-#ifndef MENUQC
-    this.mr_precache(this);
-#endif
-}
-
-#endif
-
-#ifdef IMPLEMENTATION
+#include "zombie.qh"
 
 #ifdef SVQC
 float autocvar_g_monster_zombie_health;
@@ -75,18 +49,18 @@ const float zombie_anim_spawn                               = 30;
 
 .vector moveto;
 
-void M_Zombie_Attack_Leap_Touch(entity this)
+void M_Zombie_Attack_Leap_Touch(entity this, entity toucher)
 {
        if (this.health <= 0)
                return;
 
        vector angles_face;
 
-       if(other.takedamage)
+       if(toucher.takedamage)
        {
                angles_face = vectoangles(this.moveto - this.origin);
                angles_face = normalize(angles_face) * (autocvar_g_monster_zombie_attack_leap_force);
-               Damage(other, this, this, (autocvar_g_monster_zombie_attack_leap_damage) * MONSTER_SKILLMOD(this), DEATH_MONSTER_ZOMBIE_JUMP.m_id, other.origin, angles_face);
+               Damage(toucher, this, this, (autocvar_g_monster_zombie_attack_leap_damage) * MONSTER_SKILLMOD(this), DEATH_MONSTER_ZOMBIE_JUMP.m_id, toucher.origin, angles_face);
                settouch(this, Monster_Touch); // instantly turn it off to stop damage spam
                this.state = 0;
        }
@@ -151,7 +125,7 @@ bool M_Zombie_Attack(int attack_type, entity actor, entity targ)
        return false;
 }
 
-spawnfunc(monster_zombie) { Monster_Spawn(this, MON_ZOMBIE.monsterid); }
+spawnfunc(monster_zombie) { Monster_Spawn(this, true, MON_ZOMBIE.monsterid); }
 #endif // SVQC
 
 #ifdef SVQC
@@ -180,7 +154,7 @@ METHOD(Zombie, mr_death, bool(Zombie this, entity actor))
     return true;
 }
 #endif
-#ifndef MENUQC
+#ifdef GAMEQC
 METHOD(Zombie, mr_anim, bool(Zombie this, entity actor))
 {
     TC(Zombie, this);
@@ -214,6 +188,8 @@ METHOD(Zombie, mr_setup, bool(Zombie this, entity actor))
     if(actor.spawnflags & MONSTERFLAG_NORESPAWN)
         actor.spawnflags &= ~MONSTERFLAG_NORESPAWN; // zombies always respawn
 
+    actor.spawnflags &= ~MONSTERFLAG_APPEAR; // once it's appeared, it will respawn quickly, we don't want it to appear
+
     actor.spawnflags |= MONSTER_RESPAWN_DEATHPOINT;
 
     actor.monster_loot = spawnfunc_item_health_medium;
@@ -234,5 +210,3 @@ METHOD(Zombie, mr_precache, bool(Zombie this))
     return true;
 }
 #endif
-
-#endif
diff --git a/qcsrc/common/monsters/monster/zombie.qh b/qcsrc/common/monsters/monster/zombie.qh
new file mode 100644 (file)
index 0000000..d3c94cd
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_ZOMBIE, M_Model("zombie.dpm"));
+#endif
+
+CLASS(Zombie, Monster)
+    ATTRIB(Zombie, spawnflags, int, MONSTER_TYPE_UNDEAD | MON_FLAG_MELEE | MON_FLAG_RIDE);
+    ATTRIB(Zombie, mins, vector, '-18 -18 -25');
+    ATTRIB(Zombie, maxs, vector, '18 18 47');
+#ifdef GAMEQC
+    ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE);
+#endif
+    ATTRIB(Zombie, netname, string, "zombie");
+    ATTRIB(Zombie, monster_name, string, _("Zombie"));
+ENDCLASS(Zombie)
+
+REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) {
+#ifdef GAMEQC
+    this.mr_precache(this);
+#endif
+}
diff --git a/qcsrc/common/monsters/spawn.qc b/qcsrc/common/monsters/spawn.qc
deleted file mode 100644 (file)
index 23fc845..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-    #include "../util.qh"
-    #include "all.qh"
-    #include "sv_monsters.qh"
-    #include "spawn.qh"
-    #include <server/autocvars.qh>
-    #include <server/defs.qh>
-#endif
-entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag)
-{
-       float i;
-       entity e = spawn();
-
-       e.spawnflags = MONSTERFLAG_SPAWNED;
-
-       if(!respwn) { e.spawnflags |= MONSTERFLAG_NORESPAWN; }
-       if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; }
-
-       setorigin(e, orig);
-
-       if(monster == "random")
-       {
-               RandomSelection_Init();
-               for(i = MON_FIRST; i <= MON_LAST; ++i)
-                       RandomSelection_Add(NULL, i, string_null, 1, 1);
-
-           monster_id = RandomSelection_chosen_float;
-       }
-       else if(monster != "")
-       {
-               float found = 0;
-               entity mon;
-               for(i = MON_FIRST; i <= MON_LAST; ++i)
-               {
-                       mon = get_monsterinfo(i);
-                       if(mon.netname == monster)
-                       {
-                               found = true;
-                               monster_id = mon.monsterid; // we have the monster, old monster id is no longer required
-                               break;
-                       }
-               }
-               if(!found)
-                       monster_id = ((monster_id > 0) ? monster_id : MON_FIRST);
-       }
-
-       e.realowner = spawnedby;
-
-       if(moveflag)
-               e.monster_moveflags = moveflag;
-
-       if(IS_PLAYER(spawnedby))
-       {
-               if(teamplay && autocvar_g_monsters_teams)
-                       e.team = spawnedby.team; // colors handled in spawn code
-
-               if(autocvar_g_monsters_owners)
-                       e.monster_follow = own; // using .owner makes the monster non-solid for its master
-
-               e.angles_y = spawnedby.angles_y;
-       }
-
-       // Monster_Spawn checks if monster is valid
-       Monster_Spawn(e, monster_id);
-
-       return e;
-}
diff --git a/qcsrc/common/monsters/spawn.qh b/qcsrc/common/monsters/spawn.qh
deleted file mode 100644 (file)
index 2ebdcc5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef SPAWN_H
-#define SPAWN_H
-
-entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag);
-#endif
diff --git a/qcsrc/common/monsters/spawner.qc b/qcsrc/common/monsters/spawner.qc
new file mode 100644 (file)
index 0000000..0b34d13
--- /dev/null
@@ -0,0 +1,26 @@
+#include "sv_spawn.qh"
+
+void spawner_use(entity this, entity actor, entity trigger)
+{
+       int moncount = 0;
+       IL_EACH(g_monsters, it.realowner == this,
+       {
+               ++moncount;
+       });
+
+       if(moncount >= this.count)
+               return;
+
+       entity e = spawn();
+       e.noalign = this.noalign;
+       e.angles = this.angles;
+       e.monster_skill = this.monster_skill;
+       e = spawnmonster(e, this.spawnmob, 0, this, this, this.origin, false, true, this.monster_moveflags);
+}
+
+spawnfunc(monster_spawner)
+{
+       if(!autocvar_g_monsters || !this.spawnmob || this.spawnmob == "") { delete(this); return; }
+
+       this.use = spawner_use;
+}
index c0a15e97b141f0c168960d30d9f9f8c25a7cb37e..f0f789af3762ffcf1a9ebdf8e1b2697c945b9599 100644 (file)
@@ -1,29 +1,27 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-    #include <lib/warpzone/common.qh>
-    #include "../constants.qh"
-    #include "../teams.qh"
-    #include "../util.qh"
-    #include "all.qh"
-    #include "sv_monsters.qh"
-       #include "../physics/movelib.qh"
-    #include "../weapons/all.qh"
-    #include <server/autocvars.qh>
-    #include <server/defs.qh>
-    #include "../deathtypes/all.qh"
-    #include <server/mutators/all.qh>
-       #include <server/steerlib.qh>
-       #include "../turrets/sv_turrets.qh"
-       #include "../turrets/util.qh"
-    #include "../vehicles/all.qh"
-    #include <server/campaign.qh>
-    #include <server/command/common.qh>
-    #include <server/command/cmd.qh>
-       #include "../triggers/triggers.qh"
-    #include <lib/csqcmodel/sv_model.qh>
-    #include <server/round_handler.qh>
-#endif
+#include "sv_monsters.qh"
+
+#include <server/g_subs.qh>
+#include <lib/warpzone/common.qh>
+#include "../constants.qh"
+#include "../teams.qh"
+#include "../util.qh"
+#include "all.qh"
+#include "../physics/movelib.qh"
+#include "../weapons/_mod.qh"
+#include <server/autocvars.qh>
+#include <server/defs.qh>
+#include "../deathtypes/all.qh"
+#include <server/mutators/_mod.qh>
+#include <server/steerlib.qh>
+#include "../turrets/sv_turrets.qh"
+#include "../turrets/util.qh"
+#include "../vehicles/all.qh"
+#include <server/campaign.qh>
+#include <server/command/_mod.qh>
+#include "../triggers/triggers.qh"
+#include <lib/csqcmodel/sv_model.qh>
+#include <server/round_handler.qh>
+#include <server/weapons/_mod.qh>
 
 void monsters_setstatus(entity this)
 {
@@ -36,7 +34,7 @@ void monster_dropitem(entity this, entity attacker)
        if(!this.candrop || !this.monster_loot)
                return;
 
-       vector org = this.origin + ((this.mins + this.maxs) * 0.5);
+       vector org = CENTER_OR_VIEWOFS(this);
        entity e = new(droppedweapon); // use weapon handling to remove it on touch
        e.spawnfunc_checked = true;
 
@@ -50,7 +48,7 @@ void monster_dropitem(entity this, entity attacker)
                e.noalign = true;
                e.monster_loot(e);
                e.gravity = 1;
-               e.movetype = MOVETYPE_TOSS;
+               set_movetype(e, MOVETYPE_TOSS);
                e.reset = SUB_Remove;
                setorigin(e, org);
                e.velocity = randomvec() * 175 + '0 0 325';
@@ -82,7 +80,7 @@ bool Monster_ValidTarget(entity this, entity targ)
 
        if((targ == this)
        || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
-       || (IS_VEHICLE(targ) && !((get_monsterinfo(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
+       || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
        || (time < game_starttime) // monsters do nothing before match has started
        || (targ.takedamage == DAMAGE_NO)
        || (targ.items & IT_INVISIBILITY)
@@ -94,24 +92,23 @@ bool Monster_ValidTarget(entity this, entity targ)
        || (SAME_TEAM(targ, this))
        || (STAT(FROZEN, targ))
        || (targ.alpha != 0 && targ.alpha < 0.5)
+       || (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ))
        )
        {
                // if any of the above checks fail, target is not valid
                return false;
        }
 
-       traceline(this.origin + this.view_ofs, targ.origin, 0, this);
+       traceline(this.origin + this.view_ofs, targ.origin, MOVE_NOMONSTERS, this);
 
-       if((trace_fraction < 1) && (trace_ent != targ))
-               return false;
+       if(trace_fraction < 1)
+               return false; // solid
 
        if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
        if(this.enemy != targ)
        {
-               float dot;
-
                makevectors (this.angles);
-               dot = normalize (targ.origin - this.origin) * v_forward;
+               float dot = normalize (targ.origin - this.origin) * v_forward;
 
                if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
        }
@@ -119,74 +116,71 @@ bool Monster_ValidTarget(entity this, entity targ)
        return true; // this target is valid!
 }
 
-entity Monster_FindTarget(entity mon)
+entity Monster_FindTarget(entity this)
 {
-       if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator
+       if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return this.enemy; } // Handled by a mutator
 
-       entity head, closest_target = NULL;
-       head = findradius(mon.origin, mon.target_range);
+       entity closest_target = NULL;
+       vector my_center = CENTER_OR_VIEWOFS(this);
 
-       while(head) // find the closest acceptable target to pass to
+       // find the closest acceptable target to pass to
+       FOREACH_ENTITY_RADIUS(this.origin, this.target_range, it.monster_attack,
        {
-               if(head.monster_attack)
-               if(Monster_ValidTarget(mon, head))
+               if(Monster_ValidTarget(this, it))
                {
                        // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
-                       vector head_center = CENTER_OR_VIEWOFS(head);
-                       vector ent_center = CENTER_OR_VIEWOFS(mon);
+                       vector targ_center = CENTER_OR_VIEWOFS(it);
 
                        if(closest_target)
                        {
                                vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
-                               if(vlen2(ent_center - head_center) < vlen2(ent_center - closest_target_center))
-                                       { closest_target = head; }
+                               if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
+                                       { closest_target = it; }
                        }
-                       else { closest_target = head; }
+                       else { closest_target = it; }
                }
-
-               head = head.chain;
-       }
+       });
 
        return closest_target;
 }
 
-void monster_setupcolors(entity mon)
+void monster_setupcolors(entity this)
 {
-       if(IS_PLAYER(mon.realowner))
-               mon.colormap = mon.realowner.colormap;
-       else if(teamplay && mon.team)
-               mon.colormap = 1024 + (mon.team - 1) * 17;
+       if(IS_PLAYER(this.realowner))
+               this.colormap = this.realowner.colormap;
+       else if(teamplay && this.team)
+               this.colormap = 1024 + (this.team - 1) * 17;
        else
        {
-               if(mon.monster_skill <= MONSTER_SKILL_EASY)
-                       mon.colormap = 1029;
-               else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM)
-                       mon.colormap = 1027;
-               else if(mon.monster_skill <= MONSTER_SKILL_HARD)
-                       mon.colormap = 1038;
-               else if(mon.monster_skill <= MONSTER_SKILL_INSANE)
-                       mon.colormap = 1028;
-               else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE)
-                       mon.colormap = 1032;
+               if(this.monster_skill <= MONSTER_SKILL_EASY)
+                       this.colormap = 1029;
+               else if(this.monster_skill <= MONSTER_SKILL_MEDIUM)
+                       this.colormap = 1027;
+               else if(this.monster_skill <= MONSTER_SKILL_HARD)
+                       this.colormap = 1038;
+               else if(this.monster_skill <= MONSTER_SKILL_INSANE)
+                       this.colormap = 1028;
+               else if(this.monster_skill <= MONSTER_SKILL_NIGHTMARE)
+                       this.colormap = 1032;
                else
-                       mon.colormap = 1024;
+                       this.colormap = 1024;
        }
 }
 
-void monster_changeteam(entity ent, float newteam)
+void monster_changeteam(entity this, int newteam)
 {
        if(!teamplay) { return; }
 
-       ent.team = newteam;
-       ent.monster_attack = true; // new team, activate attacking
-       monster_setupcolors(ent);
+       this.team = newteam;
+       this.monster_attack = true; // new team, activate attacking
+       monster_setupcolors(this);
 
-       if(ent.sprite)
+       if(this.sprite)
        {
-               WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
+               WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
 
-               ent.sprite.team = newteam;
-               ent.sprite.SendFlags |= 1;
+               this.sprite.team = newteam;
+               this.sprite.SendFlags |= 1;
        }
 }
 
@@ -250,7 +244,7 @@ void Monster_Sound_Precache(string f)
        {
                if(tokenize_console(s) != 3)
                {
-                       LOG_TRACE("Invalid sound info line: ", s, "\n");
+                       LOG_TRACE("Invalid sound info line: ", s);
                        continue;
                }
                PrecacheGlobalSound(strcat(argv(1), " ", argv(2)));
@@ -299,13 +293,12 @@ void Monster_Sounds_Clear(entity this)
 
 bool Monster_Sounds_Load(entity this, string f, int first)
 {
-       float fh;
        string s;
        var .string field;
-       fh = fopen(f, FILE_READ);
+       float fh = fopen(f, FILE_READ);
        if(fh < 0)
        {
-               LOG_TRACE("Monster sound file not found: ", f, "\n");
+               LOG_TRACE("Monster sound file not found: ", f);
                return false;
        }
        while((s = fgets(fh)))
@@ -334,14 +327,14 @@ void Monster_Sounds_Update(entity this)
                Monster_Sounds_Load(this, get_monster_model_datafilename(this.model, 0, "sounds"), 0);
 }
 
-void Monster_Sound(entity this, .string samplefield, float sound_delay, float delaytoo, float chan)
+void Monster_Sound(entity this, .string samplefield, float sound_delay, bool delaytoo, float chan)
 {
        if(!autocvar_g_monsters_sounds) { return; }
 
        if(delaytoo)
        if(time < this.msound_delay)
                return; // too early
-       GlobalSound_string(this, this.(samplefield), chan, VOICETYPE_PLAYERSOUND);
+       GlobalSound_string(this, this.(samplefield), chan, VOL_BASE, VOICETYPE_PLAYERSOUND);
 
        this.msound_delay = time + sound_delay;
 }
@@ -388,25 +381,25 @@ bool Monster_Attack_Leap_Check(entity this, vector vel)
        this.velocity = vel;
        tracetoss(this, this);
        this.velocity = old;
-       if (trace_ent != this.enemy)
+       if(trace_ent != this.enemy)
                return false;
 
        return true;
 }
 
-bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, vector vel, float animtime)
+bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity toucher) touchfunc, vector vel, float animtime)
 {
        if(!Monster_Attack_Leap_Check(this, vel))
                return false;
 
        setanim(this, anm, false, true, false);
 
-       if(this.animstate_endtime > time && (this.flags & FL_MONSTER))
+       if(this.animstate_endtime > time && IS_MONSTER(this))
                this.attack_finished_single[0] = this.anim_finished = this.animstate_endtime;
        else
                this.attack_finished_single[0] = this.anim_finished = time + animtime;
 
-       if(this.flags & FL_MONSTER)
+       if(IS_MONSTER(this))
                this.state = MONSTER_ATTACK_RANGED;
        settouch(this, touchfunc);
        this.origin_z += 1;
@@ -418,14 +411,14 @@ bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, v
 
 void Monster_Attack_Check(entity this, entity targ)
 {
-       if((this == NULL || targ == NULL)
+       if((!this || !targ)
        || (!this.monster_attackfunc)
        || (time < this.attack_finished_single[0])
        ) { return; }
 
        if(vdist(targ.origin - this.origin, <=, this.attack_range))
        {
-               bool attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ);
+               int attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ);
                if(attack_success == 1)
                        Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -434,7 +427,7 @@ void Monster_Attack_Check(entity this, entity targ)
 
        if(vdist(targ.origin - this.origin, >, this.attack_range))
        {
-               float attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ);
+               int attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ);
                if(attack_success == 1)
                        Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -462,19 +455,19 @@ void Monster_UpdateModel(entity this)
        this.anim_die2   = animfixfps(this, '9 1 0.01', '0 0 0');*/
 
        // then get the real values
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        mon.mr_anim(mon, this);
 }
 
-void Monster_Touch(entity this)
+void Monster_Touch(entity this, entity toucher)
 {
-       if(other == NULL) { return; }
+       if(toucher == NULL) { return; }
 
-       if(other.monster_attack)
-       if(this.enemy != other)
-       if(!IS_MONSTER(other))
-       if(Monster_ValidTarget(this, other))
-               this.enemy = other;
+       if(toucher.monster_attack)
+       if(this.enemy != toucher)
+       if(!IS_MONSTER(toucher))
+       if(Monster_ValidTarget(this, toucher))
+               this.enemy = toucher;
 }
 
 void Monster_Miniboss_Check(entity this)
@@ -509,7 +502,9 @@ bool Monster_Respawn_Check(entity this)
        return true;
 }
 
-void Monster_Respawn(entity this) { Monster_Spawn(this, this.monsterid); }
+void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterid); }
+
+.vector        pos1, pos2;
 
 void Monster_Dead_Fade(entity this)
 {
@@ -546,6 +541,7 @@ void Monster_Use(entity this, entity actor, entity trigger)
        if(Monster_ValidTarget(this, actor)) { this.enemy = actor; }
 }
 
+.float pass_distance;
 vector Monster_Move_Target(entity this, entity targ)
 {
        // enemy is always preferred target
@@ -555,7 +551,8 @@ vector Monster_Move_Target(entity this, entity targ)
                targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us)
                WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
 
-               if((this.enemy == NULL)
+               // cases where the enemy may have changed their state (don't need to check everything here)
+               if((!this.enemy)
                        || (IS_DEAD(this.enemy) || this.enemy.health < 1)
                        || (STAT(FROZEN, this.enemy))
                        || (this.enemy.flags & FL_NOTARGET)
@@ -688,11 +685,13 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra
        //this.angles = vectoangles(this.velocity);
 }
 
+.entity draggedby;
+.entity target2;
+
 void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
 {
-       if(this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
-
-       entity targ;
+       // update goal entity if lost
+       if(this.target2 && this.goalentity.targetname != this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
 
        if(STAT(FROZEN, this) == 2)
        {
@@ -766,19 +765,19 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                        }
 
 
-                       this.movetype = MOVETYPE_BOUNCE;
+                       set_movetype(this, MOVETYPE_BOUNCE);
                        //this.velocity_z = -200;
 
                        return;
                }
-               else if(this.movetype == MOVETYPE_BOUNCE)
+               else if(this.move_movetype == MOVETYPE_BOUNCE)
                {
                        this.angles_x = 0;
-                       this.movetype = MOVETYPE_WALK;
+                       set_movetype(this, MOVETYPE_WALK);
                }
        }
 
-       targ = this.goalentity;
+       entity targ = this.goalentity;
 
        if (MUTATOR_CALLHOOK(MonsterMove, this, runspeed, walkspeed, targ)
                || gameover
@@ -799,8 +798,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        runspeed = bound(0, M_ARGV(1, float) * MONSTER_SKILLMOD(this), runspeed * 2.5); // limit maxspeed to prevent craziness
        walkspeed = bound(0, M_ARGV(2, float) * MONSTER_SKILLMOD(this), walkspeed * 2.5); // limit maxspeed to prevent craziness
 
-       if(teamplay)
-       if(autocvar_g_monsters_teams)
+       if(teamplay && autocvar_g_monsters_teams)
        if(DIFF_TEAM(this.monster_follow, this))
                this.monster_follow = NULL;
 
@@ -852,13 +850,12 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
 
        if(vdist(this.origin - this.moveto, >, 100))
        {
-               float do_run = (this.enemy || this.monster_moveto);
+               bool do_run = (this.enemy || this.monster_moveto);
                if(IS_ONGROUND(this) || ((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
                        Monster_CalculateVelocity(this, this.moveto, this.origin, true, ((do_run) ? runspeed : walkspeed));
 
-               if(time > this.pain_finished) // TODO: use anim_finished instead!
+               if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!?
                if(!this.state)
-               if(time > this.anim_finished)
                if(vdist(this.velocity, >, 10))
                        setanim(this, ((do_run) ? this.anim_run : this.anim_walk), true, false, false);
                else
@@ -866,15 +863,14 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        }
        else
        {
-               entity e = find(NULL, targetname, this.target2);
+               entity e = this.goalentity; //find(NULL, targetname, this.target2);
                if(e.target2)
                        this.target2 = e.target2;
-               else if(e.target)
+               else if(e.target) // compatibility
                        this.target2 = e.target;
 
                movelib_brake_simple(this, stpspeed);
-               if(time > this.anim_finished)
-               if(time > this.pain_finished)
+               if(time > this.anim_finished && time > this.pain_finished)
                if(!this.state)
                if(vdist(this.velocity, <=, 30))
                        setanim(this, this.anim_idle, true, false, false);
@@ -900,16 +896,20 @@ void Monster_Remove(entity this)
        if(IS_CLIENT(this))
                return; // don't remove it?
 
-       .entity weaponentity = weaponentities[0];
        if(!this) { return; }
 
        if(!MUTATOR_CALLHOOK(MonsterRemove, this))
                Send_Effect(EFFECT_ITEM_PICKUP, this.origin, '0 0 0', 1);
 
-       if(this.(weaponentity)) { remove(this.(weaponentity)); }
-       if(this.iceblock) { remove(this.iceblock); }
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               if(this.(weaponentity))
+                       delete(this.(weaponentity));
+       }
+       if(this.iceblock) { delete(this.iceblock); }
        WaypointSprite_Kill(this.sprite);
-       remove(this);
+       delete(this);
 }
 
 void Monster_Dead_Think(entity this)
@@ -927,8 +927,7 @@ void Monster_Dead_Think(entity this)
 void Monster_Appear(entity this, entity actor, entity trigger)
 {
        this.enemy = actor;
-       this.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
-       Monster_Spawn(this, this.monsterid);
+       Monster_Spawn(this, false, this.monsterid);
 }
 
 bool Monster_Appear_Check(entity this, int monster_id)
@@ -1013,7 +1012,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
        this.takedamage         = DAMAGE_AIM;
        this.deadflag           = DEAD_DEAD;
        this.enemy                      = NULL;
-       this.movetype           = MOVETYPE_TOSS;
+       set_movetype(this, MOVETYPE_TOSS);
        this.moveto                     = this.origin;
        settouch(this, Monster_Touch); // reset incase monster was pouncing
        this.reset                      = func_null;
@@ -1026,7 +1025,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
 
        CSQCModel_UnlinkEntity(this);
 
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        mon.mr_death(mon, this);
 
        if(this.candrop && this.weapon)
@@ -1050,14 +1049,11 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL)
                return;
 
-       vector v;
-       float take, save;
+       vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage);
+       float take = v.x;
+       //float save = v.y;
 
-       v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage);
-       take = v_x;
-       save = v_y;
-
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        take = mon.mr_pain(mon, this, take, attacker, deathtype);
 
        if(take)
@@ -1122,23 +1118,19 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
                return;
        }
 
-       float reverse = false;
-       vector a, b;
-
        makevectors(this.angles);
-       a = this.origin + '0 0 16';
-       b = this.origin + '0 0 16' + v_forward * 32;
+       vector a = CENTER_OR_VIEWOFS(this);
+       vector b = CENTER_OR_VIEWOFS(this) + v_forward * 32;
 
        traceline(a, b, MOVE_NORMAL, this);
 
+       bool reverse = false;
        if(trace_fraction != 1.0)
-       {
                reverse = true;
-
-               if(trace_ent)
-               if(IS_PLAYER(trace_ent) && !(trace_ent.items & IT_STRENGTH))
-                       reverse = false;
-       }
+       if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid))
+               reverse = false;
+       if(trace_ent && IS_MONSTER(trace_ent))
+               reverse = true;
 
        // TODO: fix this... tracing is broken if the floor is thin
        /*
@@ -1158,8 +1150,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
 
        movelib_move_simple_gravity(this, v_forward, mspeed, 1);
 
-       if(time > this.pain_finished)
-       if(time > this.attack_finished_single[0])
+       if(time > this.pain_finished && time > this.attack_finished_single[0])
        if(vdist(this.velocity, >, 10))
                setanim(this, this.anim_walk, true, false, false);
        else
@@ -1206,16 +1197,15 @@ void Monster_Anim(entity this)
 void Monster_Think(entity this)
 {
        setthink(this, Monster_Think);
-       this.nextthink = this.ticrate;
+       this.nextthink = time + this.ticrate;
 
-       if(this.monster_lifetime)
-       if(time >= this.monster_lifetime)
+       if(this.monster_lifetime && time >= this.monster_lifetime)
        {
                Damage(this, this, this, this.health + this.max_health, DEATH_KILL.m_id, this.origin, this.origin);
                return;
        }
 
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        if(mon.mr_think(mon, this))
                Monster_Move(this, this.speed2, this.speed, this.stopspeed);
 
@@ -1285,7 +1275,7 @@ bool Monster_Spawn_Setup(entity this)
        return true;
 }
 
-bool Monster_Spawn(entity this, int mon_id)
+bool Monster_Spawn(entity this, bool check_appear, int mon_id)
 {
        // setup the basic required properties for a monster
        entity mon = Monsters_from(mon_id);
@@ -1293,7 +1283,10 @@ bool Monster_Spawn(entity this, int mon_id)
 
        if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
 
-       if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
+       if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
+               IL_PUSH(g_monsters, this);
+
+       if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
 
        if(!this.monster_skill)
                this.monster_skill = cvar("g_monsters_skill");
@@ -1314,16 +1307,20 @@ bool Monster_Spawn(entity this, int mon_id)
        this.flags                              = FL_MONSTER;
        this.classname                  = "monster";
        this.takedamage                 = DAMAGE_AIM;
+       if(!this.bot_attack)
+               IL_PUSH(g_bot_targets, this);
        this.bot_attack                 = true;
        this.iscreature                 = true;
        this.teleportable               = true;
+       if(!this.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, this);
        this.damagedbycontents  = true;
        this.monsterid                  = mon_id;
        this.event_damage               = Monster_Damage;
        settouch(this, Monster_Touch);
        this.use                                = Monster_Use;
        this.solid                              = SOLID_BBOX;
-       this.movetype                   = MOVETYPE_WALK;
+       set_movetype(this, MOVETYPE_WALK);
        this.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
        this.enemy                              = NULL;
        this.velocity                   = '0 0 0';
@@ -1339,13 +1336,13 @@ bool Monster_Spawn(entity this, int mon_id)
        this.oldtarget2                 = this.target2;
        this.pass_distance              = 0;
        this.deadflag                   = DEAD_NO;
-       this.noalign                    = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM));
        this.spawn_time                 = time;
        this.gravity                    = 1;
        this.monster_moveto             = '0 0 0';
        this.monster_face               = '0 0 0';
        this.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
 
+       if(!this.noalign) { this.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)); }
        if(!this.scale) { this.scale = 1; }
        if(autocvar_g_monsters_edit) { this.grab = 1; }
        if(autocvar_g_fullbrightplayers) { this.effects |= EF_FULLBRIGHT; }
@@ -1358,7 +1355,7 @@ bool Monster_Spawn(entity this, int mon_id)
        if(mon.spawnflags & MONSTER_TYPE_FLY)
        {
                this.flags |= FL_FLY;
-               this.movetype = MOVETYPE_FLY;
+               set_movetype(this, MOVETYPE_FLY);
        }
 
        if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
index f8501fc758e1b8184f67e9ff222beab022d647ac..b667373a0f7926496b4633dc16dbc29e69d2b167 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SV_MONSTERS_H
-#define SV_MONSTERS_H
+#pragma once
 
 // stats networking
 .int stat_monsters_killed;
@@ -73,11 +72,11 @@ void Monster_Remove(entity this);
 
 void monsters_setstatus(entity this);
 
-bool Monster_Spawn(entity this, int mon_id);
+bool Monster_Spawn(entity this, bool check_appear, int mon_id);
 
 void monster_setupcolors(entity this);
 
-void Monster_Touch(entity this);
+void Monster_Touch(entity this, entity toucher);
 
 void Monster_Move_2D(entity this, float mspeed, float allow_jumpoff);
 
@@ -85,7 +84,7 @@ void Monster_Delay(entity this, int repeat_count, float defer_amnt, void(entity)
 
 float Monster_Attack_Melee(entity this, entity targ, float damg, vector anim, float er, float animtime, int deathtype, float dostop);
 
-bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, vector vel, float animtime);
+bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity toucher) touchfunc, vector vel, float animtime);
 
 entity Monster_FindTarget(entity this);
 
@@ -113,5 +112,3 @@ ALLMONSTERSOUNDS
 #undef _MSOUND
 
 float GetMonsterSoundSampleField_notFound;
-
-#endif
diff --git a/qcsrc/common/monsters/sv_spawn.qc b/qcsrc/common/monsters/sv_spawn.qc
new file mode 100644 (file)
index 0000000..497dee8
--- /dev/null
@@ -0,0 +1,79 @@
+#include "sv_spawn.qh"
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+    #include "../util.qh"
+    #include "all.qh"
+    #include "sv_monsters.qh"
+    #include <server/autocvars.qh>
+    #include <server/defs.qh>
+#endif
+entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag)
+{
+       e.spawnflags = MONSTERFLAG_SPAWNED;
+
+       if(!respwn) { e.spawnflags |= MONSTERFLAG_NORESPAWN; }
+       //if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; }
+
+       setorigin(e, orig);
+
+       if(monster == "random")
+       {
+               RandomSelection_Init(); 
+               FOREACH(Monsters, it != MON_Null && !(it.spawnflags & MONSTER_TYPE_PASSIVE),
+               {
+                       RandomSelection_AddEnt(it, 1, 1);
+               });
+
+               monster_id = RandomSelection_chosen_ent.monsterid;
+       }
+       else if(monster != "")
+       {
+               bool found = false;
+               FOREACH(Monsters, it != MON_Null,
+               {
+                       if(it.netname == monster)
+                       {
+                               found = true;
+                               monster_id = it.monsterid; // we have the monster, old monster id is no longer required
+                               break;
+                       }
+               });
+
+               if(!found && !monster_id)
+               {
+                       if(removeifinvalid)
+                       {
+                               delete(e);
+                               return NULL; // no good
+                       }
+                       else
+                               monster_id = MON_FIRST;
+               }
+       }
+
+       e.realowner = spawnedby;
+
+       if(moveflag)
+               e.monster_moveflags = moveflag;
+
+       if(IS_PLAYER(spawnedby))
+       {
+               if(teamplay && autocvar_g_monsters_teams)
+                       e.team = spawnedby.team; // colors handled in spawn code
+
+               if(autocvar_g_monsters_owners)
+                       e.monster_follow = own; // using .owner makes the monster non-solid for its master
+
+               e.angles_y = spawnedby.angles_y;
+       }
+
+       // Monster_Spawn checks if monster is valid
+       if(!Monster_Spawn(e, false, monster_id))
+       {
+               delete(e);
+               return NULL; // remove even if told not to, as we didn't spawn any kind of monster
+       }
+
+       return e;
+}
diff --git a/qcsrc/common/monsters/sv_spawn.qh b/qcsrc/common/monsters/sv_spawn.qh
new file mode 100644 (file)
index 0000000..983676d
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag);
index 8220b2d19540a75e8f6a2014fce6b88672e3e408..29d6deabb9dd39935fa31ab4fd69a16243d61f6d 100644 (file)
@@ -1,2 +1,3 @@
 // generated file; do not modify
-#include <common/mutators/all.qc>
+
+#include <common/mutators/mutator/_mod.inc>
index 5d6ac5628b0f1ca6b1b9e3cc67736ea55090f6ba..b19e9c4d70ff03b759cb9f4bd56c7dcbbf3990f3 100644 (file)
@@ -1,2 +1,3 @@
 // generated file; do not modify
-#include <common/mutators/all.qh>
+
+#include <common/mutators/mutator/_mod.qh>
diff --git a/qcsrc/common/mutators/all.inc b/qcsrc/common/mutators/all.inc
deleted file mode 100644 (file)
index e99092c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "mutator/waypoints/module.inc"
-
-#include "mutator/itemstime.qc"
-#include "mutator/multijump/module.inc"
-#include "mutator/nades/module.inc"
-#include "mutator/superspec/module.inc"
-
-// completely self contained
-
-#include "mutator/bloodloss/module.inc"
-#include "mutator/breakablehook/module.inc"
-#include "mutator/buffs/module.inc"
-#include "mutator/bugrigs/module.inc"
-#include "mutator/campcheck/module.inc"
-#include "mutator/cloaked/module.inc"
-#include "mutator/damagetext/module.inc"
-#include "mutator/dodging/module.inc"
-#include "mutator/doublejump/module.inc"
-#include "mutator/hook/module.inc"
-#include "mutator/instagib/module.inc"
-#include "mutator/invincibleproj/module.inc"
-#include "mutator/melee_only/module.inc"
-#include "mutator/midair/module.inc"
-#include "mutator/new_toys/module.inc"
-#include "mutator/nix/module.inc"
-#include "mutator/overkill/module.inc"
-#include "mutator/physical_items/module.inc"
-#include "mutator/pinata/module.inc"
-#include "mutator/random_gravity/module.inc"
-#include "mutator/rocketflying/module.inc"
-#include "mutator/rocketminsta/module.inc"
-#include "mutator/running_guns/module.inc"
-#include "mutator/sandbox/module.inc"
-#include "mutator/spawn_near_teammate/module.inc"
-#include "mutator/touchexplode/module.inc"
-#include "mutator/vampirehook/module.inc"
-#include "mutator/vampire/module.inc"
-#include "mutator/weaponarena_random/module.inc"
diff --git a/qcsrc/common/mutators/all.qc b/qcsrc/common/mutators/all.qc
deleted file mode 100644 (file)
index f0fc719..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "all.qh"
-
-#define IMPLEMENTATION
-#include "all.inc"
-#undef IMPLEMENTATION
diff --git a/qcsrc/common/mutators/all.qh b/qcsrc/common/mutators/all.qh
deleted file mode 100644 (file)
index ceb4b51..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef MUTATORS_ALL_H
-#define MUTATORS_ALL_H
-
-#include "all.inc"
-
-#endif
index 4d92b5d40578c111b334ace23da86b39f560a60e..ee5dc4ab12b7ec974a2de6a508e0ac6086713eb4 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MUTATORS_BASE_H
-#define MUTATORS_BASE_H
+#pragma once
 
 const int CBC_ORDER_FIRST = 1;
 const int CBC_ORDER_LAST = 2;
@@ -20,7 +19,7 @@ CLASS(Callback, Object)
      *     return false;
      * }
      */
-    ATTRIB(Callback, cbc_func, bool(), func_null)
+    ATTRIB(Callback, cbc_func, bool());
     CONSTRUCTOR(Callback, bool() func) {
         CONSTRUCT(Callback);
         this.cbc_func = func;
@@ -32,9 +31,9 @@ ENDCLASS(Callback)
  */
 CLASS(CallbackChain, Object)
     CLASS(CallbackNode, Object)
-        ATTRIB(CallbackNode, cbc, Callback, NULL)
-        ATTRIB(CallbackNode, cbc_next, CallbackNode, NULL)
-        ATTRIB(CallbackNode, cbc_order, int, 0)
+        ATTRIB(CallbackNode, cbc, Callback);
+        ATTRIB(CallbackNode, cbc_next, CallbackNode);
+        ATTRIB(CallbackNode, cbc_order, int, 0);
         CONSTRUCTOR(CallbackNode, Callback it, int order) {
             CONSTRUCT(CallbackNode);
             this.cbc = it;
@@ -42,8 +41,8 @@ CLASS(CallbackChain, Object)
         }
     ENDCLASS(CallbackNode)
 
-    ATTRIB(CallbackChain, cbc_next, CallbackNode, NULL)
-    ATTRIB(CallbackChain, cbc_order, int, 0)
+    ATTRIB(CallbackChain, cbc_next, CallbackNode);
+    ATTRIB(CallbackChain, cbc_order, int, 0);
     CONSTRUCTOR(CallbackChain, string _name) {
         CONSTRUCT(CallbackChain);
         this.netname = _name;
@@ -120,9 +119,10 @@ ENDCLASS(CallbackChain)
 void RegisterHooks() {};
 void RegisterCallbacks() {};
 
-#define _MUTATOR_HOOKABLE(id, ...) CallbackChain HOOK_##id; bool __Mutator_Send_##id(__VA_ARGS__)
-#define MUTATOR_HOOKABLE(id, params) \
-    _MUTATOR_HOOKABLE(id, int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
+#define MUTATOR_HOOKABLE(id, params) _MUTATOR_HOOKABLE(id, params)
+#define _MUTATOR_HOOKABLE(id, params) \
+    CallbackChain HOOK_##id;  \
+    bool __Mutator_Send_##id(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
         params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
         params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
         bool ret = CallbackChain_Call(HOOK_##id); \
@@ -132,10 +132,12 @@ void RegisterCallbacks() {};
         return ret; \
     } \
     [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
+
+#define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
 #ifdef __STDC__
-    #define MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
+    #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
 #else
-    #define MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
+    #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
 #endif
 
 enum {
@@ -147,10 +149,10 @@ enum {
 USING(mutatorfunc_t, bool(int));
 
 CLASS(Mutator, Object)
-    ATTRIB(Mutator, m_id, int, 0)
-    ATTRIB(Mutator, m_name, string, string_null)
-    ATTRIB(Mutator, mutatorfunc, mutatorfunc_t, func_null)
-    ATTRIB(Mutator, mutatorcheck, bool(), func_null)
+    ATTRIB(Mutator, m_id, int, 0);
+    ATTRIB(Mutator, m_name, string);
+    ATTRIB(Mutator, mutatorfunc, mutatorfunc_t);
+    ATTRIB(Mutator, mutatorcheck, bool());
     CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) {
         CONSTRUCT(Mutator);
         this.m_name = _name;
@@ -165,7 +167,7 @@ bool Mutator_Add(Mutator mut);
 void Mutator_Remove(Mutator mut);
 bool mutator_log = false;
 
-#ifndef MENUQC
+#ifdef GAMEQC
 /** server mutators activate corresponding client mutators for all clients */
 REGISTER_NET_LINKED(Mutator)
 
@@ -200,7 +202,7 @@ NET_HANDLE(Mutator, bool isNew)
         WITH(bool, mutator_log, true, LAMBDA(
             FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
         ));
-        if (added > 1) LOG_WARNINGF("Added more than one mutator for %s\n", s);
+        if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
     }
 }
 #endif
@@ -224,7 +226,7 @@ bool Mutator_Add(Mutator mut)
     mutatorfunc_t func = mut.mutatorfunc;
     if (!func(MUTATOR_ADDING)) {
         // good
-        if (mutator_log) LOG_TRACEF("Mutator: added %s\n", mut.m_name);
+        if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name);
 #ifdef SVQC
         Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
 #endif
@@ -254,7 +256,7 @@ void Mutator_Remove(Mutator mut)
         // baaaaad
         error("Mutator_Remove: removing mutator failed");
     }
-    if (mutator_log) LOG_TRACEF("Mutator: removed %s\n", mut.m_name);
+    if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
 #ifdef SVQC
     Net_UnlinkEntity(mut);
 #endif
@@ -319,5 +321,3 @@ STATIC_INIT_LATE(Mutators) {
 } MACRO_END
 
 #include "events.qh"
-
-#endif
index 75fc8d31e1508a7037037c1db71e1b7db1c77e8a..6b16371a2cfa739a97325ad4a8f8c69a04c61657 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef COMMON_MUTATORS_EVENTS_H
-#define COMMON_MUTATORS_EVENTS_H
+#pragma once
 
 #define EV_NO_ARGS(i, o)
 
@@ -75,6 +74,7 @@ MUTATOR_HOOKABLE(WP_Format, EV_WP_Format);
  */
 #define EV_PlayerPhysics(i, o) \
     /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** ticrate*/ i(float, MUTATOR_ARGV_1_float) \
     /**/
 MUTATOR_HOOKABLE(PlayerPhysics, EV_PlayerPhysics);
 
@@ -92,6 +92,7 @@ MUTATOR_HOOKABLE(PlayerJump, EV_PlayerJump);
 #define EV_PM_Physics(i, o) \
     /** player */       i(entity, MUTATOR_ARGV_0_entity) \
     /** maxspeed_mod */ i(float, MUTATOR_ARGV_1_float) \
+    /** tick rate */    i(float, MUTATOR_ARGV_2_float) \
     /**/
 MUTATOR_HOOKABLE(PM_Physics, EV_PM_Physics);
 
@@ -102,5 +103,3 @@ MUTATOR_HOOKABLE(PM_Physics, EV_PM_Physics);
     /**/          o(string, MUTATOR_ARGV_1_string) \
     /**/
 MUTATOR_HOOKABLE(WeaponModel, EV_WeaponModel);
-
-#endif
index 30d67e34bcd8da1d5b971e5d4386584f40215b6b..0d6326fef0712b405f0ad88a856c27a61ef646ab 100644 (file)
@@ -1,2 +1,38 @@
 // generated file; do not modify
-#include <common/mutators/mutator/itemstime.qc>
+
+#include <common/mutators/mutator/bloodloss/_mod.inc>
+#include <common/mutators/mutator/breakablehook/_mod.inc>
+#include <common/mutators/mutator/buffs/_mod.inc>
+#include <common/mutators/mutator/bugrigs/_mod.inc>
+#include <common/mutators/mutator/campcheck/_mod.inc>
+#include <common/mutators/mutator/cloaked/_mod.inc>
+#include <common/mutators/mutator/damagetext/_mod.inc>
+#include <common/mutators/mutator/dodging/_mod.inc>
+#include <common/mutators/mutator/doublejump/_mod.inc>
+#include <common/mutators/mutator/globalforces/_mod.inc>
+#include <common/mutators/mutator/hook/_mod.inc>
+#include <common/mutators/mutator/instagib/_mod.inc>
+#include <common/mutators/mutator/invincibleproj/_mod.inc>
+#include <common/mutators/mutator/itemstime/_mod.inc>
+#include <common/mutators/mutator/melee_only/_mod.inc>
+#include <common/mutators/mutator/midair/_mod.inc>
+#include <common/mutators/mutator/multijump/_mod.inc>
+#include <common/mutators/mutator/nades/_mod.inc>
+#include <common/mutators/mutator/new_toys/_mod.inc>
+#include <common/mutators/mutator/nix/_mod.inc>
+#include <common/mutators/mutator/overkill/_mod.inc>
+#include <common/mutators/mutator/physical_items/_mod.inc>
+#include <common/mutators/mutator/pinata/_mod.inc>
+#include <common/mutators/mutator/random_gravity/_mod.inc>
+#include <common/mutators/mutator/rocketflying/_mod.inc>
+#include <common/mutators/mutator/rocketminsta/_mod.inc>
+#include <common/mutators/mutator/running_guns/_mod.inc>
+#include <common/mutators/mutator/sandbox/_mod.inc>
+#include <common/mutators/mutator/spawn_near_teammate/_mod.inc>
+#include <common/mutators/mutator/superspec/_mod.inc>
+#include <common/mutators/mutator/touchexplode/_mod.inc>
+#include <common/mutators/mutator/vampire/_mod.inc>
+#include <common/mutators/mutator/vampirehook/_mod.inc>
+#include <common/mutators/mutator/walljump/_mod.inc>
+#include <common/mutators/mutator/waypoints/_mod.inc>
+#include <common/mutators/mutator/weaponarena_random/_mod.inc>
index ac056ac68b127dd8ab36770bdbd3a885d14937f5..917dc6557ceb02b2415649bc6d9546e6a400c9c6 100644 (file)
@@ -1,2 +1,38 @@
 // generated file; do not modify
-#include <common/mutators/mutator/itemstime.qh>
+
+#include <common/mutators/mutator/bloodloss/_mod.qh>
+#include <common/mutators/mutator/breakablehook/_mod.qh>
+#include <common/mutators/mutator/buffs/_mod.qh>
+#include <common/mutators/mutator/bugrigs/_mod.qh>
+#include <common/mutators/mutator/campcheck/_mod.qh>
+#include <common/mutators/mutator/cloaked/_mod.qh>
+#include <common/mutators/mutator/damagetext/_mod.qh>
+#include <common/mutators/mutator/dodging/_mod.qh>
+#include <common/mutators/mutator/doublejump/_mod.qh>
+#include <common/mutators/mutator/globalforces/_mod.qh>
+#include <common/mutators/mutator/hook/_mod.qh>
+#include <common/mutators/mutator/instagib/_mod.qh>
+#include <common/mutators/mutator/invincibleproj/_mod.qh>
+#include <common/mutators/mutator/itemstime/_mod.qh>
+#include <common/mutators/mutator/melee_only/_mod.qh>
+#include <common/mutators/mutator/midair/_mod.qh>
+#include <common/mutators/mutator/multijump/_mod.qh>
+#include <common/mutators/mutator/nades/_mod.qh>
+#include <common/mutators/mutator/new_toys/_mod.qh>
+#include <common/mutators/mutator/nix/_mod.qh>
+#include <common/mutators/mutator/overkill/_mod.qh>
+#include <common/mutators/mutator/physical_items/_mod.qh>
+#include <common/mutators/mutator/pinata/_mod.qh>
+#include <common/mutators/mutator/random_gravity/_mod.qh>
+#include <common/mutators/mutator/rocketflying/_mod.qh>
+#include <common/mutators/mutator/rocketminsta/_mod.qh>
+#include <common/mutators/mutator/running_guns/_mod.qh>
+#include <common/mutators/mutator/sandbox/_mod.qh>
+#include <common/mutators/mutator/spawn_near_teammate/_mod.qh>
+#include <common/mutators/mutator/superspec/_mod.qh>
+#include <common/mutators/mutator/touchexplode/_mod.qh>
+#include <common/mutators/mutator/vampire/_mod.qh>
+#include <common/mutators/mutator/vampirehook/_mod.qh>
+#include <common/mutators/mutator/walljump/_mod.qh>
+#include <common/mutators/mutator/waypoints/_mod.qh>
+#include <common/mutators/mutator/weaponarena_random/_mod.qh>
index 16e6308acf9d39e466c350241b96d3ece7f990e6..768808db71f1cb41d9acf29090865a5c7d35a09c 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/bloodloss/bloodloss.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/bloodloss/sv_bloodloss.qc>
+#endif
index b1d45e2791b07d3e3d1b8572fed442328348f863..e6ad248c622d6aaad3d8f0e2b93744037e9c5577 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/bloodloss/bloodloss.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/bloodloss/sv_bloodloss.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc
deleted file mode 100644 (file)
index a335bf1..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss"));
-
-.float bloodloss_timer;
-
-MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(IS_PLAYER(player))
-       if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player))
-       {
-               PHYS_INPUT_BUTTON_CROUCH(player) = true;
-
-               if(time >= player.bloodloss_timer)
-               {
-                       if(player.vehicle)
-                               vehicles_exit(player.vehicle, VHEF_RELEASE);
-                       if(player.event_damage)
-                               player.event_damage(player, player, player, 1, DEATH_ROT.m_id, player.origin, '0 0 0');
-                       player.bloodloss_timer = time + 0.5 + random() * 0.5;
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(player.health <= autocvar_g_bloodloss)
-               return true;
-}
-
-MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bloodloss");
-}
-
-MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Blood loss");
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/bloodloss/module.inc b/qcsrc/common/mutators/mutator/bloodloss/module.inc
deleted file mode 100644 (file)
index d3f665a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "bloodloss.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc
new file mode 100644 (file)
index 0000000..61b0c06
--- /dev/null
@@ -0,0 +1,43 @@
+#include "sv_bloodloss.qh"
+
+REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss"));
+
+.float bloodloss_timer;
+
+MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(IS_PLAYER(player))
+       if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player))
+       {
+               PHYS_INPUT_BUTTON_CROUCH(player) = true;
+
+               if(time >= player.bloodloss_timer)
+               {
+                       if(player.vehicle)
+                               vehicles_exit(player.vehicle, VHEF_RELEASE);
+                       if(player.event_damage)
+                               player.event_damage(player, player, player, 1, DEATH_ROT.m_id, player.origin, '0 0 0');
+                       player.bloodloss_timer = time + 0.5 + random() * 0.5;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.health <= autocvar_g_bloodloss)
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bloodloss");
+}
+
+MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Blood loss");
+}
diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index bdbbae46cdd60b60bac6541367bce201df18930a..11a080ef4a5ae85b1e0ff0ac452d0dfb39f099ed 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/breakablehook/breakablehook.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/breakablehook/sv_breakablehook.qc>
+#endif
index 8a41908af314aa1c09d0d27f4fe95f1719f56322..f8b2d1bc653a7d6098ead487cb912ecda3606760 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/breakablehook/breakablehook.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/breakablehook/sv_breakablehook.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc
deleted file mode 100644 (file)
index ca266eb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifdef IMPLEMENTATION
-#include <common/deathtypes/all.qh>
-#include <server/g_hook.qh>
-
-REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook"));
-
-bool autocvar_g_breakablehook; // allow toggling mid match?
-bool autocvar_g_breakablehook_owner;
-
-MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-
-       if(frag_target.classname == "grapplinghook")
-       {
-               if((!autocvar_g_breakablehook)
-               || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner)
-                       ) { M_ARGV(4, float) = 0; }
-
-               // hurt the owner of the hook
-               if(DIFF_TEAM(frag_attacker, frag_target.realowner))
-               {
-                       Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0');
-                       RemoveGrapplingHook(frag_target.realowner);
-                       return; // dead
-               }
-       }
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/breakablehook/module.inc b/qcsrc/common/mutators/mutator/breakablehook/module.inc
deleted file mode 100644 (file)
index 484eb4c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "breakablehook.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc
new file mode 100644 (file)
index 0000000..fdb0dc3
--- /dev/null
@@ -0,0 +1,30 @@
+#include "sv_breakablehook.qh"
+
+#include <common/deathtypes/all.qh>
+#include <server/g_hook.qh>
+
+REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook"));
+
+bool autocvar_g_breakablehook; // allow toggling mid match?
+bool autocvar_g_breakablehook_owner;
+
+MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+
+       if(frag_target.classname == "grapplinghook")
+       {
+               if((!autocvar_g_breakablehook)
+               || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner)
+                       ) { M_ARGV(4, float) = 0; }
+
+               // hurt the owner of the hook
+               if(DIFF_TEAM(frag_attacker, frag_target.realowner))
+               {
+                       Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0');
+                       RemoveGrapplingHook(frag_target.realowner);
+                       return; // dead
+               }
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c06263a92f056dd90672e3c345a7c8515526b8f9..715a3acfccec654c443070f4a3e648f2ac90c1f1 100644 (file)
@@ -1,3 +1,8 @@
 // generated file; do not modify
-#include <common/mutators/mutator/buffs/all.qc>
 #include <common/mutators/mutator/buffs/buffs.qc>
+#ifdef CSQC
+    #include <common/mutators/mutator/buffs/cl_buffs.qc>
+#endif
+#ifdef SVQC
+    #include <common/mutators/mutator/buffs/sv_buffs.qc>
+#endif
index 2133c7250e51be50c318cf2773dead6aa20c8f6b..78217bb46e9f22873027e4c9cdb6bc6113f3035f 100644 (file)
@@ -1,3 +1,8 @@
 // generated file; do not modify
-#include <common/mutators/mutator/buffs/all.qh>
 #include <common/mutators/mutator/buffs/buffs.qh>
+#ifdef CSQC
+    #include <common/mutators/mutator/buffs/cl_buffs.qh>
+#endif
+#ifdef SVQC
+    #include <common/mutators/mutator/buffs/sv_buffs.qh>
+#endif
index f6c25f76103353e9bd865123f944ec7d61303070..915f95329dbda327f6d3f752221892346a4bb114 100644 (file)
@@ -116,3 +116,11 @@ REGISTER_BUFF(LUCK) {
     this.m_color = '1 0.23 0.44';
 }
 BUFF_SPAWNFUNCS(luck, BUFF_LUCK)
+
+REGISTER_BUFF(FLIGHT) {
+    this.m_prettyName = _("Flight");
+    this.m_name = "flight";
+    this.m_skin = 11;
+    this.m_color = '0.23 0.44 1';
+}
+BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT)
diff --git a/qcsrc/common/mutators/mutator/buffs/all.qc b/qcsrc/common/mutators/mutator/buffs/all.qc
deleted file mode 100644 (file)
index b056751..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "all.qh"
diff --git a/qcsrc/common/mutators/mutator/buffs/all.qh b/qcsrc/common/mutators/mutator/buffs/all.qh
deleted file mode 100644 (file)
index 66f83d1..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef BUFFS_ALL_H
-#define BUFFS_ALL_H
-
-#include <common/teams.qh>
-#include <common/util.qh>
-
-REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1);
-REGISTER_RADARICON(Buff, 1);
-
-REGISTRY(Buffs, BITS(5))
-#define Buffs_from(i) _Buffs_from(i, BUFF_Null)
-REGISTER_REGISTRY(Buffs)
-REGISTRY_CHECK(Buffs)
-
-#define REGISTER_BUFF(id) \
-    REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff))
-
-#include <common/items/item/pickup.qh>
-CLASS(Buff, Pickup)
-       /** bit index */
-       ATTRIB(Buff, m_itemid, int, 0)
-       ATTRIB(Buff, m_name, string, "buff")
-       ATTRIB(Buff, m_color, vector, '1 1 1')
-       ATTRIB(Buff, m_prettyName, string, "Buff")
-       ATTRIB(Buff, m_skin, int, 0)
-       ATTRIB(Buff, m_sprite, string, "")
-       METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) {
-               returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name));
-       }
-#ifdef SVQC
-       METHOD(Buff, m_time, float(Buff this))
-       { return cvar(strcat("g_buffs_", this.netname, "_time")); }
-#endif
-ENDCLASS(Buff)
-
-STATIC_INIT(REGISTER_BUFFS) {
-    FOREACH(Buffs, true, {
-        it.netname = it.m_name; \
-        it.m_itemid = BIT(it.m_id - 1); \
-        it.m_sprite = strzone(strcat("buff-", it.m_name)); \
-    });
-}
-
-#ifdef SVQC
-       // .int buffs = _STAT(BUFFS);
-       void buff_Init(entity ent);
-       void buff_Init_Compat(entity ent, entity replacement);
-       #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \
-               this.buffs = b.m_itemid; \
-               this.team = t; \
-               buff_Init(this); \
-       }
-       #define BUFF_SPAWNFUNCS(e, b)                       \
-                       BUFF_SPAWNFUNC(e,           b,  0)          \
-                       BUFF_SPAWNFUNC(e##_team1,   b,  NUM_TEAM_1) \
-                       BUFF_SPAWNFUNC(e##_team2,   b,  NUM_TEAM_2) \
-                       BUFF_SPAWNFUNC(e##_team3,   b,  NUM_TEAM_3) \
-                       BUFF_SPAWNFUNC(e##_team4,   b,  NUM_TEAM_4)
-       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); }
-#else
-       #define BUFF_SPAWNFUNC(e, b, t)
-       #define BUFF_SPAWNFUNCS(e, b)
-       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r)
-#endif
-
-REGISTER_BUFF(Null);
-BUFF_SPAWNFUNCS(random, BUFF_Null)
-
-#include "all.inc"
-
-#endif
index ec1fadabe58adb2b78130243661be98f456d25f7..f38d39d61867ef56ec625271f7a330012b6a74e2 100644 (file)
-#ifndef MUTATOR_BUFFS_H
-#define MUTATOR_BUFFS_H
+#include "buffs.qh"
 
-#include "../instagib/module.inc"
-
-bool  autocvar_g_buffs_effects;
-float autocvar_g_buffs_waypoint_distance;
-bool autocvar_g_buffs_randomize;
-float autocvar_g_buffs_random_lifetime;
-bool autocvar_g_buffs_random_location;
-int autocvar_g_buffs_random_location_attempts;
-int autocvar_g_buffs_spawn_count;
-bool autocvar_g_buffs_replace_powerups;
-float autocvar_g_buffs_cooldown_activate;
-float autocvar_g_buffs_cooldown_respawn;
-float autocvar_g_buffs_resistance_blockpercent;
-float autocvar_g_buffs_medic_survive_chance;
-float autocvar_g_buffs_medic_survive_health;
-float autocvar_g_buffs_medic_rot;
-float autocvar_g_buffs_medic_max;
-float autocvar_g_buffs_medic_regen;
-float autocvar_g_buffs_medic_heal_amount = 15;
-float autocvar_g_buffs_medic_heal_delay = 1;
-float autocvar_g_buffs_medic_heal_range = 400;
-float autocvar_g_buffs_vengeance_damage_multiplier;
-float autocvar_g_buffs_bash_force;
-float autocvar_g_buffs_bash_force_self;
-float autocvar_g_buffs_disability_slowtime;
-float autocvar_g_buffs_disability_speed;
-float autocvar_g_buffs_disability_rate;
-float autocvar_g_buffs_disability_weaponspeed;
-float autocvar_g_buffs_speed_speed;
-float autocvar_g_buffs_speed_rate;
-float autocvar_g_buffs_speed_weaponspeed;
-float autocvar_g_buffs_speed_damage_take;
-float autocvar_g_buffs_speed_regen;
-float autocvar_g_buffs_vampire_damage_steal;
-float autocvar_g_buffs_invisible_alpha;
-float autocvar_g_buffs_jump_height;
-float autocvar_g_buffs_inferno_burntime_factor;
-float autocvar_g_buffs_inferno_burntime_min_time;
-float autocvar_g_buffs_inferno_burntime_target_damage;
-float autocvar_g_buffs_inferno_burntime_target_time;
-float autocvar_g_buffs_inferno_damagemultiplier;
-float autocvar_g_buffs_swapper_range;
-float autocvar_g_buffs_magnet_range_item;
-float autocvar_g_buffs_magnet_range_buff = 200;
-float autocvar_g_buffs_luck_chance = 0.15;
-float autocvar_g_buffs_luck_damagemultiplier = 3;
-
-// ammo
-.float buff_ammo_prev_infitems;
-.int buff_ammo_prev_clipload;
-// invisible
-.float buff_invisible_prev_alpha;
-// medic
-.float buff_medic_healtime;
-// disability
-.float buff_disability_time;
-.float buff_disability_effect_time;
-// common buff variables
-.float buff_effect_delay;
-
-// buff definitions
-.float buff_active;
-.float buff_activetime;
-.float buff_activetime_updated;
-.entity buff_waypoint;
-.int oldbuffs; // for updating effects
-.entity buff_model; // controls effects (TODO: make csqc)
-
-const vector BUFF_MIN = ('-16 -16 -20');
-const vector BUFF_MAX = ('16 16 20');
-
-// client side options
-.float cvar_cl_buffs_autoreplace;
-#endif
-
-#ifdef IMPLEMENTATION
-
-#include <common/triggers/target/music.qh>
-#include <common/gamemodes/all.qh>
-
-.float buff_time = _STAT(BUFF_TIME);
-void buffs_DelayedInit(entity this);
-
-REGISTER_MUTATOR(buffs, cvar("g_buffs"))
-{
-       MUTATOR_ONADD
-       {
-               InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
-       }
-}
-
-bool buffs_BuffModel_Customize(entity this)
-{
-       entity player, myowner;
-       bool same_team;
-
-       player = WaypointSprite_getviewentity(other);
-       myowner = this.owner;
-       same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
-
-       if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
-               return false;
-
-       if(MUTATOR_CALLHOOK(BuffModel_Customize, this, player))
-               return false;
-
-       if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
-       {
-               // somewhat hide the model, but keep the glow
-               this.effects = 0;
-               this.alpha = -1;
-       }
-       else
-       {
-               this.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
-               this.alpha = 1;
-       }
-       return true;
-}
-
-void buffs_BuffModel_Spawn(entity player)
-{
-       player.buff_model = spawn();
-       setmodel(player.buff_model, MDL_BUFF);
-       setsize(player.buff_model, '0 0 -40', '0 0 40');
-       setattachment(player.buff_model, player, "");
-       setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1));
-       player.buff_model.owner = player;
-       player.buff_model.scale = 0.7;
-       player.buff_model.pflags = PFLAGS_FULLDYNAMIC;
-       player.buff_model.light_lev = 200;
-       setcefc(player.buff_model, buffs_BuffModel_Customize);
-}
-
-vector buff_GlowColor(entity buff)
-{
-       //if(buff.team) { return Team_ColorRGB(buff.team); }
-       return buff.m_color;
-}
-
-void buff_Effect(entity player, string eff)
-{
-       if(!autocvar_g_buffs_effects) { return; }
-
-       if(time >= player.buff_effect_delay)
-       {
-               Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
-               player.buff_effect_delay = time + 0.05; // prevent spam
-       }
-}
-
-// buff item
-bool buff_Waypoint_visible_for_player(entity this, entity player, entity view)
-{
-       if(!this.owner.buff_active && !this.owner.buff_activetime)
-               return false;
-
-       if (view.buffs)
-       {
-               return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs;
-       }
-
-       return WaypointSprite_visible_for_player(this, player, view);
-}
-
-void buff_Waypoint_Spawn(entity e)
-{
-       entity buff = buff_FirstFromFlags(e.buffs);
-       entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff);
-       wp.wp_extra = buff.m_id;
-       WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod);
-       e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
-}
-
-void buff_SetCooldown(entity this, float cd)
-{
-       cd = max(0, cd);
-
-       if(!this.buff_waypoint)
-               buff_Waypoint_Spawn(this);
-
-       WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + cd);
-       this.buff_activetime = cd;
-       this.buff_active = !cd;
-}
-
-void buff_Respawn(entity this)
-{
-       if(gameover) { return; }
-
-       vector oldbufforigin = this.origin;
-       this.velocity = '0 0 200';
-
-       if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY,
-               ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
-       {
-               entity spot = SelectSpawnPoint(this, true);
-               setorigin(this, spot.origin);
-               this.velocity = ((randomvec() * 100) + '0 0 200');
-               this.angles = spot.angles;
-       }
-
-       tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this);
-
-       setorigin(this, trace_endpos); // attempt to unstick
-
-       this.movetype = MOVETYPE_TOSS;
-
-       makevectors(this.angles);
-       this.angles = '0 0 0';
-       if(autocvar_g_buffs_random_lifetime > 0)
-               this.lifetime = time + autocvar_g_buffs_random_lifetime;
-
-       Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1);
-       Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
-
-       WaypointSprite_Ping(this.buff_waypoint);
-
-       sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
-}
-
-void buff_Touch(entity this)
-{
-       if(gameover) { return; }
-
-       if(ITEM_TOUCH_NEEDKILL())
-       {
-               buff_Respawn(this);
-               return;
-       }
-
-       if((this.team && DIFF_TEAM(other, this))
-       || (STAT(FROZEN, other))
-       || (other.vehicle)
-       || (!this.buff_active)
-       )
-       {
-               // can't touch this
-               return;
-       }
-
-       if(MUTATOR_CALLHOOK(BuffTouch, this, other))
-               return;
-       other = M_ARGV(1, entity);
-
-       if(!IS_PLAYER(other))
-               return; // incase mutator changed other
-
-       if (other.buffs)
-       {
-               if (other.cvar_cl_buffs_autoreplace && other.buffs != this.buffs)
-               {
-                       int buffid = buff_FirstFromFlags(other.buffs).m_id;
-                       //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs);
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid);
-
-                       other.buffs = 0;
-                       //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
-               }
-               else { return; } // do nothing
-       }
-
-       this.owner = other;
-       this.buff_active = false;
-       this.lifetime = 0;
-       int buffid = buff_FirstFromFlags(this.buffs).m_id;
-       Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid);
-       Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid);
-
-       Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
-       sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM);
-       other.buffs |= (this.buffs);
-}
-
-float buff_Available(entity buff)
-{
-       if (buff == BUFF_Null)
-               return false;
-       if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
-               return false;
-       if (buff == BUFF_VAMPIRE && cvar("g_vampire"))
-               return false;
-       return cvar(strcat("g_buffs_", buff.m_name));
-}
-
-.int buff_seencount;
-
-void buff_NewType(entity ent, float cb)
-{
-       RandomSelection_Init();
-       FOREACH(Buffs, buff_Available(it), LAMBDA(
-               it.buff_seencount += 1;
-               // if it's already been chosen, give it a lower priority
-               RandomSelection_Add(NULL, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount));
-       ));
-       ent.buffs = RandomSelection_chosen_float;
-}
-
-void buff_Think(entity this)
-{
-       if(this.buffs != this.oldbuffs)
-       {
-               entity buff = buff_FirstFromFlags(this.buffs);
-               this.color = buff.m_color;
-               this.glowmod = buff_GlowColor(buff);
-               this.skin = buff.m_skin;
-
-               setmodel(this, MDL_BUFF);
-
-               if(this.buff_waypoint)
-               {
-                       //WaypointSprite_Disown(this.buff_waypoint, 1);
-                       WaypointSprite_Kill(this.buff_waypoint);
-                       buff_Waypoint_Spawn(this);
-                       if(this.buff_activetime)
-                               WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + this.buff_activetime - frametime);
-               }
-
-               this.oldbuffs = this.buffs;
-       }
-
-       if(!gameover)
-       if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
-       if(!this.buff_activetime_updated)
-       {
-               buff_SetCooldown(this, this.buff_activetime);
-               this.buff_activetime_updated = true;
-       }
-
-       if(!this.buff_active && !this.buff_activetime)
-       if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs))
-       {
-               buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime);
-               this.owner = NULL;
-               if(autocvar_g_buffs_randomize)
-                       buff_NewType(this, this.buffs);
-
-               if(autocvar_g_buffs_random_location || (this.spawnflags & 64))
-                       buff_Respawn(this);
-       }
-
-       if(this.buff_activetime)
-       if(!gameover)
-       if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
-       {
-               this.buff_activetime = max(0, this.buff_activetime - frametime);
-
-               if(!this.buff_activetime)
-               {
-                       this.buff_active = true;
-                       sound(this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM);
-                       Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
-               }
-       }
-
-       if(this.buff_active)
-       {
-               if(this.team && !this.buff_waypoint)
-                       buff_Waypoint_Spawn(this);
-
-               if(this.lifetime)
-               if(time >= this.lifetime)
-                       buff_Respawn(this);
-       }
-
-       this.nextthink = time;
-       //this.angles_y = time * 110.1;
-}
-
-void buff_Waypoint_Reset(entity this)
-{
-       WaypointSprite_Kill(this.buff_waypoint);
-
-       if(this.buff_activetime) { buff_Waypoint_Spawn(this); }
-}
-
-void buff_Reset(entity this)
-{
-       if(autocvar_g_buffs_randomize)
-               buff_NewType(this, this.buffs);
-       this.owner = NULL;
-       buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate);
-       buff_Waypoint_Reset(this);
-       this.buff_activetime_updated = false;
-
-       if(autocvar_g_buffs_random_location || (this.spawnflags & 64))
-               buff_Respawn(this);
-}
-
-float buff_Customize(entity this)
-{
-       entity player = WaypointSprite_getviewentity(other);
-       if(!this.buff_active || (this.team && DIFF_TEAM(player, this)))
-       {
-               this.alpha = 0.3;
-               if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); }
-               this.pflags = 0;
-       }
-       else
-       {
-               this.alpha = 1;
-               if(!(this.effects & EF_FULLBRIGHT)) { this.effects |= EF_FULLBRIGHT; }
-               this.light_lev = 220 + 36 * sin(time);
-               this.pflags = PFLAGS_FULLDYNAMIC;
-       }
-       return true;
-}
-
-void buff_Init(entity this)
-{
-       if(!cvar("g_buffs")) { remove(this); return; }
-
-       if(!teamplay && this.team) { this.team = 0; }
-
-       entity buff = buff_FirstFromFlags(this.buffs);
-
-       if(!this.buffs || buff_Available(buff))
-               buff_NewType(this, 0);
-
-       this.classname = "item_buff";
-       this.solid = SOLID_TRIGGER;
-       this.flags = FL_ITEM;
-       setthink(this, buff_Think);
-       settouch(this, buff_Touch);
-       this.reset = buff_Reset;
-       this.nextthink = time + 0.1;
-       this.gravity = 1;
-       this.movetype = MOVETYPE_TOSS;
-       this.scale = 1;
-       this.skin = buff.m_skin;
-       this.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
-       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
-       setcefc(this, buff_Customize);
-       //this.gravity = 100;
-       this.color = buff.m_color;
-       this.glowmod = buff_GlowColor(this);
-       buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime);
-       this.buff_active = !this.buff_activetime;
-       this.pflags = PFLAGS_FULLDYNAMIC;
-
-       if(this.spawnflags & 1)
-               this.noalign = true;
-
-       if(this.noalign)
-               this.movetype = MOVETYPE_NONE; // reset by random location
-
-       setmodel(this, MDL_BUFF);
-       setsize(this, BUFF_MIN, BUFF_MAX);
-
-       if(cvar("g_buffs_random_location") || (this.spawnflags & 64))
-               buff_Respawn(this);
-}
-
-void buff_Init_Compat(entity ent, entity replacement)
-{
-       if (ent.spawnflags & 2)
-               ent.team = NUM_TEAM_1;
-       else if (ent.spawnflags & 4)
-               ent.team = NUM_TEAM_2;
-
-       ent.buffs = replacement.m_itemid;
-
-       buff_Init(ent);
-}
-
-void buff_SpawnReplacement(entity ent, entity old)
-{
-       setorigin(ent, old.origin);
-       ent.angles = old.angles;
-       ent.noalign = (old.noalign || (old.spawnflags & 1));
-
-       buff_Init(ent);
-}
-
-void buff_Vengeance_DelayedDamage(entity this)
-{
-       if(this.enemy)
-               Damage(this.enemy, this.owner, this.owner, this.dmg, DEATH_BUFF.m_id, this.enemy.origin, '0 0 0');
-
-       remove(this);
-       return;
-}
-
-// note: only really useful in teamplay
-void buff_Medic_Heal(entity this)
-{
-       FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
-       {
-               if(SAME_TEAM(it, this))
-               if(it.health < autocvar_g_balance_health_regenstable)
-               {
-                       Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
-                       it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable);
-               }
-       });
-}
-
-float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
-{
-       return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base);
-}
-
-// mutator hooks
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
-{
-       entity frag_target = M_ARGV(2, entity);
-       float frag_deathtype = M_ARGV(6, float);
-       float frag_damage = M_ARGV(7, float);
-
-       if(frag_deathtype == DEATH_BUFF.m_id) { return; }
-
-       if(frag_target.buffs & BUFF_RESISTANCE.m_itemid)
-       {
-               vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
-               M_ARGV(4, float) = v.x; // take
-               M_ARGV(5, float) = v.y; // save
-       }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-       float frag_deathtype = M_ARGV(3, float);
-       float frag_damage = M_ARGV(4, float);
-       vector frag_force = M_ARGV(6, vector);
-
-       if(frag_deathtype == DEATH_BUFF.m_id) { return; }
-
-       if(frag_target.buffs & BUFF_SPEED.m_itemid)
-       if(frag_target != frag_attacker)
-               frag_damage *= autocvar_g_buffs_speed_damage_take;
-
-       if(frag_target.buffs & BUFF_MEDIC.m_itemid)
-       if((frag_target.health - frag_damage) <= 0)
-       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
-       if(frag_attacker)
-       if(random() <= autocvar_g_buffs_medic_survive_chance)
-               frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health);
-
-       if(frag_target.buffs & BUFF_JUMP.m_itemid)
-       if(frag_deathtype == DEATH_FALL.m_id)
-               frag_damage = 0;
-
-       if(frag_target.buffs & BUFF_VENGEANCE.m_itemid)
-       if(frag_attacker)
-       if(frag_attacker != frag_target)
-       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
-       {
-               entity dmgent = spawn();
-
-               dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
-               dmgent.enemy = frag_attacker;
-               dmgent.owner = frag_target;
-               setthink(dmgent, buff_Vengeance_DelayedDamage);
-               dmgent.nextthink = time + 0.1;
-       }
-
-       if(frag_target.buffs & BUFF_BASH.m_itemid)
-       if(frag_attacker != frag_target)
-               frag_force = '0 0 0';
-
-       if(frag_attacker.buffs & BUFF_BASH.m_itemid)
-       if(frag_force)
-       if(frag_attacker == frag_target)
-               frag_force *= autocvar_g_buffs_bash_force_self;
-       else
-               frag_force *= autocvar_g_buffs_bash_force;
-
-       if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid)
-       if(frag_target != frag_attacker)
-               frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime;
-
-       if(frag_target.buffs & BUFF_INFERNO.m_itemid)
-       {
-               if(frag_deathtype == DEATH_FIRE.m_id)
-                       frag_damage = 0;
-               if(frag_deathtype == DEATH_LAVA.m_id)
-                       frag_damage *= 0.5; // TODO: cvarize?
-       }
-
-       if(frag_attacker.buffs & BUFF_LUCK.m_itemid)
-       if(frag_attacker != frag_target)
-       if(autocvar_g_buffs_luck_damagemultiplier > 0)
-       if(random() <= autocvar_g_buffs_luck_chance)
-               frag_damage *= autocvar_g_buffs_luck_damagemultiplier;
-
-       if(frag_attacker.buffs & BUFF_INFERNO.m_itemid)
-       if(frag_target != frag_attacker) {
-               float btime = buff_Inferno_CalculateTime(
-                       frag_damage,
-                       0,
-                       autocvar_g_buffs_inferno_burntime_min_time,
-                       autocvar_g_buffs_inferno_burntime_target_damage,
-                       autocvar_g_buffs_inferno_burntime_target_time,
-                       autocvar_g_buffs_inferno_burntime_factor
-               );
-               Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id);
-       }
-
-       // this... is ridiculous (TODO: fix!)
-       if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid)
-       if(!frag_target.vehicle)
-       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
-       if(!IS_DEAD(frag_target))
-       if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
-       if(frag_attacker != frag_target)
-       if(!STAT(FROZEN, frag_target))
-       if(frag_target.takedamage)
-       if(DIFF_TEAM(frag_attacker, frag_target))
-       {
-               frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
-               if(frag_target.armorvalue)
-                       frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max);
-       }
-
-       M_ARGV(4, float) = frag_damage;
-       M_ARGV(6, vector) = frag_force;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.buffs = 0;
-       // reset timers here to prevent them continuing after re-spawn
-       player.buff_disability_time = 0;
-       player.buff_disability_effect_time = 0;
-}
-
-.float stat_sv_maxspeed;
-.float stat_sv_airspeedlimit_nonqw;
-.float stat_sv_jumpvelocity;
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(player.buffs & BUFF_SPEED.m_itemid)
-       {
-               player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
-               player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
-       }
-
-       if(time < player.buff_disability_time)
-       {
-               player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
-               player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
-       }
-
-       if(player.buffs & BUFF_JUMP.m_itemid)
-       {
-               // automatically reset, no need to worry
-               player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(player.buffs & BUFF_JUMP.m_itemid)
-               M_ARGV(1, float) = autocvar_g_buffs_jump_height;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
-{
-       entity mon = M_ARGV(0, entity);
-
-       if(time < mon.buff_disability_time)
-       {
-               M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed
-               M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed
-       }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
-{
-       entity frag_target = M_ARGV(2, entity);
-       
-       if(frag_target.buffs)
-       {
-               int buffid = buff_FirstFromFlags(frag_target.buffs).m_id;
-               Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid);
-               frag_target.buffs = 0;
-
-               if(frag_target.buff_model)
-               {
-                       remove(frag_target.buff_model);
-                       frag_target.buff_model = NULL;
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
-{
-       if(MUTATOR_RETURNVALUE || gameover) { return; }
-
-       entity player = M_ARGV(0, entity);
-
-       if(player.buffs)
-       {
-               int buffid = buff_FirstFromFlags(player.buffs).m_id;
-               Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid);
-               Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
-
-               player.buffs = 0;
-               sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
-               return true;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
-{
-       if(MUTATOR_RETURNVALUE || gameover) { return; }
-       entity player = M_ARGV(0, entity);
-
-       if(player.buffs & BUFF_SWAPPER.m_itemid)
-       {
-               float best_distance = autocvar_g_buffs_swapper_range;
-               entity closest = NULL;
-               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
-                       if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle)
-                       if(DIFF_TEAM(it, player))
-                       {
-                               float test = vlen2(player.origin - it.origin);
-                               if(test <= best_distance * best_distance)
-                               {
-                                       best_distance = sqrt(test);
-                                       closest = it;
-                               }
-                       }
-               ));
-
-               if(closest)
-               {
-                       vector my_org, my_vel, my_ang, their_org, their_vel, their_ang;
-
-                       my_org = player.origin;
-                       my_vel = player.velocity;
-                       my_ang = player.angles;
-                       their_org = closest.origin;
-                       their_vel = closest.velocity;
-                       their_ang = closest.angles;
-
-                       Drop_Special_Items(closest);
-
-                       MUTATOR_CALLHOOK(PortalTeleport, player); // initiate flag dropper
-
-                       setorigin(player, their_org);
-                       setorigin(closest, my_org);
-
-                       closest.velocity = my_vel;
-                       closest.angles = my_ang;
-                       closest.fixangle = true;
-                       closest.oldorigin = my_org;
-                       closest.oldvelocity = my_vel;
-                       player.velocity = their_vel;
-                       player.angles = their_ang;
-                       player.fixangle = true;
-                       player.oldorigin = their_org;
-                       player.oldvelocity = their_vel;
-
-                       // set pusher so player gets the kill if they fall into void
-                       closest.pusher = player;
-                       closest.pushltime = time + autocvar_g_maxpushtime;
-                       closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest);
-
-                       Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1);
-                       Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1);
-
-                       sound(player, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
-                       sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
-
-                       // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam
-                       player.buffs = 0;
-                       return true;
-               }
-       }
-}
-
-bool buffs_RemovePlayer(entity player)
-{
-       if(player.buff_model)
-       {
-               remove(player.buff_model);
-               player.buff_model = NULL;
-       }
-
-       // also reset timers here to prevent them continuing after spectating
-       player.buff_disability_time = 0;
-       player.buff_disability_effect_time = 0;
-
-       return false;
-}
-MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); }
-MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); }
-
-MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
-{
-       entity wp = M_ARGV(0, entity);
-       entity player = M_ARGV(1, entity);
-
-       entity e = WaypointSprite_getviewentity(player);
-
-       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
-       // but only apply this to real players, not to spectators
-       if((wp.owner.flags & FL_CLIENT) && (wp.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == player))
-       if(DIFF_TEAM(wp.owner, e))
-               return true;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
-{
-       entity ent = M_ARGV(0, entity);
-
-       if(autocvar_g_buffs_replace_powerups)
-       switch(ent.classname)
-       {
-               case "item_strength":
-               case "item_invincible":
-               {
-                       entity e = spawn();
-                       buff_SpawnReplacement(e, ent);
-                       return true;
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor)
-{
-       entity player = M_ARGV(1, entity);
-
-       if(player.buffs & BUFF_SPEED.m_itemid)
-               M_ARGV(0, float) *= autocvar_g_buffs_speed_rate;
-
-       if(time < player.buff_disability_time)
-               M_ARGV(0, float) *= autocvar_g_buffs_disability_rate;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor)
-{
-       entity player = M_ARGV(1, entity);
-
-       if(player.buffs & BUFF_SPEED.m_itemid)
-               M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed;
-
-       if(time < player.buff_disability_time)
-               M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(gameover || IS_DEAD(player)) { return; }
-
-       if(time < player.buff_disability_time)
-       if(time >= player.buff_disability_effect_time)
-       {
-               Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
-               player.buff_disability_effect_time = time + 0.5;
-       }
-
-       // handle buff lost status
-       // 1: notify everyone else
-       // 2: notify carrier as well
-       int buff_lost = 0;
-
-       if(player.buff_time)
-       if(time >= player.buff_time)
-               buff_lost = 2;
-
-       if(STAT(FROZEN, player)) { buff_lost = 1; }
-
-       if(buff_lost)
-       {
-               if(player.buffs)
-               {
-                       int buffid = buff_FirstFromFlags(player.buffs).m_id;
-                       Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
-                       if(buff_lost >= 2)
-                       {
-                               Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message?
-                               sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
-                       }
-                       player.buffs = 0;
-               }
-       }
-
-       if(player.buffs & BUFF_MAGNET.m_itemid)
-       {
-               vector pickup_size;
-               FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
-               {
-                       if(it.buffs)
-                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff;
-                       else
-                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
-
-                       if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax))
-                       {
-                               if(gettouch(it))
-                               {
-                                       entity oldother = other;
-                                       other = player;
-                                       gettouch(it)(it);
-                                       other = oldother;
-                               }
-                       }
-               });
-       }
-
-       if(player.buffs & BUFF_AMMO.m_itemid)
-       if(player.clip_size)
-               player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
-
-       if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid))
-       if(player.alpha != autocvar_g_buffs_invisible_alpha)
-               player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO)
-
-       if(player.buffs & BUFF_MEDIC.m_itemid)
-       if(time >= player.buff_medic_healtime)
-       {
-               buff_Medic_Heal(player);
-               player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay;
-       }
-
-#define BUFF_ONADD(b) if ( (player.buffs & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid))
-#define BUFF_ONREM(b) if (!(player.buffs & (b).m_itemid) &&  (player.oldbuffs & (b).m_itemid))
-
-       if(player.buffs != player.oldbuffs)
-       {
-               entity buff = buff_FirstFromFlags(player.buffs);
-               float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0;
-               player.buff_time = (bufftime) ? time + bufftime : 0;
-
-               BUFF_ONADD(BUFF_AMMO)
-               {
-                       player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO);
-                       player.items |= IT_UNLIMITED_WEAPON_AMMO;
-
-                       if(player.clip_load)
-                               player.buff_ammo_prev_clipload = player.clip_load;
-                       player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
-               }
-
-               BUFF_ONREM(BUFF_AMMO)
-               {
-                       if(player.buff_ammo_prev_infitems)
-                               player.items |= IT_UNLIMITED_WEAPON_AMMO;
-                       else
-                               player.items &= ~IT_UNLIMITED_WEAPON_AMMO;
-
-                       if(player.buff_ammo_prev_clipload)
-                               player.clip_load = player.buff_ammo_prev_clipload;
-               }
-
-               BUFF_ONADD(BUFF_INVISIBLE)
-               {
-                       if(time < player.strength_finished && g_instagib)
-                               player.alpha = autocvar_g_instagib_invis_alpha;
-                       else
-                               player.alpha = player.buff_invisible_prev_alpha;
-                       player.alpha = autocvar_g_buffs_invisible_alpha;
-               }
-
-               BUFF_ONREM(BUFF_INVISIBLE)
-                       player.alpha = player.buff_invisible_prev_alpha;
-
-               player.oldbuffs = player.buffs;
-               if(player.buffs)
-               {
-                       if(!player.buff_model)
-                               buffs_BuffModel_Spawn(player);
-
-                       player.buff_model.color = buff.m_color;
-                       player.buff_model.glowmod = buff_GlowColor(player.buff_model);
-                       player.buff_model.skin = buff.m_skin;
-
-                       player.effects |= EF_NOSHADOW;
-               }
-               else
-               {
-                       remove(player.buff_model);
-                       player.buff_model = NULL;
-
-                       player.effects &= ~(EF_NOSHADOW);
-               }
-       }
-
-       if(player.buff_model)
-       {
-               player.buff_model.effects = player.effects;
-               player.buff_model.effects |= EF_LOWPRECISION;
-               player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance
-
-               player.buff_model.alpha = player.alpha;
-       }
-
-#undef BUFF_ONADD
-#undef BUFF_ONREM
-}
-
-MUTATOR_HOOKFUNCTION(buffs, SpectateCopy)
-{
-       entity spectatee = M_ARGV(0, entity);
-       entity client = M_ARGV(1, entity);
-
-       client.buffs = spectatee.buffs;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, VehicleEnter)
-{
-       entity player = M_ARGV(0, entity);
-       entity veh = M_ARGV(1, entity);
-
-       veh.buffs = player.buffs;
-       player.buffs = 0;
-       veh.buff_time = max(0, player.buff_time - time);
-       player.buff_time = 0;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, VehicleExit)
-{
-       entity player = M_ARGV(0, entity);
-       entity veh = M_ARGV(1, entity);
-
-       player.buffs = player.oldbuffs = veh.buffs;
-       veh.buffs = 0;
-       player.buff_time = time + veh.buff_time;
-       veh.buff_time = 0;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(player.buffs & BUFF_MEDIC.m_itemid)
-       {
-               M_ARGV(2, float) = autocvar_g_buffs_medic_rot; // rot_mod
-               M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod
-               M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod
-       }
-
-       if(player.buffs & BUFF_SPEED.m_itemid)
-               M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod
-}
-
-REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace");
-
-MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
-}
-
-MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
+string BUFF_NAME(int i)
 {
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
+    Buff b = Buffs_from(i);
+    return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName);
 }
 
-void buffs_DelayedInit(entity this)
+entity buff_FirstFromFlags(int _buffs)
 {
-       if(autocvar_g_buffs_spawn_count > 0)
-       if(find(NULL, classname, "item_buff") == NULL)
-       {
-               float i;
-               for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
-               {
-                       entity e = spawn();
-                       e.spawnflags |= 64; // always randomize
-                       e.velocity = randomvec() * 250; // this gets reset anyway if random location works
-                       buff_Init(e);
-               }
-       }
+    if (flags)
+    {
+        FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it));
+    }
+    return BUFF_Null;
 }
-#endif
diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qh b/qcsrc/common/mutators/mutator/buffs/buffs.qh
new file mode 100644 (file)
index 0000000..ae76d9f
--- /dev/null
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <common/teams.qh>
+#include <common/util.qh>
+
+#ifdef GAMEQC
+REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1);
+REGISTER_RADARICON(Buff, 1);
+#endif
+
+REGISTRY(Buffs, BITS(5))
+#define Buffs_from(i) _Buffs_from(i, BUFF_Null)
+REGISTER_REGISTRY(Buffs)
+REGISTRY_CHECK(Buffs)
+
+#define REGISTER_BUFF(id) \
+    REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff))
+
+#include <common/items/item/pickup.qh>
+CLASS(Buff, Pickup)
+       /** bit index */
+       ATTRIB(Buff, m_itemid, int, 0);
+       ATTRIB(Buff, m_name, string, "buff");
+       ATTRIB(Buff, m_color, vector, '1 1 1');
+       ATTRIB(Buff, m_prettyName, string, "Buff");
+       ATTRIB(Buff, m_skin, int, 0);
+       ATTRIB(Buff, m_sprite, string, "");
+       METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) {
+               returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name));
+       }
+#ifdef SVQC
+       METHOD(Buff, m_time, float(Buff this))
+       { return cvar(strcat("g_buffs_", this.netname, "_time")); }
+#endif
+ENDCLASS(Buff)
+
+STATIC_INIT(REGISTER_BUFFS) {
+    FOREACH(Buffs, true, {
+        it.netname = it.m_name; \
+        it.m_itemid = BIT(it.m_id - 1); \
+        it.m_sprite = strzone(strcat("buff-", it.m_name)); \
+    });
+}
+
+#ifdef SVQC
+       // .int buffs = _STAT(BUFFS);
+       void buff_Init(entity ent);
+       void buff_Init_Compat(entity ent, entity replacement);
+       #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \
+               this.buffs = b.m_itemid; \
+               this.team = t; \
+               buff_Init(this); \
+       }
+       #define BUFF_SPAWNFUNCS(e, b)                       \
+                       BUFF_SPAWNFUNC(e,           b,  0)          \
+                       BUFF_SPAWNFUNC(e##_team1,   b,  NUM_TEAM_1) \
+                       BUFF_SPAWNFUNC(e##_team2,   b,  NUM_TEAM_2) \
+                       BUFF_SPAWNFUNC(e##_team3,   b,  NUM_TEAM_3) \
+                       BUFF_SPAWNFUNC(e##_team4,   b,  NUM_TEAM_4)
+       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); }
+#else
+       #define BUFF_SPAWNFUNC(e, b, t)
+       #define BUFF_SPAWNFUNCS(e, b)
+       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r)
+#endif
+
+REGISTER_BUFF(Null);
+BUFF_SPAWNFUNCS(random, BUFF_Null)
+
+#include "all.inc"
diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc
new file mode 100644 (file)
index 0000000..ca14753
--- /dev/null
@@ -0,0 +1,22 @@
+#include "cl_buffs.qh"
+
+REGISTER_MUTATOR(cl_buffs, true);
+MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add)
+{
+    int allBuffs = STAT(BUFFS);
+    FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA(
+               addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60);
+       ));
+}
+MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format)
+{
+    entity this = M_ARGV(0, entity);
+    string s = M_ARGV(1, string);
+    if (s == WP_Buff.netname || s == RADARICON_Buff.netname)
+    {
+        Buff b = Buffs_from(this.wp_extra);
+        M_ARGV(2, vector) = b.m_color;
+        M_ARGV(3, string) = b.m_prettyName;
+        return true;
+    }
+}
diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh
new file mode 100644 (file)
index 0000000..c939022
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "buffs.qh"
diff --git a/qcsrc/common/mutators/mutator/buffs/module.inc b/qcsrc/common/mutators/mutator/buffs/module.inc
deleted file mode 100644 (file)
index c248928..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "all.qc"
-#ifdef SVQC
-#include "buffs.qc"
-#endif
-
-#ifdef IMPLEMENTATION
-
-string BUFF_NAME(int i)
-{
-    Buff b = Buffs_from(i);
-    return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName);
-}
-
-entity buff_FirstFromFlags(int _buffs)
-{
-    if (flags)
-    {
-        FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it));
-    }
-    return BUFF_Null;
-}
-
-#ifdef CSQC
-REGISTER_MUTATOR(cl_buffs, true);
-MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add)
-{
-    int allBuffs = STAT(BUFFS);
-    FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA(
-               addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60);
-       ));
-}
-MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format)
-{
-    entity this = M_ARGV(0, entity);
-    string s = M_ARGV(1, string);
-    if (s == WP_Buff.netname || s == RADARICON_Buff.netname)
-    {
-        Buff b = Buffs_from(this.wp_extra);
-        M_ARGV(2, vector) = b.m_color;
-        M_ARGV(3, string) = b.m_prettyName;
-        return true;
-    }
-}
-
-#endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
new file mode 100644 (file)
index 0000000..d9eed03
--- /dev/null
@@ -0,0 +1,1029 @@
+#include "sv_buffs.qh"
+
+#include <common/triggers/target/music.qh>
+#include <common/gamemodes/_mod.qh>
+
+.float buff_time = _STAT(BUFF_TIME);
+void buffs_DelayedInit(entity this);
+
+AUTOCVAR(g_buffs, int, -1, _("Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them"));
+
+REGISTER_MUTATOR(buffs, autocvar_g_buffs)
+{
+       MUTATOR_ONADD
+       {
+               if(autocvar_g_buffs > 0)
+                       InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
+       }
+}
+
+bool buffs_BuffModel_Customize(entity this, entity client)
+{
+       entity player, myowner;
+       bool same_team;
+
+       player = WaypointSprite_getviewentity(client);
+       myowner = this.owner;
+       same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
+
+       if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
+               return false;
+
+       if(MUTATOR_CALLHOOK(BuffModel_Customize, this, player))
+               return false;
+
+       if(player == myowner || (IS_SPEC(client) && client.enemy == myowner))
+       {
+               // somewhat hide the model, but keep the glow
+               this.effects = 0;
+               this.alpha = -1;
+       }
+       else
+       {
+               this.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
+               this.alpha = 1;
+       }
+       return true;
+}
+
+void buffs_BuffModel_Spawn(entity player)
+{
+       player.buff_model = spawn();
+       setmodel(player.buff_model, MDL_BUFF);
+       setsize(player.buff_model, '0 0 -40', '0 0 40');
+       setattachment(player.buff_model, player, "");
+       setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1));
+       player.buff_model.owner = player;
+       player.buff_model.scale = 0.7;
+       player.buff_model.pflags = PFLAGS_FULLDYNAMIC;
+       player.buff_model.light_lev = 200;
+       setcefc(player.buff_model, buffs_BuffModel_Customize);
+}
+
+vector buff_GlowColor(entity buff)
+{
+       //if(buff.team) { return Team_ColorRGB(buff.team); }
+       return buff.m_color;
+}
+
+void buff_Effect(entity player, string eff)
+{
+       if(!autocvar_g_buffs_effects) { return; }
+
+       if(time >= player.buff_effect_delay)
+       {
+               Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
+               player.buff_effect_delay = time + 0.05; // prevent spam
+       }
+}
+
+// buff item
+bool buff_Waypoint_visible_for_player(entity this, entity player, entity view)
+{
+       if(!this.owner.buff_active && !this.owner.buff_activetime)
+               return false;
+
+       if (view.buffs)
+       {
+               return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs;
+       }
+
+       return WaypointSprite_visible_for_player(this, player, view);
+}
+
+void buff_Waypoint_Spawn(entity e)
+{
+       entity buff = buff_FirstFromFlags(e.buffs);
+       entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff);
+       wp.wp_extra = buff.m_id;
+       WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod);
+       e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
+}
+
+void buff_SetCooldown(entity this, float cd)
+{
+       cd = max(0, cd);
+
+       if(!this.buff_waypoint)
+               buff_Waypoint_Spawn(this);
+
+       WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + cd);
+       this.buff_activetime = cd;
+       this.buff_active = !cd;
+}
+
+void buff_Respawn(entity this)
+{
+       if(gameover) { return; }
+
+       vector oldbufforigin = this.origin;
+       this.velocity = '0 0 200';
+
+       if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY,
+               ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
+       {
+               entity spot = SelectSpawnPoint(this, true);
+               setorigin(this, spot.origin);
+               this.velocity = ((randomvec() * 100) + '0 0 200');
+               this.angles = spot.angles;
+       }
+
+       tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this);
+
+       setorigin(this, trace_endpos); // attempt to unstick
+
+       set_movetype(this, MOVETYPE_TOSS);
+
+       makevectors(this.angles);
+       this.angles = '0 0 0';
+       if(autocvar_g_buffs_random_lifetime > 0)
+               this.lifetime = time + autocvar_g_buffs_random_lifetime;
+
+       Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1);
+       Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
+
+       WaypointSprite_Ping(this.buff_waypoint);
+
+       sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+}
+
+void buff_Touch(entity this, entity toucher)
+{
+       if(gameover) { return; }
+
+       if(ITEM_TOUCH_NEEDKILL())
+       {
+               buff_Respawn(this);
+               return;
+       }
+
+       if((this.team && DIFF_TEAM(toucher, this))
+       || (STAT(FROZEN, toucher))
+       || (toucher.vehicle)
+       || (!this.buff_active)
+       )
+       {
+               // can't touch this
+               return;
+       }
+
+       if(MUTATOR_CALLHOOK(BuffTouch, this, toucher))
+               return;
+       toucher = M_ARGV(1, entity);
+
+       if(!IS_PLAYER(toucher))
+               return; // incase mutator changed toucher
+
+       if (toucher.buffs)
+       {
+               if (toucher.cvar_cl_buffs_autoreplace && toucher.buffs != this.buffs)
+               {
+                       int buffid = buff_FirstFromFlags(toucher.buffs).m_id;
+                       //Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_DROP, toucher.buffs);
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid);
+
+                       toucher.buffs = 0;
+                       //sound(toucher, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+               }
+               else { return; } // do nothing
+       }
+
+       this.owner = toucher;
+       this.buff_active = false;
+       this.lifetime = 0;
+       int buffid = buff_FirstFromFlags(this.buffs).m_id;
+       Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_GOT, buffid);
+       Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF, toucher.netname, buffid);
+
+       Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
+       sound(toucher, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM);
+       toucher.buffs |= (this.buffs);
+}
+
+float buff_Available(entity buff)
+{
+       if (buff == BUFF_Null)
+               return false;
+       if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
+               return false;
+       if (buff == BUFF_VAMPIRE && cvar("g_vampire"))
+               return false;
+       return cvar(strcat("g_buffs_", buff.m_name));
+}
+
+.int buff_seencount;
+
+void buff_NewType(entity ent, float cb)
+{
+       RandomSelection_Init();
+       FOREACH(Buffs, buff_Available(it), LAMBDA(
+               it.buff_seencount += 1;
+               // if it's already been chosen, give it a lower priority
+               RandomSelection_AddFloat(it.m_itemid, 1, max(0.2, 1 / it.buff_seencount));
+       ));
+       ent.buffs = RandomSelection_chosen_float;
+}
+
+void buff_Think(entity this)
+{
+       if(this.buffs != this.oldbuffs)
+       {
+               entity buff = buff_FirstFromFlags(this.buffs);
+               this.color = buff.m_color;
+               this.glowmod = buff_GlowColor(buff);
+               this.skin = buff.m_skin;
+
+               setmodel(this, MDL_BUFF);
+               setsize(this, BUFF_MIN, BUFF_MAX);
+
+               if(this.buff_waypoint)
+               {
+                       //WaypointSprite_Disown(this.buff_waypoint, 1);
+                       WaypointSprite_Kill(this.buff_waypoint);
+                       buff_Waypoint_Spawn(this);
+                       if(this.buff_activetime)
+                               WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + this.buff_activetime - frametime);
+               }
+
+               this.oldbuffs = this.buffs;
+       }
+
+       if(!gameover)
+       if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+       if(!this.buff_activetime_updated)
+       {
+               buff_SetCooldown(this, this.buff_activetime);
+               this.buff_activetime_updated = true;
+       }
+
+       if(!this.buff_active && !this.buff_activetime)
+       if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs))
+       {
+               buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime);
+               this.owner = NULL;
+               if(autocvar_g_buffs_randomize)
+                       buff_NewType(this, this.buffs);
+
+               if(autocvar_g_buffs_random_location || (this.spawnflags & 64))
+                       buff_Respawn(this);
+       }
+
+       if(this.buff_activetime)
+       if(!gameover)
+       if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+       {
+               this.buff_activetime = max(0, this.buff_activetime - frametime);
+
+               if(!this.buff_activetime)
+               {
+                       this.buff_active = true;
+                       sound(this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM);
+                       Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
+               }
+       }
+
+       if(this.buff_active)
+       {
+               if(this.team && !this.buff_waypoint)
+                       buff_Waypoint_Spawn(this);
+
+               if(this.lifetime)
+               if(time >= this.lifetime)
+                       buff_Respawn(this);
+       }
+
+       this.nextthink = time;
+       //this.angles_y = time * 110.1;
+}
+
+void buff_Waypoint_Reset(entity this)
+{
+       WaypointSprite_Kill(this.buff_waypoint);
+
+       if(this.buff_activetime) { buff_Waypoint_Spawn(this); }
+}
+
+void buff_Reset(entity this)
+{
+       if(autocvar_g_buffs_randomize)
+               buff_NewType(this, this.buffs);
+       this.owner = NULL;
+       buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate);
+       buff_Waypoint_Reset(this);
+       this.buff_activetime_updated = false;
+
+       if(autocvar_g_buffs_random_location || (this.spawnflags & 64))
+               buff_Respawn(this);
+}
+
+bool buff_Customize(entity this, entity client)
+{
+       entity player = WaypointSprite_getviewentity(client);
+       if(!this.buff_active || (this.team && DIFF_TEAM(player, this)))
+       {
+               this.alpha = 0.3;
+               if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); }
+               this.pflags = 0;
+       }
+       else
+       {
+               this.alpha = 1;
+               if(!(this.effects & EF_FULLBRIGHT)) { this.effects |= EF_FULLBRIGHT; }
+               this.light_lev = 220 + 36 * sin(time);
+               this.pflags = PFLAGS_FULLDYNAMIC;
+       }
+       return true;
+}
+
+void buff_Init(entity this)
+{
+       if(!cvar("g_buffs")) { delete(this); return; }
+
+       if(!teamplay && this.team) { this.team = 0; }
+
+       entity buff = buff_FirstFromFlags(this.buffs);
+
+       if(!this.buffs || buff_Available(buff))
+               buff_NewType(this, 0);
+
+       this.classname = "item_buff";
+       this.solid = SOLID_TRIGGER;
+       this.flags = FL_ITEM;
+       this.bot_pickup = true;
+       this.bot_pickupevalfunc = commodity_pickupevalfunc;
+       this.bot_pickupbasevalue = 1000;
+       IL_PUSH(g_items, this);
+       setthink(this, buff_Think);
+       settouch(this, buff_Touch);
+       this.reset = buff_Reset;
+       this.nextthink = time + 0.1;
+       this.gravity = 1;
+       set_movetype(this, MOVETYPE_TOSS);
+       this.scale = 1;
+       this.skin = buff.m_skin;
+       this.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
+       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+       setcefc(this, buff_Customize);
+       //this.gravity = 100;
+       this.color = buff.m_color;
+       this.glowmod = buff_GlowColor(this);
+       buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime);
+       this.buff_active = !this.buff_activetime;
+       this.pflags = PFLAGS_FULLDYNAMIC;
+
+       if(this.spawnflags & 1)
+               this.noalign = true;
+
+       if(this.noalign)
+               set_movetype(this, MOVETYPE_NONE); // reset by random location
+
+       setmodel(this, MDL_BUFF);
+       setsize(this, BUFF_MIN, BUFF_MAX);
+
+       if(cvar("g_buffs_random_location") || (this.spawnflags & 64))
+               buff_Respawn(this);
+}
+
+void buff_Init_Compat(entity ent, entity replacement)
+{
+       if (ent.spawnflags & 2)
+               ent.team = NUM_TEAM_1;
+       else if (ent.spawnflags & 4)
+               ent.team = NUM_TEAM_2;
+
+       ent.buffs = replacement.m_itemid;
+
+       buff_Init(ent);
+}
+
+void buff_SpawnReplacement(entity ent, entity old)
+{
+       setorigin(ent, old.origin);
+       ent.angles = old.angles;
+       ent.noalign = (old.noalign || (old.spawnflags & 1));
+
+       buff_Init(ent);
+}
+
+void buff_Vengeance_DelayedDamage(entity this)
+{
+       if(this.enemy)
+               Damage(this.enemy, this.owner, this.owner, this.dmg, DEATH_BUFF.m_id, this.enemy.origin, '0 0 0');
+
+       delete(this);
+       return;
+}
+
+// note: only really useful in teamplay
+void buff_Medic_Heal(entity this)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
+       {
+               if(SAME_TEAM(it, this))
+               if(it.health < autocvar_g_balance_health_regenstable)
+               {
+                       Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
+                       it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable);
+               }
+       });
+}
+
+float buff_Inferno_CalculateTime(float damg, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
+{
+       return offset_y + (intersect_y - offset_y) * logn(((damg - offset_x) * ((base - 1) / intersect_x)) + 1, base);
+}
+
+// mutator hooks
+MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
+{
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(6, float);
+       float frag_damage = M_ARGV(7, float);
+
+       if(frag_deathtype == DEATH_BUFF.m_id) { return; }
+
+       if(frag_target.buffs & BUFF_RESISTANCE.m_itemid)
+       {
+               vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
+               M_ARGV(4, float) = v.x; // take
+               M_ARGV(5, float) = v.y; // save
+       }
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(3, float);
+       float frag_damage = M_ARGV(4, float);
+       vector frag_force = M_ARGV(6, vector);
+
+       if(frag_deathtype == DEATH_BUFF.m_id) { return; }
+
+       if(frag_target.buffs & BUFF_SPEED.m_itemid)
+       if(frag_target != frag_attacker)
+               frag_damage *= autocvar_g_buffs_speed_damage_take;
+
+       if(frag_target.buffs & BUFF_MEDIC.m_itemid)
+       if((frag_target.health - frag_damage) <= 0)
+       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       if(frag_attacker)
+       if(random() <= autocvar_g_buffs_medic_survive_chance)
+               frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health);
+
+       if(frag_target.buffs & BUFF_JUMP.m_itemid)
+       if(frag_deathtype == DEATH_FALL.m_id)
+               frag_damage = 0;
+
+       if(frag_target.buffs & BUFF_VENGEANCE.m_itemid)
+       if(frag_attacker)
+       if(frag_attacker != frag_target)
+       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       {
+               entity dmgent = spawn();
+
+               dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
+               dmgent.enemy = frag_attacker;
+               dmgent.owner = frag_target;
+               setthink(dmgent, buff_Vengeance_DelayedDamage);
+               dmgent.nextthink = time + 0.1;
+       }
+
+       if(frag_target.buffs & BUFF_BASH.m_itemid)
+       if(frag_attacker != frag_target)
+               frag_force = '0 0 0';
+
+       if(frag_attacker.buffs & BUFF_BASH.m_itemid)
+       if(frag_force)
+       if(frag_attacker == frag_target)
+               frag_force *= autocvar_g_buffs_bash_force_self;
+       else
+               frag_force *= autocvar_g_buffs_bash_force;
+
+       if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid)
+       if(frag_target != frag_attacker)
+               frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime;
+
+       if(frag_target.buffs & BUFF_INFERNO.m_itemid)
+       {
+               if(frag_deathtype == DEATH_FIRE.m_id)
+                       frag_damage = 0;
+               if(frag_deathtype == DEATH_LAVA.m_id)
+                       frag_damage *= 0.5; // TODO: cvarize?
+       }
+
+       if(frag_attacker.buffs & BUFF_LUCK.m_itemid)
+       if(frag_attacker != frag_target)
+       if(autocvar_g_buffs_luck_damagemultiplier > 0)
+       if(random() <= autocvar_g_buffs_luck_chance)
+               frag_damage *= autocvar_g_buffs_luck_damagemultiplier;
+
+       if(frag_attacker.buffs & BUFF_INFERNO.m_itemid)
+       if(frag_target != frag_attacker) {
+               float btime = buff_Inferno_CalculateTime(
+                       frag_damage,
+                       0,
+                       autocvar_g_buffs_inferno_burntime_min_time,
+                       autocvar_g_buffs_inferno_burntime_target_damage,
+                       autocvar_g_buffs_inferno_burntime_target_time,
+                       autocvar_g_buffs_inferno_burntime_factor
+               );
+               Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id);
+       }
+
+       // this... is ridiculous (TODO: fix!)
+       if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid)
+       if(!frag_target.vehicle)
+       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       if(!IS_DEAD(frag_target))
+       if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
+       if(frag_attacker != frag_target)
+       if(!STAT(FROZEN, frag_target))
+       if(frag_target.takedamage)
+       if(DIFF_TEAM(frag_attacker, frag_target))
+       {
+               frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
+               if(frag_target.armorvalue)
+                       frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max);
+       }
+
+       M_ARGV(4, float) = frag_damage;
+       M_ARGV(6, vector) = frag_force;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.buffs = 0;
+       player.buff_time = 0;
+       // reset timers here to prevent them continuing after re-spawn
+       player.buff_disability_time = 0;
+       player.buff_disability_effect_time = 0;
+}
+
+.float stat_sv_maxspeed;
+.float stat_sv_airspeedlimit_nonqw;
+.float stat_sv_jumpvelocity;
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.buffs & BUFF_SPEED.m_itemid)
+       {
+               player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
+               player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
+       }
+
+       if(time < player.buff_disability_time)
+       {
+               player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
+               player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
+       }
+
+       if(player.buffs & BUFF_JUMP.m_itemid)
+       {
+               // automatically reset, no need to worry
+               player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.buffs & BUFF_JUMP.m_itemid)
+               M_ARGV(1, float) = autocvar_g_buffs_jump_height;
+
+       if(player.buffs & BUFF_FLIGHT.m_itemid)
+       if(!IS_JUMP_HELD(player) && PHYS_INPUT_BUTTON_CROUCH(player))
+               player.gravity *= -1;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
+{
+       entity mon = M_ARGV(0, entity);
+
+       if(time < mon.buff_disability_time)
+       {
+               M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed
+               M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed
+       }
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
+{
+       entity frag_target = M_ARGV(2, entity);
+
+       if(frag_target.buffs)
+       {
+               int buffid = buff_FirstFromFlags(frag_target.buffs).m_id;
+               Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid);
+               frag_target.buffs = 0;
+
+               if(frag_target.buff_model)
+               {
+                       delete(frag_target.buff_model);
+                       frag_target.buff_model = NULL;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
+{
+       if(MUTATOR_RETURNVALUE || gameover) { return; }
+
+       entity player = M_ARGV(0, entity);
+
+       if(player.buffs)
+       {
+               int buffid = buff_FirstFromFlags(player.buffs).m_id;
+               Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid);
+               Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
+
+               player.buffs = 0;
+               player.buff_time = 0; // already notified
+               sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+               return true;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
+{
+       if(MUTATOR_RETURNVALUE || gameover) { return; }
+       entity player = M_ARGV(0, entity);
+
+       if(player.buffs & BUFF_SWAPPER.m_itemid)
+       {
+               float best_distance = autocvar_g_buffs_swapper_range;
+               entity closest = NULL;
+               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+                       if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle)
+                       if(DIFF_TEAM(it, player))
+                       {
+                               float test = vlen2(player.origin - it.origin);
+                               if(test <= best_distance * best_distance)
+                               {
+                                       best_distance = sqrt(test);
+                                       closest = it;
+                               }
+                       }
+               ));
+
+               if(closest)
+               {
+                       vector my_org, my_vel, my_ang, their_org, their_vel, their_ang;
+
+                       my_org = player.origin;
+                       my_vel = player.velocity;
+                       my_ang = player.angles;
+                       their_org = closest.origin;
+                       their_vel = closest.velocity;
+                       their_ang = closest.angles;
+
+                       Drop_Special_Items(closest);
+
+                       MUTATOR_CALLHOOK(PortalTeleport, player); // initiate flag dropper
+
+                       setorigin(player, their_org);
+                       setorigin(closest, my_org);
+
+                       closest.velocity = my_vel;
+                       closest.angles = my_ang;
+                       closest.fixangle = true;
+                       closest.oldorigin = my_org;
+                       closest.oldvelocity = my_vel;
+                       player.velocity = their_vel;
+                       player.angles = their_ang;
+                       player.fixangle = true;
+                       player.oldorigin = their_org;
+                       player.oldvelocity = their_vel;
+
+                       // set pusher so player gets the kill if they fall into void
+                       closest.pusher = player;
+                       closest.pushltime = time + autocvar_g_maxpushtime;
+                       closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest);
+
+                       Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1);
+                       Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1);
+
+                       sound(player, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
+                       sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
+
+                       // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam
+                       player.buffs = 0;
+                       return true;
+               }
+       }
+}
+
+bool buffs_RemovePlayer(entity player)
+{
+       if(player.buff_model)
+       {
+               delete(player.buff_model);
+               player.buff_model = NULL;
+       }
+
+       // also reset timers here to prevent them continuing after spectating
+       player.buff_disability_time = 0;
+       player.buff_disability_effect_time = 0;
+
+       return false;
+}
+MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); }
+MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); }
+
+MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
+{
+       entity wp = M_ARGV(0, entity);
+       entity player = M_ARGV(1, entity);
+
+       entity e = WaypointSprite_getviewentity(player);
+
+       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
+       // but only apply this to real players, not to spectators
+       if((wp.owner.flags & FL_CLIENT) && (wp.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == player))
+       if(DIFF_TEAM(wp.owner, e))
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
+{
+       if(autocvar_g_buffs < 0)
+               return; // no auto replacing of entities in this mode
+
+       entity ent = M_ARGV(0, entity);
+
+       if(autocvar_g_buffs_replace_powerups)
+       switch(ent.classname)
+       {
+               case "item_strength":
+               case "item_invincible":
+               {
+                       entity e = spawn();
+                       buff_SpawnReplacement(e, ent);
+                       return true;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor)
+{
+       entity player = M_ARGV(1, entity);
+
+       if(player.buffs & BUFF_SPEED.m_itemid)
+               M_ARGV(0, float) *= autocvar_g_buffs_speed_rate;
+
+       if(time < player.buff_disability_time)
+               M_ARGV(0, float) *= autocvar_g_buffs_disability_rate;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor)
+{
+       entity player = M_ARGV(1, entity);
+
+       if(player.buffs & BUFF_SPEED.m_itemid)
+               M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed;
+
+       if(time < player.buff_disability_time)
+               M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(gameover || IS_DEAD(player)) { return; }
+
+       if(time < player.buff_disability_time)
+       if(time >= player.buff_disability_effect_time)
+       {
+               Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
+               player.buff_disability_effect_time = time + 0.5;
+       }
+
+       // handle buff lost status
+       // 1: notify everyone else
+       // 2: notify carrier as well
+       int buff_lost = 0;
+
+       if(player.buff_time && player.buffs)
+       if(time >= player.buff_time)
+       {
+               player.buff_time = 0;
+               buff_lost = 2;
+       }
+
+       if(STAT(FROZEN, player)) { buff_lost = 1; }
+
+       if(buff_lost)
+       {
+               if(player.buffs)
+               {
+                       int buffid = buff_FirstFromFlags(player.buffs).m_id;
+                       if(buff_lost == 2)
+                       {
+                               Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message?
+                               sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+                       }
+                       else
+                               Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
+                       player.buffs = 0;
+               }
+       }
+
+       if(player.buffs & BUFF_MAGNET.m_itemid)
+       {
+               vector pickup_size;
+               IL_EACH(g_items, it.itemdef,
+               {
+                       if(it.buffs)
+                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff;
+                       else
+                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
+
+                       if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax))
+                       {
+                               if(gettouch(it))
+                                       gettouch(it)(it, player);
+                       }
+               });
+       }
+
+       if(player.buffs & BUFF_AMMO.m_itemid)
+       if(player.clip_size)
+               player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
+
+       if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid))
+       if(player.alpha != autocvar_g_buffs_invisible_alpha)
+               player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO)
+
+       if(player.buffs & BUFF_MEDIC.m_itemid)
+       if(time >= player.buff_medic_healtime)
+       {
+               buff_Medic_Heal(player);
+               player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay;
+       }
+
+#define BUFF_ONADD(b) if ( (player.buffs & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid))
+#define BUFF_ONREM(b) if (!(player.buffs & (b).m_itemid) &&  (player.oldbuffs & (b).m_itemid))
+
+       if(player.buffs != player.oldbuffs)
+       {
+               entity buff = buff_FirstFromFlags(player.buffs);
+               float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0;
+               player.buff_time = (bufftime) ? time + bufftime : 0;
+
+               BUFF_ONADD(BUFF_AMMO)
+               {
+                       player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO);
+                       player.items |= IT_UNLIMITED_WEAPON_AMMO;
+
+                       if(player.clip_load)
+                               player.buff_ammo_prev_clipload = player.clip_load;
+                       player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
+               }
+
+               BUFF_ONREM(BUFF_AMMO)
+               {
+                       if(player.buff_ammo_prev_infitems)
+                               player.items |= IT_UNLIMITED_WEAPON_AMMO;
+                       else
+                               player.items &= ~IT_UNLIMITED_WEAPON_AMMO;
+
+                       if(player.buff_ammo_prev_clipload)
+                               player.clip_load = player.buff_ammo_prev_clipload;
+               }
+
+               BUFF_ONADD(BUFF_INVISIBLE)
+               {
+                       if(time < player.strength_finished && g_instagib)
+                               player.alpha = autocvar_g_instagib_invis_alpha;
+                       else
+                               player.alpha = player.buff_invisible_prev_alpha;
+                       player.alpha = autocvar_g_buffs_invisible_alpha;
+               }
+
+               BUFF_ONREM(BUFF_INVISIBLE)
+                       player.alpha = player.buff_invisible_prev_alpha;
+
+               BUFF_ONADD(BUFF_FLIGHT)
+               {
+                       player.buff_flight_oldgravity = player.gravity;
+                       if(!player.gravity)
+                               player.gravity = 1;
+               }
+
+               BUFF_ONREM(BUFF_FLIGHT)
+                       player.gravity = ((player.trigger_gravity_check) ? player.trigger_gravity_check.enemy.gravity : player.buff_flight_oldgravity);
+
+               player.oldbuffs = player.buffs;
+               if(player.buffs)
+               {
+                       if(!player.buff_model)
+                               buffs_BuffModel_Spawn(player);
+
+                       player.buff_model.color = buff.m_color;
+                       player.buff_model.glowmod = buff_GlowColor(player.buff_model);
+                       player.buff_model.skin = buff.m_skin;
+
+                       player.effects |= EF_NOSHADOW;
+               }
+               else
+               {
+                       delete(player.buff_model);
+                       player.buff_model = NULL;
+
+                       player.effects &= ~(EF_NOSHADOW);
+               }
+       }
+
+       if(player.buff_model)
+       {
+               player.buff_model.effects = player.effects;
+               player.buff_model.effects |= EF_LOWPRECISION;
+               player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance
+
+               player.buff_model.alpha = player.alpha;
+       }
+
+#undef BUFF_ONADD
+#undef BUFF_ONREM
+}
+
+MUTATOR_HOOKFUNCTION(buffs, SpectateCopy)
+{
+       entity spectatee = M_ARGV(0, entity);
+       entity client = M_ARGV(1, entity);
+
+       client.buffs = spectatee.buffs;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, VehicleEnter)
+{
+       entity player = M_ARGV(0, entity);
+       entity veh = M_ARGV(1, entity);
+
+       veh.buffs = player.buffs;
+       player.buffs = 0;
+       veh.buff_time = max(0, player.buff_time - time);
+       player.buff_time = 0;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, VehicleExit)
+{
+       entity player = M_ARGV(0, entity);
+       entity veh = M_ARGV(1, entity);
+
+       player.buffs = player.oldbuffs = veh.buffs;
+       veh.buffs = 0;
+       player.buff_time = time + veh.buff_time;
+       veh.buff_time = 0;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.buffs & BUFF_MEDIC.m_itemid)
+       {
+               M_ARGV(2, float) = autocvar_g_buffs_medic_rot; // rot_mod
+               M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod
+               M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod
+       }
+
+       if(player.buffs & BUFF_SPEED.m_itemid)
+               M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod
+}
+
+REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace");
+
+MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
+{
+       if(autocvar_g_buffs > 0) // only report as a mutator if they're enabled
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
+}
+
+MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
+{
+       if(autocvar_g_buffs > 0)
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
+}
+
+void buffs_DelayedInit(entity this)
+{
+       if(autocvar_g_buffs_spawn_count > 0)
+       if(find(NULL, classname, "item_buff") == NULL)
+       {
+               float i;
+               for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
+               {
+                       entity e = spawn();
+                       e.spawnflags |= 64; // always randomize
+                       e.velocity = randomvec() * 250; // this gets reset anyway if random location works
+                       buff_Init(e);
+               }
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
new file mode 100644 (file)
index 0000000..8383a72
--- /dev/null
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "buffs.qh"
+
+#include "../instagib/_mod.qh"
+
+bool  autocvar_g_buffs_effects;
+float autocvar_g_buffs_waypoint_distance;
+bool autocvar_g_buffs_randomize;
+float autocvar_g_buffs_random_lifetime;
+bool autocvar_g_buffs_random_location;
+int autocvar_g_buffs_random_location_attempts;
+int autocvar_g_buffs_spawn_count;
+bool autocvar_g_buffs_replace_powerups;
+float autocvar_g_buffs_cooldown_activate;
+float autocvar_g_buffs_cooldown_respawn;
+float autocvar_g_buffs_resistance_blockpercent;
+float autocvar_g_buffs_medic_survive_chance;
+float autocvar_g_buffs_medic_survive_health;
+float autocvar_g_buffs_medic_rot;
+float autocvar_g_buffs_medic_max;
+float autocvar_g_buffs_medic_regen;
+float autocvar_g_buffs_medic_heal_amount = 15;
+float autocvar_g_buffs_medic_heal_delay = 1;
+float autocvar_g_buffs_medic_heal_range = 400;
+float autocvar_g_buffs_vengeance_damage_multiplier;
+float autocvar_g_buffs_bash_force;
+float autocvar_g_buffs_bash_force_self;
+float autocvar_g_buffs_disability_slowtime;
+float autocvar_g_buffs_disability_speed;
+float autocvar_g_buffs_disability_rate;
+float autocvar_g_buffs_disability_weaponspeed;
+float autocvar_g_buffs_speed_speed;
+float autocvar_g_buffs_speed_rate;
+float autocvar_g_buffs_speed_weaponspeed;
+float autocvar_g_buffs_speed_damage_take;
+float autocvar_g_buffs_speed_regen;
+float autocvar_g_buffs_vampire_damage_steal;
+float autocvar_g_buffs_invisible_alpha;
+float autocvar_g_buffs_jump_height;
+float autocvar_g_buffs_inferno_burntime_factor;
+float autocvar_g_buffs_inferno_burntime_min_time;
+float autocvar_g_buffs_inferno_burntime_target_damage;
+float autocvar_g_buffs_inferno_burntime_target_time;
+float autocvar_g_buffs_inferno_damagemultiplier;
+float autocvar_g_buffs_swapper_range;
+float autocvar_g_buffs_magnet_range_item;
+float autocvar_g_buffs_magnet_range_buff = 200;
+float autocvar_g_buffs_luck_chance = 0.15;
+float autocvar_g_buffs_luck_damagemultiplier = 3;
+
+// ammo
+.float buff_ammo_prev_infitems;
+.int buff_ammo_prev_clipload;
+// invisible
+.float buff_invisible_prev_alpha;
+// medic
+.float buff_medic_healtime;
+// disability
+.float buff_disability_time;
+.float buff_disability_effect_time;
+// flight
+.float buff_flight_oldgravity;
+// common buff variables
+.float buff_effect_delay;
+
+// buff definitions
+.float buff_active;
+.float buff_activetime;
+.float buff_activetime_updated;
+.entity buff_waypoint;
+.int oldbuffs; // for updating effects
+.entity buff_model; // controls effects (TODO: make csqc)
+
+const vector BUFF_MIN = ('-16 -16 0');
+const vector BUFF_MAX = ('16 16 60');
+
+// client side options
+.float cvar_cl_buffs_autoreplace;
index 96490700e567ee5eb356678641f334e204639021..a67e9455d9233640030bd1e0b879ff7b980ee0e8 100644 (file)
@@ -1,4 +1,7 @@
-#ifdef IMPLEMENTATION
+#include "bugrigs.qh"
+
+#ifdef GAMEQC
+
 #ifdef SVQC
        #include <server/antilag.qh>
 #endif
@@ -60,7 +63,7 @@ void bugrigs_SetVars()
 
 #endif
 
-void RaceCarPhysics(entity this)
+void RaceCarPhysics(entity this, float dt)
 {
        // using this move type for "big rigs"
        // the engine does not push the entity!
@@ -114,35 +117,35 @@ void RaceCarPhysics(entity this)
                {
                        if (myspeed > 0)
                        {
-                               myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
+                               myspeed = max(0, myspeed - dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
                        }
                        else
                        {
                                if (!PHYS_BUGRIGS_REVERSE_SPEEDING(this))
-                                       myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR(this));
+                                       myspeed = min(0, myspeed + dt * PHYS_BUGRIGS_FRICTION_FLOOR(this));
                        }
                }
                else
                {
                        if (myspeed >= 0)
                        {
-                               myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR(this));
+                               myspeed = max(0, myspeed - dt * PHYS_BUGRIGS_FRICTION_FLOOR(this));
                        }
                        else
                        {
                                if (PHYS_BUGRIGS_REVERSE_STOPPING(this))
                                        myspeed = 0;
                                else
-                                       myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
+                                       myspeed = min(0, myspeed + dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
                        }
                }
                // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec
                //MAXIMA: friction(v) := PHYS_BUGRIGS_FRICTION_FLOOR(this);
 
-               this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
+               this.angles_y += steer * dt * steerfactor; // apply steering
                makevectors(this.angles); // new forward direction!
 
-               myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH;
+               myspeed += accel * accelfactor * dt;
 
                rigvel = myspeed * v_forward + '0 0 1' * upspeed;
        }
@@ -153,13 +156,13 @@ void RaceCarPhysics(entity this)
                // responsiveness factor for steering and acceleration
                float f = 1 / (1 + pow(max(0, myspeed / PHYS_BUGRIGS_SPEED_REF(this)), PHYS_BUGRIGS_SPEED_POW(this)));
                float steerfactor = -myspeed * f;
-               this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
+               this.angles_y += steer * dt * steerfactor; // apply steering
 
                rigvel = this.velocity;
                makevectors(this.angles); // new forward direction!
        }
 
-       rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * PHYS_INPUT_TIMELENGTH);
+       rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * dt);
        //MAXIMA: airfriction(v) := v * v * PHYS_BUGRIGS_FRICTION_AIR(this);
        //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);
        //MAXIMA: solve(total_acceleration(v) = 0, v);
@@ -169,7 +172,7 @@ void RaceCarPhysics(entity this)
                vector rigvel_xy, neworigin, up;
                float mt;
 
-               rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY(this); // 4x gravity plays better
+               rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better
                rigvel_xy = vec2(rigvel);
 
                if (PHYS_BUGRIGS_CAR_JUMPING(this))
@@ -182,10 +185,10 @@ void RaceCarPhysics(entity this)
 
                // BUG RIGS: align the move to the surface instead of doing collision testing
                // can we move?
-               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, this);
+               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * dt, mt, this);
 
                // align to surface
-               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, this);
+               tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * dt, mt, this);
 
                if (trace_fraction < 0.5)
                {
@@ -213,14 +216,14 @@ void RaceCarPhysics(entity this)
                        UNSET_ONGROUND(this);
                }
 
-               this.velocity = (neworigin - this.origin) * (1.0 / PHYS_INPUT_TIMELENGTH);
-               this.movetype = MOVETYPE_NOCLIP;
+               this.velocity = (neworigin - this.origin) * (1.0 / dt);
+               set_movetype(this, MOVETYPE_NOCLIP);
        }
        else
        {
-               rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY(this); // 4x gravity plays better
+               rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better
                this.velocity = rigvel;
-               this.movetype = MOVETYPE_FLY;
+               set_movetype(this, MOVETYPE_FLY);
        }
 
        trace_fraction = 1;
@@ -251,7 +254,7 @@ void RaceCarPhysics(entity this)
        // smooth the angles
        vector vf1, vu1, smoothangles;
        makevectors(this.angles);
-       float f = bound(0, PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1);
+       float f = bound(0, dt * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1);
        if (f == 0)
                f = 1;
        vf1 = v_forward * f;
@@ -262,8 +265,6 @@ void RaceCarPhysics(entity this)
        smoothangles = vectoangles2(vf1, vu1);
        this.angles_x = -smoothangles_x;
        this.angles_z =  smoothangles_z;
-
-       PM_ClientMovement_Move(this);
 }
 
 #ifdef SVQC
@@ -272,6 +273,7 @@ void RaceCarPhysics(entity this)
 MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics)
 {
     entity player = M_ARGV(0, entity);
+    float dt = M_ARGV(2, float);
 
        if(!PHYS_BUGRIGS(player) || !IS_PLAYER(player)) { return; }
 
@@ -279,7 +281,7 @@ MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics)
        player.angles = player.bugrigs_prevangles;
 #endif
 
-       RaceCarPhysics(player);
+       RaceCarPhysics(player, dt);
        return true;
 }
 
@@ -312,4 +314,5 @@ MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsPrettyString)
 }
 
 #endif
+
 #endif
diff --git a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/bugrigs/module.inc b/qcsrc/common/mutators/mutator/bugrigs/module.inc
deleted file mode 100644 (file)
index cef744f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifndef MENUQC
-#include "bugrigs.qc"
-#endif
index 3ddb376fc9db5f685cc219bf52ecd0332b37f5c6..07c2ae2722fda8db1ff7c0d6aa088d71edeb1192 100644 (file)
@@ -1,2 +1,5 @@
 // generated file; do not modify
 #include <common/mutators/mutator/campcheck/campcheck.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/campcheck/sv_campcheck.qc>
+#endif
index 81345f1cff98c0a79d02e339007a63ff5550a1d8..c52811129b200a6b14867af6d9bca7fc01d3c38a 100644 (file)
@@ -1,2 +1,5 @@
 // generated file; do not modify
 #include <common/mutators/mutator/campcheck/campcheck.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/campcheck/sv_campcheck.qh>
+#endif
index 191f61681583ab70092cde44e2eb49ff2fb9a18e..822d4af7808ea0757d4c3dd67bab12f520fcb681 100644 (file)
@@ -1,91 +1 @@
-#ifdef IMPLEMENTATION
-float autocvar_g_campcheck_damage;
-float autocvar_g_campcheck_distance;
-float autocvar_g_campcheck_interval;
-
-REGISTER_MUTATOR(campcheck, cvar("g_campcheck"));
-
-.float campcheck_nextcheck;
-.float campcheck_traveled_distance;
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerDies)
-{
-       entity frag_target = M_ARGV(2, entity);
-       
-       Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK);
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-
-       if(IS_PLAYER(frag_target))
-       if(IS_PLAYER(frag_attacker))
-       if(frag_attacker != frag_target)
-       {
-               frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance;
-               frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(!gameover)
-       if(!warmup_stage) // don't consider it camping during warmup?
-       if(time >= game_starttime)
-       if(IS_PLAYER(player))
-       if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them
-       if(!IS_DEAD(player))
-       if(!STAT(FROZEN, player))
-       if(!PHYS_INPUT_BUTTON_CHAT(player))
-       if(autocvar_g_campcheck_interval)
-       {
-               vector dist;
-
-               // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
-               dist = player.prevorigin - player.origin;
-               dist.z = 0;
-               player.campcheck_traveled_distance += fabs(vlen(dist));
-
-               if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
-               {
-                       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
-                       player.campcheck_traveled_distance = 0;
-               }
-
-               if(time > player.campcheck_nextcheck)
-               {
-                       if(player.campcheck_traveled_distance < autocvar_g_campcheck_distance)
-                       {
-                               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CAMPCHECK);
-                               if(player.vehicle)
-                                       Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0');
-                               else
-                                       Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0');
-                       }
-                       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval;
-                       player.campcheck_traveled_distance = 0;
-               }
-
-               return;
-       }
-
-       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
-       player.campcheck_traveled_distance = 0;
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":CampCheck");;
-}
-#endif
+#include "campcheck.qh"
diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qh b/qcsrc/common/mutators/mutator/campcheck/campcheck.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/campcheck/module.inc b/qcsrc/common/mutators/mutator/campcheck/module.inc
deleted file mode 100644 (file)
index 9f4181f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "campcheck.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
new file mode 100644 (file)
index 0000000..c4d2e03
--- /dev/null
@@ -0,0 +1,92 @@
+#include "sv_campcheck.qh"
+
+float autocvar_g_campcheck_damage;
+float autocvar_g_campcheck_distance;
+float autocvar_g_campcheck_interval;
+
+REGISTER_MUTATOR(campcheck, cvar("g_campcheck"));
+
+.float campcheck_nextcheck;
+.float campcheck_traveled_distance;
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerDies)
+{
+       entity frag_target = M_ARGV(2, entity);
+
+       Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK);
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+
+       if(IS_PLAYER(frag_target))
+       if(IS_PLAYER(frag_attacker))
+       if(frag_attacker != frag_target)
+       {
+               frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance;
+               frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(!gameover)
+       if(!warmup_stage) // don't consider it camping during warmup?
+       if(time >= game_starttime)
+       if(IS_PLAYER(player))
+       if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them
+       if(!IS_DEAD(player))
+       if(!forbidWeaponUse(player))
+       if(!STAT(FROZEN, player))
+       if(!PHYS_INPUT_BUTTON_CHAT(player))
+       if(autocvar_g_campcheck_interval)
+       {
+               vector dist;
+
+               // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
+               dist = player.prevorigin - player.origin;
+               dist.z = 0;
+               player.campcheck_traveled_distance += fabs(vlen(dist));
+
+               if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
+               {
+                       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
+                       player.campcheck_traveled_distance = 0;
+               }
+
+               if(time > player.campcheck_nextcheck)
+               {
+                       if(player.campcheck_traveled_distance < autocvar_g_campcheck_distance)
+                       {
+                               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CAMPCHECK);
+                               if(player.vehicle)
+                                       Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0');
+                               else
+                                       Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0');
+                       }
+                       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval;
+                       player.campcheck_traveled_distance = 0;
+               }
+
+               return;
+       }
+
+       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
+       player.campcheck_traveled_distance = 0;
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":CampCheck");
+}
diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 72136952258113fec67b40cba93287bfc3b3a6a5..34fdea74797b23989b222c2b98ae907873338b80 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/cloaked/cloaked.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/cloaked/sv_cloaked.qc>
+#endif
index 5606b9aafd5edb118d1d44713cdd8dcaee399a3f..b225a2be5b0757d6bfe5e4f43f2e0022567c64f5 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/cloaked/cloaked.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/cloaked/sv_cloaked.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/cloaked/cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/cloaked.qc
deleted file mode 100644 (file)
index 4fca532..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifdef IMPLEMENTATION
-
-REGISTER_MUTATOR(cloaked, cvar("g_cloaked"));
-
-float autocvar_g_balance_cloaked_alpha;
-
-MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha)
-{
-    default_player_alpha = autocvar_g_balance_cloaked_alpha;
-    default_weapon_alpha = default_player_alpha;
-    return true;
-}
-
-MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString)
-{
-       if (!g_cts) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Cloaked");
-}
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/cloaked/module.inc b/qcsrc/common/mutators/mutator/cloaked/module.inc
deleted file mode 100644 (file)
index 3d3a042..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "cloaked.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc
new file mode 100644 (file)
index 0000000..eb45d4b
--- /dev/null
@@ -0,0 +1,17 @@
+#include "sv_cloaked.qh"
+
+REGISTER_MUTATOR(cloaked, cvar("g_cloaked"));
+
+float autocvar_g_balance_cloaked_alpha;
+
+MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha)
+{
+    default_player_alpha = autocvar_g_balance_cloaked_alpha;
+    default_weapon_alpha = default_player_alpha;
+    return true;
+}
+
+MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString)
+{
+       if (!g_cts) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Cloaked");
+}
diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 2a1795d63c94afad39c367c52eb0decd51172575..7f2b740ba56aa6e1d654644406f4f5e656d583a3 100644 (file)
-#ifndef MUTATOR_DAMAGETEXT_H
-#define MUTATOR_DAMAGETEXT_H
+#include "damagetext.qh"
 
-#ifdef MENUQC
-#include <menu/xonotic/tab.qh>
-#endif
+#define DAMAGETEXT_PRECISION_MULTIPLIER 128
+#define DAMAGETEXT_SHORT_LIMIT 256 // the smallest value that we can't send as short - 2^15 (signed short) / DAMAGETEXT_PRECISION_MULTIPLIER
 
-#endif
+const int DTFLAG_SAMETEAM = BIT(0);
+const int DTFLAG_BIG_HEALTH = BIT(1);
+const int DTFLAG_BIG_ARMOR = BIT(2);
+const int DTFLAG_BIG_POTENTIAL = BIT(3);
+const int DTFLAG_NO_ARMOR = BIT(4);
+const int DTFLAG_NO_POTENTIAL = BIT(5);
 
-#ifdef IMPLEMENTATION
 REGISTER_MUTATOR(damagetext, true);
 
-#if defined(CSQC) || defined(MENUQC)
+// || defined(MENUQC)
+#if defined(CSQC)
 // no translatable cvar description please
-AUTOCVAR_SAVE(cl_damagetext,                    bool,   false,      "Draw damage dealt where you hit the enemy");
-AUTOCVAR_SAVE(cl_damagetext_format,             string, "-{total}", "How to format the damage text. {health}, {armor}, {total}");
+AUTOCVAR_SAVE(cl_damagetext,                        bool,   true,       "Draw damage dealt where you hit the enemy");
+AUTOCVAR_SAVE(cl_damagetext_format,                 string, "-{total}", "How to format the damage text. {health}, {armor}, {total}, {potential}: full damage not capped to target's health, {potential_health}: health damage not capped to target's health");
+AUTOCVAR_SAVE(cl_damagetext_format_verbose,         bool,   false,      "{health} shows {potential_health} too when they differ; {total} shows {potential} too when they differ");
 STATIC_INIT(DamageText_LegacyFormat) {
     if (strstrofs(autocvar_cl_damagetext_format, "{", 0) < 0) autocvar_cl_damagetext_format = "-{total}";
 }
-AUTOCVAR_SAVE(cl_damagetext_color,              vector, '1 1 0',    "Damage text color");
-AUTOCVAR_SAVE(cl_damagetext_color_per_weapon,   bool,   false,      "Damage text uses weapon color");
-AUTOCVAR_SAVE(cl_damagetext_size,               float,  8,          "Damage text font size");
-AUTOCVAR_SAVE(cl_damagetext_alpha_start,        float,  1,          "Damage text initial alpha");
-AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime,     float,  3,          "Damage text lifetime in seconds");
-AUTOCVAR_SAVE(cl_damagetext_velocity,           vector, '0 0 20',   "Damage text move direction");
-AUTOCVAR_SAVE(cl_damagetext_offset,             vector, '0 -40 0',  "Damage text offset");
-AUTOCVAR_SAVE(cl_damagetext_accumulate_range,   float,  30,         "Damage text spawned within this range is accumulated");
+AUTOCVAR_SAVE(cl_damagetext_color,                  vector, '1 1 0',    "Damage text color");
+AUTOCVAR_SAVE(cl_damagetext_color_per_weapon,       bool,   false,      "Damage text uses weapon color");
+AUTOCVAR_SAVE(cl_damagetext_size,                   float,  8,          "Damage text font size");
+AUTOCVAR_SAVE(cl_damagetext_alpha_start,            float,  1,          "Damage text initial alpha");
+AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime,         float,  3,          "Damage text lifetime in seconds");
+AUTOCVAR_SAVE(cl_damagetext_velocity,               vector, '0 0 20',   "Damage text move direction");
+AUTOCVAR_SAVE(cl_damagetext_offset,                 vector, '0 -40 0',  "Damage text offset");
+AUTOCVAR_SAVE(cl_damagetext_accumulate_range,       float,  30,         "Damage text spawned within this range is accumulated");
+AUTOCVAR_SAVE(cl_damagetext_accumulate_alpha_rel,   float,  0.65,       "Only update existing damage text when it's above this much percentage (0 to 1) of the starting alpha");
+AUTOCVAR_SAVE(cl_damagetext_friendlyfire,           bool,   true,       "Show damage text for friendlyfire too");
+AUTOCVAR_SAVE(cl_damagetext_friendlyfire_color,     vector, '1 0 0',    "Damage text color for friendlyfire");
 #endif
 
 #ifdef CSQC
 CLASS(DamageText, Object)
-    ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color)
-    ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size)
-    ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start)
-    ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime)
-    ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity)
-    ATTRIB(DamageText, m_group, int, 0)
-    ATTRIB(DamageText, m_damage, int, 0)
-    ATTRIB(DamageText, m_armordamage, int, 0)
-    ATTRIB(DamageText, m_deathtype, int, 0)
-    ATTRIB(DamageText, time_prev, float, time)
+    ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color);
+    ATTRIB(DamageText, m_color_friendlyfire, vector, autocvar_cl_damagetext_friendlyfire_color);
+    ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size);
+    ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start);
+    ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime);
+    ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity);
+    ATTRIB(DamageText, m_group, int, 0);
+    ATTRIB(DamageText, m_friendlyfire, bool, false);
+    ATTRIB(DamageText, m_damage, int, 0);
+    ATTRIB(DamageText, m_armordamage, int, 0);
+    ATTRIB(DamageText, m_potential_damage, int, 0);
+    ATTRIB(DamageText, m_deathtype, int, 0);
+    ATTRIB(DamageText, time_prev, float, time);
 
     void DamageText_draw2d(DamageText this) {
         float dt = time - this.time_prev;
         this.time_prev = time;
         setorigin(this, this.origin + dt * this.velocity);
         this.alpha -= dt * this.fade_rate;
-        if (this.alpha < 0) remove(this);
+        if (this.alpha < 0) delete(this);
         vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset;
         if (pos.z >= 0 && this.m_size > 0) {
             pos.z = 0;
-            vector rgb = this.m_color;
+            vector rgb;
+            if (this.m_friendlyfire) {
+                rgb = this.m_color_friendlyfire;
+            }
+            else {
+                rgb = this.m_color;
+            }
             if (autocvar_cl_damagetext_color_per_weapon) {
                 Weapon w = DEATH_WEAPONOF(this.m_deathtype);
                 if (w != WEP_Null) rgb = w.wpcolor;
             }
+            int health = rint(this.m_damage / DAMAGETEXT_PRECISION_MULTIPLIER);
+            int total = rint((this.m_damage + this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER);
+            int potential = rint(this.m_potential_damage / DAMAGETEXT_PRECISION_MULTIPLIER);
+            int potential_health = rint((this.m_potential_damage - this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER);
+
             string s = autocvar_cl_damagetext_format;
-            s = strreplace("{health}", sprintf("%d", this.m_damage), s);
-            s = strreplace("{armor}",  sprintf("%d", this.m_armordamage), s);
-            s = strreplace("{total}",  sprintf("%d", this.m_damage + this.m_armordamage), s);
+            s = strreplace("{armor}",  sprintf("%d", rint(this.m_armordamage / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
+            s = strreplace("{potential}",  sprintf("%d", potential), s);
+            s = strreplace("{potential_health}",  sprintf("%d", potential_health), s);
+
+            s = strreplace("{health}", (
+                               (health == potential_health || !autocvar_cl_damagetext_format_verbose)
+                               ? sprintf("%d",      health)
+                               : sprintf("%d (%d)", health, potential_health)
+               ), s);
+            s = strreplace("{total}", (
+                               (total == potential || !autocvar_cl_damagetext_format_verbose)
+                               ? sprintf("%d",      total)
+                               : sprintf("%d (%d)", total, potential)
+               ), s);
             drawcolorcodedstring2_builtin(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL);
         }
     }
-    ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d)
+    ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d);
 
-    void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _deathtype) {
+    void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _potential_damage, int _deathtype) {
         this.m_damage = _health;
         this.m_armordamage = _armor;
+        this.m_potential_damage = _potential_damage;
         this.m_deathtype = _deathtype;
         setorigin(this, _origin);
-        this.alpha = 1;
+        this.alpha = autocvar_cl_damagetext_alpha_start;
     }
 
-    CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype) {
+    CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _potential_damage, int _deathtype, bool _friendlyfire) {
         CONSTRUCT(DamageText);
         this.m_group = _group;
-        DamageText_update(this, _origin, _health, _armor, _deathtype);
+        this.m_friendlyfire = _friendlyfire;
+        DamageText_update(this, _origin, _health, _armor, _potential_damage, _deathtype);
+               IL_PUSH(g_drawables_2d, this);
     }
 ENDCLASS(DamageText)
 #endif
@@ -91,9 +126,10 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
     if (SV_DAMAGETEXT_DISABLED()) return;
     const entity attacker = M_ARGV(0, entity);
     const entity hit = M_ARGV(1, entity); if (hit == attacker) return;
-    const int health = M_ARGV(2, int);
-    const int armor = M_ARGV(3, int);
+    const float health = M_ARGV(2, float);
+    const float armor = M_ARGV(3, float);
     const int deathtype = M_ARGV(5, int);
+    const float potential_damage = M_ARGV(6, float);
     const vector location = hit.origin;
     FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
         if (
@@ -102,15 +138,38 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
             (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(it) && it.enemy == attacker) ||
             (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(it))
         ) {
+            int flags = 0;
+            if (SAME_TEAM(hit, attacker)) flags |= DTFLAG_SAMETEAM;
+            if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_HEALTH;
+            if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_ARMOR;
+            if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_POTENTIAL;
+            if (!armor) flags |= DTFLAG_NO_ARMOR;
+            if (fabs((armor + health) - potential_damage) < 0.0001) flags |= DTFLAG_NO_POTENTIAL;
+
             msg_entity = it;
             WriteHeader(MSG_ONE, damagetext);
-            WriteShort(MSG_ONE, rint(health));
-            WriteShort(MSG_ONE, rint(armor));
             WriteEntity(MSG_ONE, hit);
             WriteCoord(MSG_ONE, location.x);
             WriteCoord(MSG_ONE, location.y);
             WriteCoord(MSG_ONE, location.z);
             WriteInt24_t(MSG_ONE, deathtype);
+            WriteByte(MSG_ONE, flags);
+
+            // we need to send a few decimal places to minimize errors when accumulating damage
+            // sending them multiplied saves bandwidth compared to using WriteCoord,
+            // however if the multiplied damage would be too much for (signed) short, we send an int24
+            if (flags & DTFLAG_BIG_HEALTH) WriteInt24_t(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+            else WriteShort(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+            if (!(flags & DTFLAG_NO_ARMOR))
+            {
+                if (flags & DTFLAG_BIG_ARMOR) WriteInt24_t(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
+                else WriteShort(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
+            }
+            if (!(flags & DTFLAG_NO_POTENTIAL))
+            {
+                if (flags & DTFLAG_BIG_POTENTIAL) WriteInt24_t(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
+                else WriteShort(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
+                       }
         }
     ));
 }
@@ -119,34 +178,50 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
 #ifdef CSQC
 NET_HANDLE(damagetext, bool isNew)
 {
-    int health = ReadShort();
-    int armor = ReadShort();
     int group = ReadShort();
     vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord());
     int deathtype = ReadInt24_t();
+    int flags = ReadByte();
+    bool friendlyfire = flags & DTFLAG_SAMETEAM;
+
+    int health, armor, potential_damage;
+    if (flags & DTFLAG_BIG_HEALTH) health = ReadInt24_t();
+    else health = ReadShort();
+    if (flags & DTFLAG_NO_ARMOR) armor = 0;
+    else if (flags & DTFLAG_BIG_ARMOR) armor = ReadInt24_t();
+    else armor = ReadShort();
+    if (flags & DTFLAG_NO_POTENTIAL) potential_damage = health + armor;
+    else if (flags & DTFLAG_BIG_POTENTIAL) potential_damage = ReadInt24_t();
+    else potential_damage = ReadShort();
+
     return = true;
     if (autocvar_cl_damagetext) {
+        if (friendlyfire && !autocvar_cl_damagetext_friendlyfire) {
+            return;
+        }
         if (autocvar_cl_damagetext_accumulate_range) {
             for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) {
-                if (e.instanceOfDamageText && e.m_group == group) {
-                    DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype);
+                if (e.instanceOfDamageText && e.m_group == group && e.alpha > autocvar_cl_damagetext_accumulate_alpha_rel * autocvar_cl_damagetext_alpha_start) {
+                    DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, e.m_potential_damage + potential_damage, deathtype);
                     return;
                 }
             }
         }
-        NEW(DamageText, group, location, health, armor, deathtype);
+        make_impure(NEW(DamageText, group, location, health, armor, potential_damage, deathtype, friendlyfire));
     }
 }
 #endif
 
 #ifdef MENUQC
+
+#include <menu/gamesettings.qh>
+
 CLASS(XonoticDamageTextSettings, XonoticTab)
-    #include <menu/gamesettings.qh>
     REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings));
-    ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text"))
-    ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9)
-    ATTRIB(XonoticDamageTextSettings, rows, float, 15.5)
-    ATTRIB(XonoticDamageTextSettings, columns, float, 5)
+    ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text"));
+    ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9);
+    ATTRIB(XonoticDamageTextSettings, rows, float, 15.5);
+    ATTRIB(XonoticDamageTextSettings, columns, float, 5);
     INIT(XonoticDamageTextSettings) { this.configureDialog(this); }
     METHOD(XonoticDamageTextSettings, showNotify, void(entity this)) { loadAllCvars(this); }
     METHOD(XonoticDamageTextSettings, fill, void(entity this))
@@ -174,7 +249,17 @@ CLASS(XonoticDamageTextSettings, XonoticTab)
                 setDependent(e, "cl_damagetext", 1, 1);
             this.TD(this, 2, 2, e = makeXonoticColorpickerString("cl_damagetext_color", "cl_damagetext_color"));
                 setDependent(e, "cl_damagetext", 1, 1);
+        this.TR(this);
+        this.TR(this);
+        // friendly fire
+            this.TD(this, 1, 3, e = makeXonoticCheckBox(0, "cl_damagetext_friendlyfire", _("Draw damage numbers for friendly fire")));
+                setDependent(e, "cl_damagetext", 1, 1);
+        this.TR(this);
+            this.TD(this, 1, 1, e = makeXonoticTextLabel(0, _("Color:")));
+                setDependentAND(e, "cl_damagetext", 1, 1, "cl_damagetext_friendlyfire", 1, 1);
+            this.TD(this, 2, 2, e = makeXonoticColorpickerString("cl_damagetext_friendlyfire_color", "cl_damagetext_friendlyfire_color"));
+                setDependentAND(e, "cl_damagetext", 1, 1, "cl_damagetext_friendlyfire", 1, 1);
+        this.TR(this);
     }
 ENDCLASS(XonoticDamageTextSettings)
 #endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qh b/qcsrc/common/mutators/mutator/damagetext/damagetext.qh
new file mode 100644 (file)
index 0000000..7228f37
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#ifdef MENUQC
+#include <menu/xonotic/tab.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/damagetext/module.inc b/qcsrc/common/mutators/mutator/damagetext/module.inc
deleted file mode 100644 (file)
index 8e70be7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "damagetext.qc"
index 4902d5fc1b36e0f34f3be37ebde5418e55306575..80a7828a703f409a707d36aac3b0e59fc8f02831 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/dodging/dodging.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/dodging/sv_dodging.qc>
+#endif
index b2b65f02f7afefee6e820ad90d2a3c7cf6b65cb7..fef7b8e6e4e7f289ea505ba469708bc116e3a280 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/dodging/dodging.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/dodging/sv_dodging.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/dodging/dodging.qc b/qcsrc/common/mutators/mutator/dodging/dodging.qc
deleted file mode 100644 (file)
index 0d20ff7..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-#ifdef IMPLEMENTATION
-
-#define PHYS_DODGING                                           STAT(DODGING, this)
-#define PHYS_DODGING_DELAY                                     STAT(DODGING_DELAY, this)
-#define PHYS_DODGING_DISTANCE_THRESHOLD        STAT(DODGING_DISTANCE_THRESHOLD, this)
-#define PHYS_DODGING_FROZEN_NODOUBLETAP                STAT(DODGING_FROZEN_NO_DOUBLETAP, this)
-#define PHYS_DODGING_HEIGHT_THRESHOLD          STAT(DODGING_HEIGHT_THRESHOLD, this)
-#define PHYS_DODGING_HORIZ_SPEED                       STAT(DODGING_HORIZ_SPEED, this)
-#define PHYS_DODGING_HORIZ_SPEED_FROZEN        STAT(DODGING_HORIZ_SPEED_FROZEN, this)
-#define PHYS_DODGING_RAMP_TIME                                 STAT(DODGING_RAMP_TIME, this)
-#define PHYS_DODGING_UP_SPEED                          STAT(DODGING_UP_SPEED, this)
-#define PHYS_DODGING_WALL                                      STAT(DODGING_WALL, this)
-#define PHYS_DODGING_PRESSED_KEYS(s)           (s).pressedkeys
-
-#ifdef CSQC
-       #define PHYS_DODGING_FRAMETIME                          (1 / (frametime <= 0 ? 60 : frametime))
-       #define PHYS_DODGING_TIMEOUT(s)                         STAT(DODGING_TIMEOUT)
-#elif defined(SVQC)
-       #define PHYS_DODGING_FRAMETIME                          sys_frametime
-       #define PHYS_DODGING_TIMEOUT(s)                         s.cvar_cl_dodging_timeout
-
-
-#endif
-
-#ifdef SVQC
-
-bool autocvar_sv_dodging_sound;
-
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
-#include <common/animdecide.qh>
-#include <common/physics/player.qh>
-
-.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT);
-
-REGISTER_MUTATOR(dodging, cvar("g_dodging"))
-{
-       // this just turns on the cvar.
-       MUTATOR_ONADD
-       {
-               g_dodging = cvar("g_dodging");
-       }
-
-       // this just turns off the cvar.
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               g_dodging = 0;
-       }
-
-       return false;
-}
-
-#elif defined(CSQC)
-REGISTER_MUTATOR(dodging, true);
-#endif
-
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
-
-// these are used to store the last key press time for each of the keys..
-.float last_FORWARD_KEY_time;
-.float last_BACKWARD_KEY_time;
-.float last_LEFT_KEY_time;
-.float last_RIGHT_KEY_time;
-
-// these store the movement direction at the time of the dodge action happening.
-.vector dodging_direction;
-
-// this indicates the last time a dodge was executed. used to check if another one is allowed
-// and to ramp up the dodge acceleration in the physics hook.
-.float last_dodging_time;
-
-// This is the velocity gain to be added over the ramp time.
-// It will decrease from frame to frame during dodging_action = 1
-// until it's 0.
-.float dodging_velocity_gain;
-
-#ifdef CSQC
-.int pressedkeys;
-#endif
-
-// returns 1 if the player is close to a wall
-bool check_close_to_wall(entity this, float threshold)
-{
-       if (PHYS_DODGING_WALL == 0) { return false; }
-
-#define X(OFFSET) \
-       tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \
-       if(trace_fraction < 1 && vdist(this.origin - trace_endpos, <, threshold)) \
-               return true;
-       X(1000*v_right);
-       X(-1000*v_right);
-       X(1000*v_forward);
-       X(-1000*v_forward);
-#undef X
-
-       return false;
-}
-
-bool check_close_to_ground(entity this, float threshold)
-{
-       return IS_ONGROUND(this) ? true : false;
-}
-
-float PM_dodging_checkpressedkeys(entity this)
-{
-       if(!PHYS_DODGING)
-               return false;
-
-       float frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
-       float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
-
-       // first check if the last dodge is far enough back in time so we can dodge again
-       if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY)
-               return false;
-
-       makevectors(this.angles);
-
-       if (check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) != 1
-               && check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
-               return true;
-
-       float tap_direction_x = 0;
-       float tap_direction_y = 0;
-       bool dodge_detected = false;
-
-       #define X(COND,BTN,RESULT)                                                                                                                      \
-       if (this.movement_##COND)                                                                                               \
-               /* is this a state change? */                                                                                                   \
-               if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) {             \
-                               tap_direction_##RESULT;                                                                                                 \
-                               if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap)    \
-                                       dodge_detected = true;                                                                                                  \
-                               this.last_##BTN##_KEY_time = time;                                                                              \
-               }
-       X(x < 0, BACKWARD,      x--);
-       X(x > 0, FORWARD,       x++);
-       X(y < 0, LEFT,          y--);
-       X(y > 0, RIGHT,         y++);
-       #undef X
-
-       if (dodge_detected)
-       {
-               this.last_dodging_time = time;
-
-               this.dodging_action = 1;
-               this.dodging_single_action = 1;
-
-               this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
-
-               this.dodging_direction_x = tap_direction_x;
-               this.dodging_direction_y = tap_direction_y;
-
-               // normalize the dodging_direction vector.. (unlike UT99) XD
-               float length = this.dodging_direction_x * this.dodging_direction_x
-                                       + this.dodging_direction_y * this.dodging_direction_y;
-               length = sqrt(length);
-
-               this.dodging_direction_x = this.dodging_direction_x * 1.0 / length;
-               this.dodging_direction_y = this.dodging_direction_y * 1.0 / length;
-               return true;
-       }
-       return false;
-}
-
-void PM_dodging(entity this)
-{
-       if (!PHYS_DODGING)
-               return;
-
-    if (IS_DEAD(this))
-        return;
-
-       // when swimming, no dodging allowed..
-       if (this.waterlevel >= WATERLEVEL_SWIMMING)
-       {
-               this.dodging_action = 0;
-               this.dodging_direction_x = 0;
-               this.dodging_direction_y = 0;
-               return;
-       }
-
-       // make sure v_up, v_right and v_forward are sane
-       makevectors(this.angles);
-
-       // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
-       // will be called ramp_time/frametime times = 2 times. so, we need to
-       // add 0.5 * the total speed each frame until the dodge action is done..
-       float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
-
-       // if ramp time is smaller than frametime we get problems ;D
-       common_factor = min(common_factor, 1);
-
-       float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
-       float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed);
-       new_velocity_gain = max(0, new_velocity_gain);
-
-       float velocity_difference = this.dodging_velocity_gain - new_velocity_gain;
-
-       // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
-       if (this.dodging_action == 1)
-       {
-               //disable jump key during dodge accel phase
-               if(this.movement_z > 0) { this.movement_z = 0; }
-
-               this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right)
-                                       + ((this.dodging_direction_x * velocity_difference) * v_forward);
-
-               this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference;
-       }
-
-       // the up part of the dodge is a single shot action
-       if (this.dodging_single_action == 1)
-       {
-               UNSET_ONGROUND(this);
-
-               this.velocity += PHYS_DODGING_UP_SPEED * v_up;
-
-#ifdef SVQC
-               if (autocvar_sv_dodging_sound)
-                       PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-
-               animdecide_setaction(this, ANIMACTION_JUMP, true);
-#endif
-
-               this.dodging_single_action = 0;
-       }
-
-       // are we done with the dodging ramp yet?
-       if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
-       {
-               // reset state so next dodge can be done correctly
-               this.dodging_action = 0;
-               this.dodging_direction_x = 0;
-               this.dodging_direction_y = 0;
-       }
-}
-
-void PM_dodging_GetPressedKeys(entity this)
-{
-#ifdef CSQC
-       if(!PHYS_DODGING) { return; }
-
-       PM_dodging_checkpressedkeys(this);
-
-       int keys = this.pressedkeys;
-       keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
-       keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
-       keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
-       keys = BITSET(keys, KEY_LEFT,           this.movement.y < 0);
-
-       keys = BITSET(keys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(this));
-       keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
-       keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
-       keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
-       this.pressedkeys = keys;
-#endif
-}
-
-MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
-{
-    entity player = M_ARGV(0, entity);
-
-       // print("dodging_PlayerPhysics\n");
-       PM_dodging_GetPressedKeys(player);
-       PM_dodging(player);
-}
-
-#ifdef SVQC
-
-REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
-
-MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
-{
-       entity player = M_ARGV(0, entity);
-
-       PM_dodging_checkpressedkeys(player);
-}
-
-#endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/dodging/module.inc b/qcsrc/common/mutators/mutator/dodging/module.inc
deleted file mode 100644 (file)
index 60b193f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-       #include "dodging.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc
new file mode 100644 (file)
index 0000000..fb1502d
--- /dev/null
@@ -0,0 +1,293 @@
+#include "sv_dodging.qh"
+
+#define PHYS_DODGING                                           STAT(DODGING, this)
+#define PHYS_DODGING_DELAY                                     STAT(DODGING_DELAY, this)
+#define PHYS_DODGING_DISTANCE_THRESHOLD        STAT(DODGING_DISTANCE_THRESHOLD, this)
+#define PHYS_DODGING_FROZEN_NODOUBLETAP                STAT(DODGING_FROZEN_NO_DOUBLETAP, this)
+#define PHYS_DODGING_HEIGHT_THRESHOLD          STAT(DODGING_HEIGHT_THRESHOLD, this)
+#define PHYS_DODGING_HORIZ_SPEED                       STAT(DODGING_HORIZ_SPEED, this)
+#define PHYS_DODGING_HORIZ_SPEED_FROZEN        STAT(DODGING_HORIZ_SPEED_FROZEN, this)
+#define PHYS_DODGING_RAMP_TIME                                 STAT(DODGING_RAMP_TIME, this)
+#define PHYS_DODGING_UP_SPEED                          STAT(DODGING_UP_SPEED, this)
+#define PHYS_DODGING_WALL                                      STAT(DODGING_WALL, this)
+#define PHYS_DODGING_AIR                                       STAT(DODGING_AIR, this)
+#define PHYS_DODGING_PRESSED_KEYS(s)           (s).pressedkeys
+
+#ifdef CSQC
+       #define PHYS_DODGING_FRAMETIME                          (1 / (frametime <= 0 ? 60 : frametime))
+       #define PHYS_DODGING_TIMEOUT(s)                         STAT(DODGING_TIMEOUT)
+#elif defined(SVQC)
+       #define PHYS_DODGING_FRAMETIME                          sys_frametime
+       #define PHYS_DODGING_TIMEOUT(s)                         s.cvar_cl_dodging_timeout
+#endif
+
+#ifdef SVQC
+
+bool autocvar_sv_dodging_sound;
+
+// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
+.float dodging_action;
+
+// the jump part of the dodge cannot be ramped
+.float dodging_single_action;
+
+#include <common/animdecide.qh>
+#include <common/physics/player.qh>
+
+.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT);
+
+REGISTER_MUTATOR(dodging, cvar("g_dodging"))
+{
+       // this just turns on the cvar.
+       MUTATOR_ONADD
+       {
+               g_dodging = cvar("g_dodging");
+       }
+
+       // this just turns off the cvar.
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               g_dodging = 0;
+       }
+
+       return false;
+}
+
+#elif defined(CSQC)
+REGISTER_MUTATOR(dodging, true);
+#endif
+
+// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
+.float dodging_action;
+
+// the jump part of the dodge cannot be ramped
+.float dodging_single_action;
+
+
+// these are used to store the last key press time for each of the keys..
+.float last_FORWARD_KEY_time;
+.float last_BACKWARD_KEY_time;
+.float last_LEFT_KEY_time;
+.float last_RIGHT_KEY_time;
+
+// these store the movement direction at the time of the dodge action happening.
+.vector dodging_direction;
+
+// this indicates the last time a dodge was executed. used to check if another one is allowed
+// and to ramp up the dodge acceleration in the physics hook.
+.float last_dodging_time;
+
+// This is the velocity gain to be added over the ramp time.
+// It will decrease from frame to frame during dodging_action = 1
+// until it's 0.
+.float dodging_velocity_gain;
+
+#ifdef CSQC
+.int pressedkeys;
+#endif
+
+// returns 1 if the player is close to a wall
+bool check_close_to_wall(entity this, float threshold)
+{
+       if (PHYS_DODGING_WALL == 0) { return false; }
+
+#define X(OFFSET) \
+       tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \
+       if(trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) && vdist(this.origin - trace_endpos, <, threshold)) \
+               return true;
+       X(1000*v_right);
+       X(-1000*v_right);
+       X(1000*v_forward);
+       X(-1000*v_forward);
+#undef X
+
+       return false;
+}
+
+bool check_close_to_ground(entity this, float threshold)
+{
+       return IS_ONGROUND(this) ? true : false;
+}
+
+bool PM_dodging_checkpressedkeys(entity this)
+{
+       if(!PHYS_DODGING)
+               return false;
+
+       bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
+       bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+
+       // first check if the last dodge is far enough back in time so we can dodge again
+       if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY)
+               return false;
+
+       makevectors(this.angles);
+
+       if(!PHYS_DODGING_AIR)
+       if (check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) != 1
+               && check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
+               return true;
+
+       float tap_direction_x = 0;
+       float tap_direction_y = 0;
+       bool dodge_detected = false;
+
+       #define X(COND,BTN,RESULT)                                                                                                                      \
+       if (this.movement_##COND)                                                                                               \
+               /* is this a state change? */                                                                                                   \
+               if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) {             \
+                               tap_direction_##RESULT;                                                                                                 \
+                               if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap)    \
+                                       dodge_detected = true;                                                                                          \
+                               if(PHYS_INPUT_BUTTON_DODGE(this))                                                                               \
+                                       dodge_detected = true;                                                                                          \
+                               this.last_##BTN##_KEY_time = time;                                                                              \
+               }
+       X(x < 0, BACKWARD,      x--);
+       X(x > 0, FORWARD,       x++);
+       X(y < 0, LEFT,          y--);
+       X(y > 0, RIGHT,         y++);
+       #undef X
+
+       if (dodge_detected)
+       {
+               this.last_dodging_time = time;
+
+               this.dodging_action = 1;
+               this.dodging_single_action = 1;
+
+               this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+
+               this.dodging_direction_x = tap_direction_x;
+               this.dodging_direction_y = tap_direction_y;
+
+               // normalize the dodging_direction vector.. (unlike UT99) XD
+               float length = this.dodging_direction_x * this.dodging_direction_x
+                                       + this.dodging_direction_y * this.dodging_direction_y;
+               length = sqrt(length);
+
+               this.dodging_direction_x = this.dodging_direction_x * 1.0 / length;
+               this.dodging_direction_y = this.dodging_direction_y * 1.0 / length;
+               return true;
+       }
+       return false;
+}
+
+void PM_dodging(entity this)
+{
+       if (!PHYS_DODGING)
+               return;
+
+    if (IS_DEAD(this))
+        return;
+
+       // when swimming, no dodging allowed..
+       if (this.waterlevel >= WATERLEVEL_SWIMMING)
+       {
+               this.dodging_action = 0;
+               this.dodging_direction_x = 0;
+               this.dodging_direction_y = 0;
+               return;
+       }
+
+       // make sure v_up, v_right and v_forward are sane
+       if(PHYS_DODGING_AIR)
+               makevectors(this.v_angle);
+       else
+               makevectors(this.angles);
+
+       // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
+       // will be called ramp_time/frametime times = 2 times. so, we need to
+       // add 0.5 * the total speed each frame until the dodge action is done..
+       float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
+
+       // if ramp time is smaller than frametime we get problems ;D
+       common_factor = min(common_factor, 1);
+
+       float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
+       float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed);
+       new_velocity_gain = max(0, new_velocity_gain);
+
+       float velocity_difference = this.dodging_velocity_gain - new_velocity_gain;
+
+       // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
+       if (this.dodging_action == 1)
+       {
+               //disable jump key during dodge accel phase
+               if(this.movement_z > 0) { this.movement_z = 0; }
+
+               this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right)
+                                       + ((this.dodging_direction_x * velocity_difference) * v_forward);
+
+               this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference;
+       }
+
+       // the up part of the dodge is a single shot action
+       if (this.dodging_single_action == 1)
+       {
+               UNSET_ONGROUND(this);
+
+               this.velocity += PHYS_DODGING_UP_SPEED * v_up;
+
+#ifdef SVQC
+               if (autocvar_sv_dodging_sound)
+                       PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
+
+               animdecide_setaction(this, ANIMACTION_JUMP, true);
+#endif
+
+               this.dodging_single_action = 0;
+       }
+
+       // are we done with the dodging ramp yet?
+       if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
+       {
+               // reset state so next dodge can be done correctly
+               this.dodging_action = 0;
+               this.dodging_direction_x = 0;
+               this.dodging_direction_y = 0;
+       }
+}
+
+void PM_dodging_GetPressedKeys(entity this)
+{
+#ifdef CSQC
+       if(!PHYS_DODGING) { return; }
+
+       PM_dodging_checkpressedkeys(this);
+
+       int keys = this.pressedkeys;
+       keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
+       keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
+       keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
+       keys = BITSET(keys, KEY_LEFT,           this.movement.y < 0);
+
+       keys = BITSET(keys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(this));
+       keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
+       keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
+       keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
+       this.pressedkeys = keys;
+#endif
+}
+
+MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
+{
+    entity player = M_ARGV(0, entity);
+
+       // print("dodging_PlayerPhysics\n");
+       PM_dodging_GetPressedKeys(player);
+       PM_dodging(player);
+}
+
+#ifdef SVQC
+
+REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
+
+MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
+{
+       entity player = M_ARGV(0, entity);
+
+       PM_dodging_checkpressedkeys(player);
+}
+
+#endif
diff --git a/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d490ecaaff364d71ad3bc5511c5fb734a2f1cce2..cc60ccc1fd8a9bccf1a9006ca0b46a86796d35f8 100644 (file)
@@ -1,4 +1,6 @@
-#ifdef IMPLEMENTATION
+#include "doublejump.qh"
+
+#ifdef GAMEQC
 #ifdef SVQC
        #include <server/antilag.qh>
 #endif
@@ -31,5 +33,4 @@ MUTATOR_HOOKFUNCTION(doublejump, PlayerJump)
                }
        }
 }
-
 #endif
diff --git a/qcsrc/common/mutators/mutator/doublejump/doublejump.qh b/qcsrc/common/mutators/mutator/doublejump/doublejump.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/doublejump/module.inc b/qcsrc/common/mutators/mutator/doublejump/module.inc
deleted file mode 100644 (file)
index f4c695b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifndef MENUQC
-#include "doublejump.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/globalforces/_mod.inc b/qcsrc/common/mutators/mutator/globalforces/_mod.inc
new file mode 100644 (file)
index 0000000..2302d85
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/globalforces/sv_globalforces.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/globalforces/_mod.qh b/qcsrc/common/mutators/mutator/globalforces/_mod.qh
new file mode 100644 (file)
index 0000000..c31ee5c
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/globalforces/sv_globalforces.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc
new file mode 100644 (file)
index 0000000..3e9cd0d
--- /dev/null
@@ -0,0 +1,31 @@
+#include "sv_globalforces.qh"
+
+AUTOCVAR(g_globalforces, float, false, "Global forces: knockback affects everyone");
+AUTOCVAR(g_globalforces_noself, bool, true, "Global forces: ignore self damage");
+AUTOCVAR(g_globalforces_self, float, 1, "Global forces: knockback self scale");
+AUTOCVAR(g_globalforces_range, float, 1000, "Global forces: max range of effect");
+REGISTER_MUTATOR(mutator_globalforces, autocvar_g_globalforces);
+
+MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsString) {
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), ":GlobalForces");
+}
+
+MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsPrettyString) {
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Global forces");
+}
+
+MUTATOR_HOOKFUNCTION(mutator_globalforces, PlayerDamage_SplitHealthArmor) {
+    entity frag_attacker = M_ARGV(1, entity);
+    entity frag_target = M_ARGV(2, entity);
+    if (autocvar_g_globalforces_noself && frag_target == frag_attacker) return;
+    vector damage_force = M_ARGV(3, vector) * autocvar_g_globalforces;
+    FOREACH_CLIENT(IS_PLAYER(it) && it != frag_target, {
+        if (autocvar_g_globalforces_range) {
+            if (vdist(it.origin - frag_target.origin, >, autocvar_g_globalforces_range)) {
+                continue;
+            }
+        }
+        float f = (it == frag_attacker) ? autocvar_g_globalforces_self : 1;
+        it.velocity += damage_explosion_calcpush(f * it.damageforcescale * damage_force, it.velocity, autocvar_g_balance_damagepush_speedfactor);
+    });
+}
diff --git a/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index ec6da662e7a1b26be37f1fe4cdd122874294448d..e5e68b6106fed48992f8d3d743eb9f087ac168c6 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/hook/hook.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/hook/sv_hook.qc>
+#endif
index 50c0c13726f6768634a7534eaeee654cf5f3bd8b..5a5d26e8172fd5ee077fc200e18a519b64acca0c 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/hook/hook.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/hook/sv_hook.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/hook/hook.qc b/qcsrc/common/mutators/mutator/hook/hook.qc
deleted file mode 100644 (file)
index e72134e..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifdef IMPLEMENTATION
-AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up"));
-#ifdef SVQC
-REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) {
-    MUTATOR_ONADD {
-        g_grappling_hook = true;
-        WEP_HOOK.ammo_factor = 0;
-    }
-    MUTATOR_ONROLLBACK_OR_REMOVE {
-        g_grappling_hook = false;
-        WEP_HOOK.ammo_factor = 1;
-    }
-
-    return false;
-}
-
-MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString)
-{
-    M_ARGV(0, string) = strcat(M_ARGV(0, string), ":grappling_hook");
-}
-
-MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString)
-{
-    M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Hook");
-}
-
-MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString)
-{
-    M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
-}
-
-MUTATOR_HOOKFUNCTION(hook, PlayerSpawn)
-{
-    entity player = M_ARGV(0, entity);
-
-    player.offhand = OFFHAND_HOOK;
-}
-
-MUTATOR_HOOKFUNCTION(hook, FilterItem)
-{
-    entity item = M_ARGV(0, entity);
-
-    return item.weapon == WEP_HOOK.m_id;
-}
-
-#endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/hook/module.inc b/qcsrc/common/mutators/mutator/hook/module.inc
deleted file mode 100644 (file)
index 61600c4..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "hook.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qc b/qcsrc/common/mutators/mutator/hook/sv_hook.qc
new file mode 100644 (file)
index 0000000..b5a196c
--- /dev/null
@@ -0,0 +1,47 @@
+#include "sv_hook.qh"
+
+AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up"));
+#ifdef SVQC
+REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) {
+    MUTATOR_ONADD {
+        g_grappling_hook = true;
+        WEP_HOOK.ammo_factor = 0;
+    }
+    MUTATOR_ONROLLBACK_OR_REMOVE {
+        g_grappling_hook = false;
+        WEP_HOOK.ammo_factor = 1;
+    }
+
+    return false;
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString)
+{
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), ":grappling_hook");
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString)
+{
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Hook");
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString)
+{
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
+}
+
+MUTATOR_HOOKFUNCTION(hook, PlayerSpawn)
+{
+    entity player = M_ARGV(0, entity);
+
+    player.offhand = OFFHAND_HOOK;
+}
+
+MUTATOR_HOOKFUNCTION(hook, FilterItem)
+{
+    entity item = M_ARGV(0, entity);
+
+    return item.weapon == WEP_HOOK.m_id;
+}
+
+#endif
diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qh b/qcsrc/common/mutators/mutator/hook/sv_hook.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index dad005fe494a1002959c2ab5eafd094b4bd2ee6f..2195111f0f269e2903f767e3badf17644e0aa27d 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
-#include <common/mutators/mutator/instagib/instagib.qc>
 #include <common/mutators/mutator/instagib/items.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/instagib/sv_instagib.qc>
+#endif
index 2e88f427e9a143ec41187dd4a69f46cd8744cd0e..7097eaf390dac022c2840f7db4d9dc6ee7028368 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
-#include <common/mutators/mutator/instagib/instagib.qh>
 #include <common/mutators/mutator/instagib/items.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/instagib/sv_instagib.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/instagib/instagib.qc b/qcsrc/common/mutators/mutator/instagib/instagib.qc
deleted file mode 100644 (file)
index 34f3825..0000000
+++ /dev/null
@@ -1,505 +0,0 @@
-#ifndef MUTATOR_INSTAGIB_H
-#define MUTATOR_INSTAGIB_H
-
-#include "items.qc"
-
-#ifdef SVQC
-float autocvar_g_instagib_invis_alpha;
-#endif
-
-#endif
-
-#ifdef IMPLEMENTATION
-#ifdef SVQC
-
-int autocvar_g_instagib_ammo_drop;
-int autocvar_g_instagib_extralives;
-float autocvar_g_instagib_speed_highspeed;
-
-#include <server/cl_client.qh>
-
-#include <common/items/all.qc>
-
-REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball);
-
-spawnfunc(item_minst_cells)
-{
-       if (!g_instagib) { remove(this); return; }
-       if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop;
-       StartItem(this, ITEM_VaporizerCells);
-}
-
-void instagib_invisibility(entity this)
-{
-       this.strength_finished = autocvar_g_balance_powerup_strength_time;
-       StartItem(this, ITEM_Invisibility);
-}
-
-void instagib_extralife(entity this)
-{
-       StartItem(this, ITEM_ExtraLife);
-}
-
-void instagib_speed(entity this)
-{
-       this.invincible_finished = autocvar_g_balance_powerup_invincible_time;
-       StartItem(this, ITEM_Speed);
-}
-
-.float instagib_nextthink;
-.float instagib_needammo;
-void instagib_stop_countdown(entity e)
-{
-       if (!e.instagib_needammo)
-               return;
-       Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO);
-       e.instagib_needammo = false;
-}
-void instagib_ammocheck(entity this)
-{
-       if(time < this.instagib_nextthink)
-               return;
-       if(!IS_PLAYER(this))
-               return; // not a player
-
-       if(IS_DEAD(this) || gameover)
-               instagib_stop_countdown(this);
-       else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE))
-               instagib_stop_countdown(this);
-       else if(autocvar_g_rm && autocvar_g_rm_laser)
-       {
-               if(!this.instagib_needammo)
-               {
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE);
-                       this.instagib_needammo = true;
-               }
-       }
-       else
-       {
-               this.instagib_needammo = true;
-               if (this.health <= 5)
-               {
-                       Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED);
-               }
-               else if (this.health <= 10)
-               {
-                       Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1);
-               }
-               else if (this.health <= 20)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2);
-               }
-               else if (this.health <= 30)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3);
-               }
-               else if (this.health <= 40)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4);
-               }
-               else if (this.health <= 50)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5);
-               }
-               else if (this.health <= 60)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6);
-               }
-               else if (this.health <= 70)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7);
-               }
-               else if (this.health <= 80)
-               {
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8);
-               }
-               else if (this.health <= 90)
-               {
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9);
-               }
-               else
-               {
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO);
-                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
-               }
-       }
-       this.instagib_nextthink = time + 1;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd)
-{
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it)));
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem)
-{
-       entity item = M_ARGV(1, entity);
-
-       item.monster_loot = spawnfunc_item_minst_cells;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn)
-{
-       entity mon = M_ARGV(0, entity);
-
-       // always refill ammo
-       if(mon.monsterid == MON_MAGE.monsterid)
-               mon.skin = 1;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack)
-{
-       entity targ = M_ARGV(1, entity);
-
-       if (targ.items & ITEM_Invisibility.m_itemid)
-               return true;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver)
-{
-       entity player = M_ARGV(0, entity);
-
-       instagib_stop_countdown(player);
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.effects |= EF_FULLBRIGHT;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       instagib_ammocheck(player);
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen)
-{
-       // no regeneration in instagib
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups)
-{
-       entity player = M_ARGV(0, entity);
-
-       if (!(player.effects & EF_FULLBRIGHT))
-               player.effects |= EF_FULLBRIGHT;
-
-       if (player.items & ITEM_Invisibility.m_itemid)
-       {
-               play_countdown(player, player.strength_finished, SND_POWEROFF);
-               if (time > player.strength_finished)
-               {
-                       player.alpha = default_player_alpha;
-                       player.exteriorweaponentity.alpha = default_weapon_alpha;
-                       player.items &= ~ITEM_Invisibility.m_itemid;
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
-               }
-       }
-       else
-       {
-               if (time < player.strength_finished)
-               {
-                       player.alpha = autocvar_g_instagib_invis_alpha;
-                       player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha;
-                       player.items |= ITEM_Invisibility.m_itemid;
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname);
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
-               }
-       }
-
-       if (player.items & ITEM_Speed.m_itemid)
-       {
-               play_countdown(player, player.invincible_finished, SND_POWEROFF);
-               if (time > player.invincible_finished)
-               {
-                       player.items &= ~ITEM_Speed.m_itemid;
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED);
-               }
-       }
-       else
-       {
-               if (time < player.invincible_finished)
-               {
-                       player.items |= ITEM_Speed.m_itemid;
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname);
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED);
-               }
-       }
-}
-
-.float stat_sv_maxspeed;
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(player.items & ITEM_Speed.m_itemid)
-               player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor)
-{
-       M_ARGV(4, float) = M_ARGV(7, float); // take = damage
-       M_ARGV(5, float) = 0; // save
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon)
-{
-       // weapon dropping on death handled by FilterItem
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-       float frag_deathtype = M_ARGV(3, float);
-       float frag_damage = M_ARGV(4, float);
-       float frag_mirrordamage = M_ARGV(5, float);
-       vector frag_force = M_ARGV(6, vector);
-
-       if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
-               frag_damage = 0;
-
-       if(IS_PLAYER(frag_target))
-       {
-               if(frag_deathtype == DEATH_FALL.m_id)
-                       frag_damage = 0; // never count fall damage
-
-               if(!autocvar_g_instagib_damagedbycontents)
-               switch(DEATH_ENT(frag_deathtype))
-               {
-                       case DEATH_DROWN:
-                       case DEATH_SLIME:
-                       case DEATH_LAVA:
-                               frag_damage = 0;
-                               break;
-               }
-
-               if(IS_PLAYER(frag_attacker))
-               if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
-               {
-                       if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker))
-                               frag_force = '0 0 0';
-
-                       if(frag_target.armorvalue)
-                       {
-                               frag_target.armorvalue -= 1;
-                               frag_damage = 0;
-                               frag_target.damage_dealt += 1;
-                               frag_attacker.damage_dealt += 1;
-                               Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
-                       }
-               }
-
-               if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
-               {
-                       if(frag_deathtype & HITTYPE_SECONDARY)
-                       {
-                               if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target)
-                               {
-                                       frag_damage = 0;
-                                       if(!autocvar_g_instagib_mirrordamage)
-                                               frag_mirrordamage = 0; // never do mirror damage on enemies
-                               }
-
-                               if(frag_target != frag_attacker)
-                               {
-                                       if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); }
-                                       if(!autocvar_g_instagib_blaster_keepforce)
-                                               frag_force = '0 0 0';
-                               }
-                       }
-               }
-       }
-
-       if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring
-       if(IS_PLAYER(frag_attacker))
-       if(frag_mirrordamage > 0)
-       {
-               // just lose extra LIVES, don't kill the player for mirror damage
-               if(frag_attacker.armorvalue > 0)
-               {
-                       frag_attacker.armorvalue -= 1;
-                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue);
-                       frag_attacker.damage_dealt += frag_mirrordamage;
-               }
-               frag_mirrordamage = 0;
-       }
-
-       if(frag_target.alpha && frag_target.alpha < 1)
-       if(IS_PLAYER(frag_target))
-               yoda = 1;
-
-       M_ARGV(4, float) = frag_damage;
-       M_ARGV(5, float) = frag_mirrordamage;
-       M_ARGV(6, vector) = frag_force;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems)
-{
-       start_health       = warmup_start_health       = 100;
-       start_armorvalue   = warmup_start_armorvalue   = 0;
-
-       start_ammo_shells  = warmup_start_ammo_shells  = 0;
-       start_ammo_nails   = warmup_start_ammo_nails   = 0;
-       start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_instagib_ammo_start");
-       start_ammo_plasma  = warmup_start_ammo_plasma  = 0;
-       start_ammo_rockets = warmup_start_ammo_rockets = 0;
-       start_ammo_fuel    = warmup_start_ammo_fuel    = 0;
-
-       start_weapons = warmup_start_weapons = WEPSET(VAPORIZER);
-       start_items |= IT_UNLIMITED_SUPERWEAPONS;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
-{
-       entity item = M_ARGV(0, entity);
-
-       if(item.classname == "item_cells")
-               return true; // no normal cells?
-
-       if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon")
-       {
-               item.ammo_cells = autocvar_g_instagib_ammo_drop;
-               return false;
-       }
-
-       if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id)
-       {
-               entity e = spawn();
-               setorigin(e, item.origin);
-               e.noalign = item.noalign;
-        e.cnt = item.cnt;
-        e.team = item.team;
-        e.spawnfunc_checked = true;
-               spawnfunc_item_minst_cells(e);
-               return true;
-       }
-
-       if(item.flags & FL_POWERUP)
-               return false;
-
-       if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
-               item.ammo_cells = autocvar_g_instagib_ammo_drop;
-
-       if(item.ammo_cells && !item.weapon)
-               return false;
-
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint)
-{
-       entity wp = M_ARGV(0, entity);
-       entity player = M_ARGV(1, entity);
-
-       entity e = WaypointSprite_getviewentity(player);
-
-       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
-       // but only apply this to real players, not to spectators
-       if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player))
-       if(DIFF_TEAM(wp.owner, e))
-               return true;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies)
-{
-       float frag_deathtype = M_ARGV(3, float);
-
-       if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
-               M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch)
-{
-       entity item = M_ARGV(0, entity);
-       entity toucher = M_ARGV(1, entity);
-
-       if(item.ammo_cells)
-       {
-               // play some cool sounds ;)
-               if (IS_CLIENT(toucher))
-               {
-                       if(toucher.health <= 5)
-                               Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
-                       else if(toucher.health < 50)
-                               Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
-               }
-
-               if(toucher.health < 100)
-                       toucher.health = 100;
-
-               return MUT_ITEMTOUCH_CONTINUE;
-       }
-
-       if(item.itemdef == ITEM_ExtraLife)
-       {
-               toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives);
-               Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES);
-               return MUT_ITEMTOUCH_PICKUP;
-       }
-
-       return MUT_ITEMTOUCH_CONTINUE;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn)
-{
-       if (!autocvar_g_powerups) { return; }
-       entity ent = M_ARGV(0, entity);
-       // Can't use .itemdef here
-       if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega"))
-               return;
-
-       entity e = spawn();
-
-       float r = random();
-       if (r < 0.3)
-               setthink(e, instagib_invisibility);
-       else if (r < 0.6)
-               setthink(e, instagib_extralife);
-       else
-               setthink(e, instagib_speed);
-
-       e.nextthink = time + 0.1;
-       e.spawnflags = ent.spawnflags;
-       e.noalign = ent.noalign;
-       setorigin(e, ent.origin);
-
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib");
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib");
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname)
-{
-       M_ARGV(0, string) = "InstaGib";
-       return true;
-}
-
-#endif
-#endif
index 269ec2d245ea02cc473e1345cd02aad9b585e598..b0205a5f7b30aeb1dece55525c0943777eadbfcd 100644 (file)
@@ -1,84 +1 @@
-#pragma once
-
-#include <common/items/all.qh>
-#include <common/items/item/ammo.qh>
-#include <common/items/item/powerup.qh>
-
-float instagib_respawntime_ammo = 45;
-float instagib_respawntimejitter_ammo = 0;
-GETTER(float, instagib_respawntime_ammo)
-GETTER(float, instagib_respawntimejitter_ammo)
-
-#ifndef MENUQC
-MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3"));
-SOUND(VaporizerCells, "misc/itempickup");
-#endif
-
-REGISTER_ITEM(VaporizerCells, Ammo) {
-#ifndef MENUQC
-    this.m_model                =   MDL_VaporizerCells_ITEM;
-    this.m_sound                =   SND_VaporizerCells;
-#endif
-    this.m_name                 =   "Vaporizer Ammo";
-    this.m_icon                 =   "ammo_supercells";
-#ifdef SVQC
-    this.m_botvalue             =   100;
-    this.m_itemid               =   IT_CELLS;
-    this.m_respawntime          =   GET(instagib_respawntime_ammo);
-    this.m_respawntimejitter    =   GET(instagib_respawntimejitter_ammo);
-#endif
-}
-
-#ifndef MENUQC
-MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3"));
-SOUND(ExtraLife, "misc/megahealth");
-#endif
-
-REGISTER_ITEM(ExtraLife, Powerup) {
-#ifndef MENUQC
-    this.m_model                =   MDL_ExtraLife_ITEM;
-    this.m_sound                =   SND_ExtraLife;
-#endif
-    this.m_name                 =   "Extra life";
-    this.m_icon                 =   "item_mega_health";
-    this.m_color                =   '1 0 0';
-    this.m_waypoint             =   _("Extra life");
-    this.m_waypointblink        =   2;
-    this.m_itemid               =   IT_NAILS;
-}
-
-#ifndef MENUQC
-MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
-SOUND(Invisibility, "misc/powerup");
-#endif
-
-REGISTER_ITEM(Invisibility, Powerup) {
-#ifndef MENUQC
-    this.m_model            =   MDL_Invisibility_ITEM;
-    this.m_sound            =   SND_Invisibility;
-#endif
-    this.m_name             =   "Invisibility";
-    this.m_icon             =   "strength";
-    this.m_color            =   '0 0 1';
-    this.m_waypoint         =   _("Invisibility");
-    this.m_waypointblink    =   2;
-    this.m_itemid           =   IT_STRENGTH;
-}
-
-#ifndef MENUQC
-MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
-SOUND(Speed, "misc/powerup_shield");
-#endif
-
-REGISTER_ITEM(Speed, Powerup) {
-#ifndef MENUQC
-    this.m_model            =   MDL_Speed_ITEM;
-    this.m_sound            =   SND_Speed;
-#endif
-    this.m_name             =   "Speed";
-    this.m_icon             =   "shield";
-    this.m_color            =   '1 0 1';
-    this.m_waypoint         =   _("Speed");
-    this.m_waypointblink    =   2;
-    this.m_itemid           =   IT_INVINCIBLE;
-}
+#include "items.qh"
diff --git a/qcsrc/common/mutators/mutator/instagib/items.qh b/qcsrc/common/mutators/mutator/instagib/items.qh
new file mode 100644 (file)
index 0000000..1efa4cb
--- /dev/null
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <common/items/_mod.qh>
+#include <common/items/item/ammo.qh>
+#include <common/items/item/powerup.qh>
+
+float instagib_respawntime_ammo = 45;
+float instagib_respawntimejitter_ammo = 0;
+GETTER(float, instagib_respawntime_ammo)
+GETTER(float, instagib_respawntimejitter_ammo)
+
+#ifdef GAMEQC
+MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3"));
+SOUND(VaporizerCells, Item_Sound("itempickup"));
+#endif
+
+REGISTER_ITEM(VaporizerCells, Ammo) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_VaporizerCells_ITEM;
+    this.m_sound                =   SND_VaporizerCells;
+#endif
+    this.m_name                 =   "Vaporizer Ammo";
+    this.m_icon                 =   "ammo_supercells";
+#ifdef SVQC
+    this.m_botvalue             =   100;
+    this.m_itemid               =   IT_CELLS;
+    this.m_respawntime          =   GET(instagib_respawntime_ammo);
+    this.m_respawntimejitter    =   GET(instagib_respawntimejitter_ammo);
+#endif
+}
+
+#ifdef GAMEQC
+MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3"));
+SOUND(ExtraLife, Item_Sound("megahealth"));
+#endif
+
+REGISTER_ITEM(ExtraLife, Powerup) {
+#ifdef GAMEQC
+    this.m_model                =   MDL_ExtraLife_ITEM;
+    this.m_sound                =   SND_ExtraLife;
+#endif
+    this.m_name                 =   "Extra life";
+    this.m_icon                 =   "item_mega_health";
+    this.m_color                =   '1 0 0';
+    this.m_waypoint             =   _("Extra life");
+    this.m_waypointblink        =   2;
+    this.m_itemid               =   IT_NAILS;
+}
+
+#ifdef GAMEQC
+MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
+SOUND(Invisibility, Item_Sound("powerup"));
+#endif
+
+REGISTER_ITEM(Invisibility, Powerup) {
+#ifdef GAMEQC
+    this.m_model            =   MDL_Invisibility_ITEM;
+    this.m_sound            =   SND_Invisibility;
+#endif
+    this.m_name             =   "Invisibility";
+    this.m_icon             =   "strength";
+    this.m_color            =   '0 0 1';
+    this.m_waypoint         =   _("Invisibility");
+    this.m_waypointblink    =   2;
+    this.m_itemid           =   IT_STRENGTH;
+}
+
+#ifdef GAMEQC
+MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
+SOUND(Speed, Item_Sound("powerup_shield"));
+#endif
+
+REGISTER_ITEM(Speed, Powerup) {
+#ifdef GAMEQC
+    this.m_model            =   MDL_Speed_ITEM;
+    this.m_sound            =   SND_Speed;
+#endif
+    this.m_name             =   "Speed";
+    this.m_icon             =   "shield";
+    this.m_color            =   '1 0 1';
+    this.m_waypoint         =   _("Speed");
+    this.m_waypointblink    =   2;
+    this.m_itemid           =   IT_INVINCIBLE;
+}
diff --git a/qcsrc/common/mutators/mutator/instagib/module.inc b/qcsrc/common/mutators/mutator/instagib/module.inc
deleted file mode 100644 (file)
index 7270067..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "instagib.qc"
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
new file mode 100644 (file)
index 0000000..4c6dc15
--- /dev/null
@@ -0,0 +1,535 @@
+#include "sv_instagib.qh"
+
+int autocvar_g_instagib_ammo_drop;
+bool autocvar_g_instagib_ammo_convert_cells;
+bool autocvar_g_instagib_ammo_convert_rockets;
+bool autocvar_g_instagib_ammo_convert_shells;
+bool autocvar_g_instagib_ammo_convert_bullets;
+int autocvar_g_instagib_extralives;
+float autocvar_g_instagib_speed_highspeed;
+
+#include <server/client.qh>
+
+#include <common/items/_mod.qh>
+
+REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball);
+
+spawnfunc(item_minst_cells)
+{
+       if (!g_instagib) { delete(this); return; }
+       if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop;
+       StartItem(this, ITEM_VaporizerCells);
+}
+
+void instagib_invisibility(entity this)
+{
+       this.strength_finished = autocvar_g_balance_powerup_strength_time;
+       StartItem(this, ITEM_Invisibility);
+}
+
+void instagib_extralife(entity this)
+{
+       StartItem(this, ITEM_ExtraLife);
+}
+
+void instagib_speed(entity this)
+{
+       this.invincible_finished = autocvar_g_balance_powerup_invincible_time;
+       StartItem(this, ITEM_Speed);
+}
+
+.float instagib_nextthink;
+.float instagib_needammo;
+void instagib_stop_countdown(entity e)
+{
+       if (!e.instagib_needammo)
+               return;
+       Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO);
+       e.instagib_needammo = false;
+}
+void instagib_ammocheck(entity this)
+{
+       if(time < this.instagib_nextthink)
+               return;
+       if(!IS_PLAYER(this))
+               return; // not a player
+
+       if(IS_DEAD(this) || gameover)
+               instagib_stop_countdown(this);
+       else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE))
+               instagib_stop_countdown(this);
+       else if(autocvar_g_rm && autocvar_g_rm_laser)
+       {
+               if(!this.instagib_needammo)
+               {
+                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE);
+                       this.instagib_needammo = true;
+               }
+       }
+       else
+       {
+               this.instagib_needammo = true;
+               if (this.health <= 5)
+               {
+                       Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED);
+               }
+               else if (this.health <= 10)
+               {
+                       Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1);
+               }
+               else if (this.health <= 20)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2);
+               }
+               else if (this.health <= 30)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3);
+               }
+               else if (this.health <= 40)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4);
+               }
+               else if (this.health <= 50)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5);
+               }
+               else if (this.health <= 60)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6);
+               }
+               else if (this.health <= 70)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7);
+               }
+               else if (this.health <= 80)
+               {
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8);
+               }
+               else if (this.health <= 90)
+               {
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9);
+               }
+               else
+               {
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO);
+                       Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
+               }
+       }
+       this.instagib_nextthink = time + 1;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd)
+{
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it)));
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem)
+{
+       entity item = M_ARGV(1, entity);
+
+       item.monster_loot = spawnfunc_item_minst_cells;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn)
+{
+       entity mon = M_ARGV(0, entity);
+
+       // always refill ammo
+       if(mon.monsterid == MON_MAGE.monsterid)
+               mon.skin = 1;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack)
+{
+       entity targ = M_ARGV(1, entity);
+
+       if (targ.items & ITEM_Invisibility.m_itemid)
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver)
+{
+       entity player = M_ARGV(0, entity);
+
+       instagib_stop_countdown(player);
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.effects |= EF_FULLBRIGHT;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       instagib_ammocheck(player);
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen)
+{
+       // no regeneration in instagib
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups)
+{
+       entity player = M_ARGV(0, entity);
+
+       if (!(player.effects & EF_FULLBRIGHT))
+               player.effects |= EF_FULLBRIGHT;
+
+       if (player.items & ITEM_Invisibility.m_itemid)
+       {
+               play_countdown(player, player.strength_finished, SND_POWEROFF);
+               if (time > player.strength_finished)
+               {
+                       player.alpha = default_player_alpha;
+                       player.exteriorweaponentity.alpha = default_weapon_alpha;
+                       player.items &= ~ITEM_Invisibility.m_itemid;
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
+               }
+       }
+       else
+       {
+               if (time < player.strength_finished)
+               {
+                       player.alpha = autocvar_g_instagib_invis_alpha;
+                       player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha;
+                       player.items |= ITEM_Invisibility.m_itemid;
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname);
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
+               }
+       }
+
+       if (player.items & ITEM_Speed.m_itemid)
+       {
+               play_countdown(player, player.invincible_finished, SND_POWEROFF);
+               if (time > player.invincible_finished)
+               {
+                       player.items &= ~ITEM_Speed.m_itemid;
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED);
+               }
+       }
+       else
+       {
+               if (time < player.invincible_finished)
+               {
+                       player.items |= ITEM_Speed.m_itemid;
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname);
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED);
+               }
+       }
+}
+
+.float stat_sv_maxspeed;
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.items & ITEM_Speed.m_itemid)
+               player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor)
+{
+       M_ARGV(4, float) = M_ARGV(7, float); // take = damage
+       M_ARGV(5, float) = 0; // save
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon)
+{
+       // weapon dropping on death handled by FilterItem
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(3, float);
+       float frag_damage = M_ARGV(4, float);
+       float frag_mirrordamage = M_ARGV(5, float);
+       vector frag_force = M_ARGV(6, vector);
+
+       if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
+               frag_damage = 0;
+
+       if(IS_PLAYER(frag_target))
+       {
+               if(frag_deathtype == DEATH_FALL.m_id)
+                       frag_damage = 0; // never count fall damage
+
+               if(!autocvar_g_instagib_damagedbycontents)
+               switch(DEATH_ENT(frag_deathtype))
+               {
+                       case DEATH_DROWN:
+                       case DEATH_SLIME:
+                       case DEATH_LAVA:
+                               frag_damage = 0;
+                               break;
+               }
+
+               if(IS_PLAYER(frag_attacker))
+               if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
+               {
+                       if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker))
+                               frag_force = '0 0 0';
+
+                       if(frag_target.armorvalue)
+                       {
+                               frag_target.armorvalue -= 1;
+                               frag_damage = 0;
+                               frag_target.damage_dealt += 1;
+                               frag_attacker.damage_dealt += 1;
+                               Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
+                       }
+               }
+
+               if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
+               {
+                       if(frag_deathtype & HITTYPE_SECONDARY)
+                       {
+                               if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target)
+                               {
+                                       frag_damage = 0;
+                                       if(!autocvar_g_instagib_mirrordamage)
+                                               frag_mirrordamage = 0; // never do mirror damage on enemies
+                               }
+
+                               if(frag_target != frag_attacker)
+                               {
+                                       if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); }
+                                       if(!autocvar_g_instagib_blaster_keepforce)
+                                               frag_force = '0 0 0';
+                               }
+                       }
+               }
+       }
+
+       if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring
+       if(IS_PLAYER(frag_attacker))
+       if(frag_mirrordamage > 0)
+       {
+               // just lose extra LIVES, don't kill the player for mirror damage
+               if(frag_attacker.armorvalue > 0)
+               {
+                       frag_attacker.armorvalue -= 1;
+                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue);
+                       frag_attacker.damage_dealt += frag_mirrordamage;
+               }
+               frag_mirrordamage = 0;
+       }
+
+       if(frag_target.alpha && frag_target.alpha < 1)
+       if(IS_PLAYER(frag_target))
+               yoda = 1;
+
+       M_ARGV(4, float) = frag_damage;
+       M_ARGV(5, float) = frag_mirrordamage;
+       M_ARGV(6, vector) = frag_force;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems)
+{
+       start_health       = warmup_start_health       = 100;
+       start_armorvalue   = warmup_start_armorvalue   = 0;
+
+       start_ammo_shells  = warmup_start_ammo_shells  = 0;
+       start_ammo_nails   = warmup_start_ammo_nails   = 0;
+       start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_instagib_ammo_start");
+       start_ammo_plasma  = warmup_start_ammo_plasma  = 0;
+       start_ammo_rockets = warmup_start_ammo_rockets = 0;
+       start_ammo_fuel    = warmup_start_ammo_fuel    = 0;
+
+       start_weapons = warmup_start_weapons = WEPSET(VAPORIZER);
+       start_items |= IT_UNLIMITED_SUPERWEAPONS;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena)
+{
+       // turn weapon arena off
+       M_ARGV(0, string) = "off";
+}
+
+void replace_with_insta_cells(entity item)
+{
+       entity e = spawn();
+       setorigin(e, item.origin);
+       e.noalign = item.noalign;
+       e.cnt = item.cnt;
+       e.team = item.team;
+       e.spawnfunc_checked = true;
+       spawnfunc_item_minst_cells(e);
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
+{
+       entity item = M_ARGV(0, entity);
+
+       if(item.classname == "item_cells")
+       {
+               if(autocvar_g_instagib_ammo_convert_cells)
+               {
+                       replace_with_insta_cells(item);
+               }
+               return true;
+       }
+       else if(item.classname == "item_rockets")
+       {
+               if(autocvar_g_instagib_ammo_convert_rockets)
+               {
+                       replace_with_insta_cells(item);
+               }
+               return true;
+       }
+       else if(item.classname == "item_shells")
+       {
+               if(autocvar_g_instagib_ammo_convert_shells)
+               {
+                       replace_with_insta_cells(item);
+               }
+               return true;
+       }
+       else if(item.classname == "item_bullets")
+       {
+               if(autocvar_g_instagib_ammo_convert_bullets)
+               {
+                       replace_with_insta_cells(item);
+               }
+               return true;
+       }
+
+       if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon")
+       {
+               item.ammo_cells = autocvar_g_instagib_ammo_drop;
+               return false;
+       }
+
+       if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id)
+       {
+               replace_with_insta_cells(item);
+               return true;
+       }
+
+       if(item.flags & FL_POWERUP)
+               return false;
+
+       if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
+               item.ammo_cells = autocvar_g_instagib_ammo_drop;
+
+       if(item.ammo_cells && !item.weapon)
+               return false;
+
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint)
+{
+       entity wp = M_ARGV(0, entity);
+       entity player = M_ARGV(1, entity);
+
+       entity e = WaypointSprite_getviewentity(player);
+
+       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
+       // but only apply this to real players, not to spectators
+       if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player))
+       if(DIFF_TEAM(wp.owner, e))
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies)
+{
+       float frag_deathtype = M_ARGV(3, float);
+
+       if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
+               M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch)
+{
+       entity item = M_ARGV(0, entity);
+       entity toucher = M_ARGV(1, entity);
+
+       if(item.ammo_cells)
+       {
+               // play some cool sounds ;)
+               if (IS_CLIENT(toucher))
+               {
+                       if(toucher.health <= 5)
+                               Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
+                       else if(toucher.health < 50)
+                               Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
+               }
+
+               if(toucher.health < 100)
+                       toucher.health = 100;
+
+               return MUT_ITEMTOUCH_CONTINUE;
+       }
+
+       if(item.itemdef == ITEM_ExtraLife)
+       {
+               toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives);
+               Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES);
+               return MUT_ITEMTOUCH_PICKUP;
+       }
+
+       return MUT_ITEMTOUCH_CONTINUE;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn)
+{
+       if (!autocvar_g_powerups) { return; }
+       entity ent = M_ARGV(0, entity);
+       // Can't use .itemdef here
+       if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega"))
+               return;
+
+       entity e = spawn();
+
+       float r = random();
+       if (r < 0.3)
+               setthink(e, instagib_invisibility);
+       else if (r < 0.6)
+               setthink(e, instagib_extralife);
+       else
+               setthink(e, instagib_speed);
+
+       e.nextthink = time + 0.1;
+       e.spawnflags = ent.spawnflags;
+       e.noalign = ent.noalign;
+       setorigin(e, ent.origin);
+
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib");
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib");
+}
+
+MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname)
+{
+       M_ARGV(0, string) = "InstaGib";
+       return true;
+}
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh
new file mode 100644 (file)
index 0000000..4c6d20b
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "items.qh"
+
+float autocvar_g_instagib_invis_alpha;
index 68d313e95ce5608e6cc352cda79f0503f5b9b7c2..eb8c95fcb81df64309502206570ad41f6cadf47b 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/invincibleproj/invincibleproj.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/invincibleproj/sv_invincibleproj.qc>
+#endif
index dc3f32f10e8511e0cde0a654d6336ade2e6c7271..2d59a0891aa8eaf222de699744c5a0a8ccac50b5 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/invincibleproj/invincibleproj.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/invincibleproj/sv_invincibleproj.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc
deleted file mode 100644 (file)
index 5bdafa1..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles"));
-
-MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile)
-{
-       entity proj = M_ARGV(1, entity);
-
-       if(proj.health)
-       {
-               // disable health which in effect disables damage calculations
-               proj.health = 0;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":InvincibleProjectiles");
-}
-
-MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Invincible Projectiles");
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/invincibleproj/module.inc b/qcsrc/common/mutators/mutator/invincibleproj/module.inc
deleted file mode 100644 (file)
index 61d0383..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "invincibleproj.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc
new file mode 100644 (file)
index 0000000..23e0d0d
--- /dev/null
@@ -0,0 +1,24 @@
+#include "sv_invincibleproj.qh"
+
+REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles"));
+
+MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile)
+{
+       entity proj = M_ARGV(1, entity);
+
+       if(proj.health)
+       {
+               // disable health which in effect disables damage calculations
+               proj.health = 0;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":InvincibleProjectiles");
+}
+
+MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Invincible Projectiles");
+}
diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime.qc
deleted file mode 100644 (file)
index 1c4de9a..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(itemstime, true);
-
-REGISTER_NET_TEMP(itemstime)
-
-#ifdef SVQC
-void IT_Write(entity e, int i, float f) {
-    if (!IS_REAL_CLIENT(e)) return;
-    msg_entity = e;
-    WriteHeader(MSG_ONE, itemstime);
-    WriteByte(MSG_ONE, i);
-    WriteFloat(MSG_ONE, f);
-}
-#endif
-
-#ifdef CSQC
-// reserve one more spot for superweapons time
-float ItemsTime_time[Items_MAX + 1];
-float ItemsTime_availableTime[Items_MAX + 1];
-NET_HANDLE(itemstime, bool isNew)
-{
-    int i = ReadByte();
-    float f = ReadFloat();
-    return = true;
-    ItemsTime_time[i] = f;
-}
-#endif
-
-#ifdef CSQC
-void Item_ItemsTime_Init()
-{
-    FOREACH(Items, true, LAMBDA(
-        ItemsTime_time[it.m_id] = -1;
-    ));
-    ItemsTime_time[Items_MAX] = -1;
-}
-
-STATIC_INIT(ItemsTime_Init) {
-    Item_ItemsTime_Init();
-}
-
-int autocvar_hud_panel_itemstime = 2;
-float autocvar_hud_panel_itemstime_dynamicsize = 1;
-float autocvar_hud_panel_itemstime_ratio = 2;
-int autocvar_hud_panel_itemstime_iconalign;
-bool autocvar_hud_panel_itemstime_progressbar = 0;
-float autocvar_hud_panel_itemstime_progressbar_maxtime = 30;
-string autocvar_hud_panel_itemstime_progressbar_name = "progressbar";
-float autocvar_hud_panel_itemstime_progressbar_reduced;
-bool autocvar_hud_panel_itemstime_hidespawned = 1;
-bool autocvar_hud_panel_itemstime_hidelarge = false;
-int autocvar_hud_panel_itemstime_text = 1;
-#define hud_panel_itemstime_hidelarge autocvar_hud_panel_itemstime_hidelarge
-#else
-#define hud_panel_itemstime_hidelarge false
-#endif
-
-bool Item_ItemsTime_SpectatorOnly(GameItem it)
-{
-    return (false
-    || it == ITEM_ArmorMega     || (it == ITEM_ArmorLarge && !hud_panel_itemstime_hidelarge)
-    || it == ITEM_HealthMega    || (it == ITEM_HealthLarge && !hud_panel_itemstime_hidelarge)
-    );
-}
-
-bool Item_ItemsTime_Allow(GameItem it)
-{
-    return (false
-    || it.instanceOfPowerup
-    || Item_ItemsTime_SpectatorOnly(it)
-    );
-}
-
-#ifdef SVQC
-
-// reserve one more spot for superweapons time
-float it_times[Items_MAX + 1];
-
-void Item_ItemsTime_Init()
-{
-    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-        it_times[it.m_id] = -1;
-    ));
-    it_times[Items_MAX] = -1;
-}
-
-STATIC_INIT(ItemsTime_Init) {
-    // items time
-    Item_ItemsTime_Init();
-}
-
-void Item_ItemsTime_ResetTimes()
-{
-    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-        it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0;
-    ));
-    it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0;
-}
-
-void Item_ItemsTime_ResetTimesForPlayer(entity e)
-{
-    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-        IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0);
-    ));
-    IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0);
-}
-
-void Item_ItemsTime_SetTimesForPlayer(entity e)
-{
-    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-        IT_Write(e, it.m_id, it_times[it.m_id]);
-    ));
-    IT_Write(e, Items_MAX, it_times[Items_MAX]);
-}
-
-void Item_ItemsTime_SetTime(entity e, float t)
-{
-    if (!autocvar_sv_itemstime)
-        return;
-
-    GameItem item = e.itemdef;
-    if (item.instanceOfGameItem)
-    {
-               if (!item.instanceOfWeaponPickup)
-                       it_times[item.m_id] = t;
-               else if (e.weapons & WEPSET_SUPERWEAPONS)
-                       it_times[Items_MAX] = t;
-    }
-}
-
-void Item_ItemsTime_SetTimesForAllPlayers()
-{
-    FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it)), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it)));
-}
-
-float Item_ItemsTime_UpdateTime(entity e, float t)
-{
-    bool isavailable = (t == 0);
-    FOREACH_ENTITY(it.itemdef == e.itemdef || ((e.weapons & WEPSET_SUPERWEAPONS) && (it.weapons & WEPSET_SUPERWEAPONS) && clienttype(it) == CLIENTTYPE_NOTACLIENT), LAMBDA(
-        if (e == it) continue;
-        if (it.scheduledrespawntime <= time)
-            isavailable = true;
-        else if (t == 0 || it.scheduledrespawntime < t)
-            t = it.scheduledrespawntime;
-    ));
-    if (isavailable)
-        t = -t; // let know the client there's another available item
-    return t;
-}
-
-MUTATOR_HOOKFUNCTION(itemstime, reset_map_global)
-{
-    Item_ItemsTime_ResetTimes();
-    // ALL the times need to be reset before .reset()ing each item
-    // since Item_Reset schedules respawn of superweapons and powerups
-    FOREACH_ENTITY(IS_NOT_A_CLIENT(it), LAMBDA(
-        if (it.reset) Item_ItemsTime_SetTime(it, 0);
-    ));
-    Item_ItemsTime_SetTimesForAllPlayers();
-}
-
-MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver)
-{
-    entity player = M_ARGV(0, entity);
-
-    Item_ItemsTime_SetTimesForPlayer(player);
-}
-
-MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST)
-{
-    entity player = M_ARGV(0, entity);
-
-       if(IS_PLAYER(player))
-       {
-               // client became player on connection skipping putObserverInServer step
-               if (IS_REAL_CLIENT(player))
-               if (warmup_stage)
-                       Item_ItemsTime_SetTimesForPlayer(player);
-       }
-}
-
-MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn)
-{
-    if (warmup_stage) return;
-    entity player = M_ARGV(0, entity);
-
-    Item_ItemsTime_ResetTimesForPlayer(player);
-}
-
-#endif
-
-#ifdef CSQC
-
-void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime)
-{
-    float t = 0;
-    vector color = '0 0 0';
-    float picalpha;
-
-    if (autocvar_hud_panel_itemstime_hidespawned == 2)
-        picalpha = 1;
-    else if (item_available)
-    {
-        float BLINK_FACTOR = 0.15;
-        float BLINK_BASE = 0.85;
-        float BLINK_FREQ = 5;
-        picalpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
-    }
-    else
-        picalpha = 0.5;
-    t = floor(item_time - time + 0.999);
-    if (t < 5)
-        color = '0.7 0 0';
-    else if (t < 10)
-        color = '0.7 0.7 0';
-    else
-        color = '1 1 1';
-
-    vector picpos, numpos;
-    if (autocvar_hud_panel_itemstime_iconalign)
-    {
-        numpos = myPos;
-        picpos = myPos + eX * (ar - 1) * mySize_y;
-    }
-    else
-    {
-        numpos = myPos + eX * mySize_y;
-        picpos = myPos;
-    }
-
-    if (t > 0 && autocvar_hud_panel_itemstime_progressbar)
-    {
-        vector p_pos, p_size;
-        if (autocvar_hud_panel_itemstime_progressbar_reduced)
-        {
-            p_pos = numpos;
-            p_size = eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y;
-        }
-        else
-        {
-            p_pos = myPos;
-            p_size = mySize;
-        }
-        HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
-    }
-
-    if(autocvar_hud_panel_itemstime_text)
-    {
-        if(t > 0)
-            drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
-        else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it
-            drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
-        else // legacy code, if the image is missing just center the icon
-            picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2;
-    }
-    if (item_availableTime)
-        drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime);
-    drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
-}
-
-void HUD_ItemsTime()
-{
-    if (!autocvar__hud_configure)
-    {
-        if (!(
-            (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0)
-        ||     (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage))
-            )) { return; }
-    }
-    else
-    {
-        ItemsTime_time[ITEM_ArmorMega.m_id] = time + 0;
-        ItemsTime_time[ITEM_HealthMega.m_id] = time + 8;
-        ItemsTime_time[ITEM_Strength.m_id] = time + 0;
-        ItemsTime_time[ITEM_Shield.m_id] = time + 4;
-    }
-
-    int count = 0;
-    if (autocvar_hud_panel_itemstime_hidespawned == 1)
-    {
-        FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-            count += (ItemsTime_time[it.m_id] > time || -ItemsTime_time[it.m_id] > time);
-        ));
-        count += (ItemsTime_time[Items_MAX] > time || -ItemsTime_time[Items_MAX] > time);
-    }
-    else if (autocvar_hud_panel_itemstime_hidespawned == 2)
-    {
-        FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-            count += (ItemsTime_time[it.m_id] > time);
-        ));
-        count += (ItemsTime_time[Items_MAX] > time);
-    }
-    else
-    {
-        FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
-            count += (ItemsTime_time[it.m_id] != -1);
-        ));
-        count += (ItemsTime_time[Items_MAX] != -1);
-    }
-    if (count == 0)
-        return;
-
-    HUD_Panel_UpdateCvars();
-
-    vector pos, mySize;
-    pos = panel_pos;
-    mySize = panel_size;
-
-    if (panel_bg_padding)
-    {
-        pos += '1 1 0' * panel_bg_padding;
-        mySize -= '2 2 0' * panel_bg_padding;
-    }
-
-    float rows, columns;
-    float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1;
-    rows = HUD_GetRowCount(count, mySize, ar);
-    columns = ceil(count/rows);
-
-    vector itemstime_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
-
-    vector offset = '0 0 0';
-    float newSize;
-    if (autocvar_hud_panel_itemstime_dynamicsize)
-    {
-        if (autocvar__hud_configure)
-        if (menu_enabled != 2)
-            HUD_Panel_DrawBg(1); // also draw the bg of the entire panel
-
-        // reduce panel to avoid spacing items
-        if (itemstime_size.x / itemstime_size.y < ar)
-        {
-            newSize = rows * itemstime_size.x / ar;
-            pos.y += (mySize.y - newSize) / 2;
-            mySize.y = newSize;
-            itemstime_size.y = mySize.y / rows;
-        }
-        else
-        {
-            newSize = columns * itemstime_size.y * ar;
-            pos.x += (mySize.x - newSize) / 2;
-            mySize.x = newSize;
-            itemstime_size.x = mySize.x / columns;
-        }
-        panel_pos = pos - '1 1 0' * panel_bg_padding;
-        panel_size = mySize + '2 2 0' * panel_bg_padding;
-    }
-    else
-    {
-        if (itemstime_size.x/itemstime_size.y > ar)
-        {
-            newSize = ar * itemstime_size.y;
-            offset.x = itemstime_size.x - newSize;
-            pos.x += offset.x/2;
-            itemstime_size.x = newSize;
-        }
-        else
-        {
-            newSize = 1/ar * itemstime_size.x;
-            offset.y = itemstime_size.y - newSize;
-            pos.y += offset.y/2;
-            itemstime_size.y = newSize;
-        }
-    }
-
-    HUD_Scale_Enable();
-    HUD_Panel_DrawBg(1);
-
-    float row = 0, column = 0;
-    bool item_available;
-    int id = 0;
-    string icon = "";
-    FOREACH(Items, Item_ItemsTime_Allow(it) && ItemsTime_time[it.m_id] != -1, LAMBDA(
-       id = it.m_id;
-       icon = it.m_icon;
-
-LABEL(iteration)
-        float item_time = ItemsTime_time[id];
-        if (item_time < -1)
-        {
-            item_available = true;
-            item_time = -item_time;
-        }
-        else
-            item_available = (item_time <= time);
-
-        if (ItemsTime_time[id] >= 0)
-        {
-            if (time <= ItemsTime_time[id])
-                ItemsTime_availableTime[id] = 0;
-            else if (ItemsTime_availableTime[id] == 0)
-                ItemsTime_availableTime[id] = time;
-        }
-        else if (ItemsTime_availableTime[id] == 0)
-            ItemsTime_availableTime[id] = time;
-
-        float f = (time - ItemsTime_availableTime[id]) * 2;
-        f = (f > 1) ? 0 : bound(0, f, 1);
-
-        if (autocvar_hud_panel_itemstime_hidespawned == 1)
-            if (!(ItemsTime_time[id] > time || -ItemsTime_time[id] > time))
-                continue;
-
-        if (autocvar_hud_panel_itemstime_hidespawned == 2)
-            if (!(ItemsTime_time[id] > time))
-                continue;
-
-        DrawItemsTimeItem(pos + eX * column * (itemstime_size.x + offset.x) + eY * row * (itemstime_size.y + offset.y), itemstime_size, ar, icon, item_time, item_available, f);
-        ++row;
-        if (row >= rows)
-        {
-            row = 0;
-            column = column + 1;
-        }
-        if(id == Items_MAX) // can happen only in the last fake iteration
-               break;
-    ));
-    // add another fake iteration for superweapons time
-    if(id < Items_MAX && ItemsTime_time[Items_MAX] != -1)
-    {
-               id = Items_MAX;
-               icon = "superweapons";
-               goto iteration;
-    }
-}
-
-#endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/itemstime/_mod.inc b/qcsrc/common/mutators/mutator/itemstime/_mod.inc
new file mode 100644 (file)
index 0000000..5b34dd6
--- /dev/null
@@ -0,0 +1,2 @@
+// generated file; do not modify
+#include <common/mutators/mutator/itemstime/itemstime.qc>
diff --git a/qcsrc/common/mutators/mutator/itemstime/_mod.qh b/qcsrc/common/mutators/mutator/itemstime/_mod.qh
new file mode 100644 (file)
index 0000000..5c73eea
--- /dev/null
@@ -0,0 +1,2 @@
+// generated file; do not modify
+#include <common/mutators/mutator/itemstime/itemstime.qh>
diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc
new file mode 100644 (file)
index 0000000..3db867a
--- /dev/null
@@ -0,0 +1,431 @@
+#include "itemstime.qh"
+
+REGISTER_MUTATOR(itemstime, true);
+
+REGISTER_NET_TEMP(itemstime)
+
+#ifdef SVQC
+void IT_Write(entity e, int i, float f) {
+    if (!IS_REAL_CLIENT(e)) return;
+    msg_entity = e;
+    WriteHeader(MSG_ONE, itemstime);
+    WriteByte(MSG_ONE, i);
+    WriteFloat(MSG_ONE, f);
+}
+#endif
+
+#ifdef CSQC
+// reserve one more spot for superweapons time
+float ItemsTime_time[Items_MAX + 1];
+float ItemsTime_availableTime[Items_MAX + 1];
+NET_HANDLE(itemstime, bool isNew)
+{
+    int i = ReadByte();
+    float f = ReadFloat();
+    return = true;
+    ItemsTime_time[i] = f;
+}
+#endif
+
+#ifdef CSQC
+void Item_ItemsTime_Init()
+{
+    FOREACH(Items, true, LAMBDA(
+        ItemsTime_time[it.m_id] = -1;
+    ));
+    ItemsTime_time[Items_MAX] = -1;
+}
+
+STATIC_INIT(ItemsTime_Init) {
+    Item_ItemsTime_Init();
+}
+
+int autocvar_hud_panel_itemstime = 2;
+float autocvar_hud_panel_itemstime_dynamicsize = 1;
+float autocvar_hud_panel_itemstime_ratio = 2;
+int autocvar_hud_panel_itemstime_iconalign;
+bool autocvar_hud_panel_itemstime_progressbar = 0;
+float autocvar_hud_panel_itemstime_progressbar_maxtime = 30;
+string autocvar_hud_panel_itemstime_progressbar_name = "progressbar";
+float autocvar_hud_panel_itemstime_progressbar_reduced;
+bool autocvar_hud_panel_itemstime_hidespawned = 1;
+bool autocvar_hud_panel_itemstime_hidebig = false;
+int autocvar_hud_panel_itemstime_text = 1;
+#define hud_panel_itemstime_hidebig autocvar_hud_panel_itemstime_hidebig
+#else
+#define hud_panel_itemstime_hidebig false
+#endif
+
+bool Item_ItemsTime_SpectatorOnly(GameItem it)
+{
+    return (false
+    || it == ITEM_ArmorMega     || (it == ITEM_ArmorBig && !hud_panel_itemstime_hidebig)
+    || it == ITEM_HealthMega    || (it == ITEM_HealthBig && !hud_panel_itemstime_hidebig)
+    );
+}
+
+bool Item_ItemsTime_Allow(GameItem it)
+{
+    return (false
+    || it.instanceOfPowerup
+    || Item_ItemsTime_SpectatorOnly(it)
+    );
+}
+
+#ifdef SVQC
+
+// reserve one more spot for superweapons time
+float it_times[Items_MAX + 1];
+
+void Item_ItemsTime_Init()
+{
+    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+        it_times[it.m_id] = -1;
+    ));
+    it_times[Items_MAX] = -1;
+}
+
+STATIC_INIT(ItemsTime_Init) {
+    // items time
+    Item_ItemsTime_Init();
+}
+
+void Item_ItemsTime_ResetTimes()
+{
+    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+        it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0;
+    ));
+    it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0;
+}
+
+void Item_ItemsTime_ResetTimesForPlayer(entity e)
+{
+    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+        IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0);
+    ));
+    IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0);
+}
+
+void Item_ItemsTime_SetTimesForPlayer(entity e)
+{
+    FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+        IT_Write(e, it.m_id, it_times[it.m_id]);
+    ));
+    IT_Write(e, Items_MAX, it_times[Items_MAX]);
+}
+
+void Item_ItemsTime_SetTime(entity e, float t)
+{
+    if (!autocvar_sv_itemstime)
+        return;
+
+    GameItem item = e.itemdef;
+    if (item.instanceOfGameItem)
+    {
+               if (!item.instanceOfWeaponPickup)
+                       it_times[item.m_id] = t;
+               else if (e.weapons & WEPSET_SUPERWEAPONS)
+                       it_times[Items_MAX] = t;
+    }
+}
+
+void Item_ItemsTime_SetTimesForAllPlayers()
+{
+    FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it) || autocvar_sv_itemstime == 2), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it)));
+}
+
+float Item_ItemsTime_UpdateTime(entity e, float t)
+{
+    bool isavailable = (t == 0);
+    IL_EACH(g_items, it != e,
+    {
+        if(!(it.itemdef == e.itemdef || ((e.weapons & WEPSET_SUPERWEAPONS) && (it.weapons & WEPSET_SUPERWEAPONS))))
+            continue;
+        if (it.scheduledrespawntime <= time)
+            isavailable = true;
+        else if (t == 0 || it.scheduledrespawntime < t)
+            t = it.scheduledrespawntime;
+    });
+    if (isavailable)
+        t = -t; // let know the client there's another available item
+    return t;
+}
+
+MUTATOR_HOOKFUNCTION(itemstime, reset_map_global)
+{
+    Item_ItemsTime_ResetTimes();
+    // ALL the times need to be reset before .reset()ing each item
+    // since Item_Reset schedules respawn of superweapons and powerups
+    IL_EACH(g_items, it.reset,
+    {
+        Item_ItemsTime_SetTime(it, 0);
+    });
+    Item_ItemsTime_SetTimesForAllPlayers();
+}
+
+MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver)
+{
+    entity player = M_ARGV(0, entity);
+
+    Item_ItemsTime_SetTimesForPlayer(player);
+}
+
+MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST)
+{
+    entity player = M_ARGV(0, entity);
+
+       if(IS_PLAYER(player))
+       {
+               // client became player on connection skipping putObserverInServer step
+               if (IS_REAL_CLIENT(player))
+               if (warmup_stage || autocvar_sv_itemstime == 2)
+                       Item_ItemsTime_SetTimesForPlayer(player);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn)
+{
+    if (warmup_stage || autocvar_sv_itemstime == 2) return;
+    entity player = M_ARGV(0, entity);
+
+    Item_ItemsTime_ResetTimesForPlayer(player);
+}
+
+#endif
+
+#ifdef CSQC
+
+void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime)
+{
+    float t = 0;
+    vector color = '0 0 0';
+    float picalpha;
+
+    if (autocvar_hud_panel_itemstime_hidespawned == 2)
+        picalpha = 1;
+    else if (item_available)
+    {
+        float BLINK_FACTOR = 0.15;
+        float BLINK_BASE = 0.85;
+        float BLINK_FREQ = 5;
+        picalpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
+    }
+    else
+        picalpha = 0.5;
+    t = floor(item_time - time + 0.999);
+    if (t < 5)
+        color = '0.7 0 0';
+    else if (t < 10)
+        color = '0.7 0.7 0';
+    else
+        color = '1 1 1';
+
+    vector picpos, numpos;
+    if (autocvar_hud_panel_itemstime_iconalign)
+    {
+        numpos = myPos;
+        picpos = myPos + eX * (ar - 1) * mySize_y;
+    }
+    else
+    {
+        numpos = myPos + eX * mySize_y;
+        picpos = myPos;
+    }
+
+    if (t > 0 && autocvar_hud_panel_itemstime_progressbar)
+    {
+        vector p_pos, p_size;
+        if (autocvar_hud_panel_itemstime_progressbar_reduced)
+        {
+            p_pos = numpos;
+            p_size = eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y;
+        }
+        else
+        {
+            p_pos = myPos;
+            p_size = mySize;
+        }
+        HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+    }
+
+    if(autocvar_hud_panel_itemstime_text)
+    {
+        if(t > 0)
+            drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+        else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it
+            drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
+        else // legacy code, if the image is missing just center the icon
+            picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2;
+    }
+    if (item_availableTime)
+        drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime);
+    drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
+}
+
+void HUD_ItemsTime()
+{
+    if (!autocvar__hud_configure)
+    {
+        if (!(
+            (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0)
+        ||     (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2))
+            )) { return; }
+    }
+    else
+    {
+        ItemsTime_time[ITEM_ArmorMega.m_id] = time + 0;
+        ItemsTime_time[ITEM_HealthMega.m_id] = time + 8;
+        ItemsTime_time[ITEM_Strength.m_id] = time + 0;
+        ItemsTime_time[ITEM_Shield.m_id] = time + 4;
+    }
+
+    int count = 0;
+    if (autocvar_hud_panel_itemstime_hidespawned == 1)
+    {
+        FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+            count += (ItemsTime_time[it.m_id] > time || -ItemsTime_time[it.m_id] > time);
+        ));
+        count += (ItemsTime_time[Items_MAX] > time || -ItemsTime_time[Items_MAX] > time);
+    }
+    else if (autocvar_hud_panel_itemstime_hidespawned == 2)
+    {
+        FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+            count += (ItemsTime_time[it.m_id] > time);
+        ));
+        count += (ItemsTime_time[Items_MAX] > time);
+    }
+    else
+    {
+        FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA(
+            count += (ItemsTime_time[it.m_id] != -1);
+        ));
+        count += (ItemsTime_time[Items_MAX] != -1);
+    }
+    if (count == 0)
+        return;
+
+    HUD_Panel_LoadCvars();
+
+    vector pos, mySize;
+    pos = panel_pos;
+    mySize = panel_size;
+
+    if (panel_bg_padding)
+    {
+        pos += '1 1 0' * panel_bg_padding;
+        mySize -= '2 2 0' * panel_bg_padding;
+    }
+
+    float rows, columns;
+    float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1;
+    rows = HUD_GetRowCount(count, mySize, ar);
+    columns = ceil(count/rows);
+
+    vector itemstime_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
+
+    vector offset = '0 0 0';
+    float newSize;
+    if (autocvar_hud_panel_itemstime_dynamicsize)
+    {
+        if (autocvar__hud_configure)
+        if (hud_configure_menu_open != 2)
+            HUD_Panel_DrawBg(); // also draw the bg of the entire panel
+
+        // reduce panel to avoid spacing items
+        if (itemstime_size.x / itemstime_size.y < ar)
+        {
+            newSize = rows * itemstime_size.x / ar;
+            pos.y += (mySize.y - newSize) / 2;
+            mySize.y = newSize;
+            itemstime_size.y = mySize.y / rows;
+        }
+        else
+        {
+            newSize = columns * itemstime_size.y * ar;
+            pos.x += (mySize.x - newSize) / 2;
+            mySize.x = newSize;
+            itemstime_size.x = mySize.x / columns;
+        }
+        panel_pos = pos - '1 1 0' * panel_bg_padding;
+        panel_size = mySize + '2 2 0' * panel_bg_padding;
+    }
+    else
+    {
+        if (itemstime_size.x/itemstime_size.y > ar)
+        {
+            newSize = ar * itemstime_size.y;
+            offset.x = itemstime_size.x - newSize;
+            pos.x += offset.x/2;
+            itemstime_size.x = newSize;
+        }
+        else
+        {
+            newSize = 1/ar * itemstime_size.x;
+            offset.y = itemstime_size.y - newSize;
+            pos.y += offset.y/2;
+            itemstime_size.y = newSize;
+        }
+    }
+
+    HUD_Scale_Enable();
+    HUD_Panel_DrawBg();
+
+    float row = 0, column = 0;
+    bool item_available;
+    int id = 0;
+    string icon = "";
+    FOREACH(Items, Item_ItemsTime_Allow(it) && ItemsTime_time[it.m_id] != -1, LAMBDA(
+       id = it.m_id;
+       icon = it.m_icon;
+
+LABEL(iteration)
+        float item_time = ItemsTime_time[id];
+        if (item_time < -1)
+        {
+            item_available = true;
+            item_time = -item_time;
+        }
+        else
+            item_available = (item_time <= time);
+
+        if (ItemsTime_time[id] >= 0)
+        {
+            if (time <= ItemsTime_time[id])
+                ItemsTime_availableTime[id] = 0;
+            else if (ItemsTime_availableTime[id] == 0)
+                ItemsTime_availableTime[id] = time;
+        }
+        else if (ItemsTime_availableTime[id] == 0)
+            ItemsTime_availableTime[id] = time;
+
+        float f = (time - ItemsTime_availableTime[id]) * 2;
+        f = (f > 1) ? 0 : bound(0, f, 1);
+
+        if (autocvar_hud_panel_itemstime_hidespawned == 1)
+            if (!(ItemsTime_time[id] > time || -ItemsTime_time[id] > time))
+                continue;
+
+        if (autocvar_hud_panel_itemstime_hidespawned == 2)
+            if (!(ItemsTime_time[id] > time))
+                continue;
+
+        DrawItemsTimeItem(pos + eX * column * (itemstime_size.x + offset.x) + eY * row * (itemstime_size.y + offset.y), itemstime_size, ar, icon, item_time, item_available, f);
+        ++row;
+        if (row >= rows)
+        {
+            row = 0;
+            column = column + 1;
+        }
+        if(id == Items_MAX) // can happen only in the last fake iteration
+               break;
+    ));
+    // add another fake iteration for superweapons time
+    if(id < Items_MAX && ItemsTime_time[Items_MAX] != -1)
+    {
+               id = Items_MAX;
+               icon = "superweapons";
+               goto iteration;
+    }
+}
+
+#endif
diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qh b/qcsrc/common/mutators/mutator/itemstime/itemstime.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index db31be3fddf35d6efcaf5b0876a48b40e9990f4b..da02f0808601dea33caa0dc281dd04d6b96cdd6a 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/melee_only/melee_only.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/melee_only/sv_melee_only.qc>
+#endif
index 2228d64f1844e84595b50a576947ecce36c54e2e..297bb2b9620a137d3505244621771440e6247bdd 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/melee_only/melee_only.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/melee_only/sv_melee_only.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc
deleted file mode 100644 (file)
index ecd5fc7..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball);
-
-MUTATOR_HOOKFUNCTION(melee_only, SetStartItems)
-{
-       start_ammo_shells = warmup_start_ammo_shells = 0;
-       start_weapons = warmup_start_weapons = WEPSET(SHOTGUN);
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon)
-{
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, FilterItem)
-{
-       entity item = M_ARGV(0, entity);
-
-       switch (item.items)
-       {
-               case ITEM_HealthSmall.m_itemid:
-               case ITEM_ArmorSmall.m_itemid:
-                       return false;
-       }
-
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":MeleeOnly");
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Melee Only Arena");
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/melee_only/module.inc b/qcsrc/common/mutators/mutator/melee_only/module.inc
deleted file mode 100644 (file)
index c711556..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "melee_only.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc
new file mode 100644 (file)
index 0000000..a542921
--- /dev/null
@@ -0,0 +1,44 @@
+#include "sv_melee_only.qh"
+
+REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !cvar("g_overkill") && !g_nexball);
+
+MUTATOR_HOOKFUNCTION(melee_only, SetStartItems, CBC_ORDER_LAST)
+{
+       start_ammo_shells = warmup_start_ammo_shells = 0;
+       start_weapons = warmup_start_weapons = WEPSET(SHOTGUN);
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, SetWeaponArena)
+{
+       // turn weapon arena off
+       M_ARGV(0, string) = "off";
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon)
+{
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, FilterItem)
+{
+       entity item = M_ARGV(0, entity);
+
+       switch (item.itemdef)
+       {
+               case ITEM_HealthSmall:
+               case ITEM_ArmorSmall:
+                       return false;
+       }
+
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":MeleeOnly");
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Melee Only Arena");
+}
diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 8fcc96ea218b6810bdc65416d997fe14e2f38e42..b144ca4b6aea060507cb3b9d13cd239996b25132 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/midair/midair.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/midair/sv_midair.qc>
+#endif
index 48272b8709032972b531a2c262eb100259150ada..f96da138670e5e2891db759371d3fe22d6761331 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/midair/midair.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/midair/sv_midair.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/midair/midair.qc b/qcsrc/common/mutators/mutator/midair/midair.qc
deleted file mode 100644 (file)
index cda6b4c..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifdef IMPLEMENTATION
-
-float autocvar_g_midair_shieldtime;
-
-REGISTER_MUTATOR(midair, cvar("g_midair"));
-
-.float midair_shieldtime;
-
-MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-       
-       if(IS_PLAYER(frag_attacker))
-       if(IS_PLAYER(frag_target))
-       if(time < frag_target.midair_shieldtime)
-               M_ARGV(4, float) = 0;
-}
-
-MUTATOR_HOOKFUNCTION(midair, PlayerPowerups)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(time >= game_starttime)
-       if(IS_ONGROUND(player))
-       {
-               player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
-               player.midair_shieldtime = max(player.midair_shieldtime, time + autocvar_g_midair_shieldtime);
-       }
-}
-
-MUTATOR_HOOKFUNCTION(midair, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(IS_BOT_CLIENT(player))
-               player.bot_moveskill = 0; // disable bunnyhopping
-}
-
-MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":midair");
-}
-
-MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Midair");
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/midair/module.inc b/qcsrc/common/mutators/mutator/midair/module.inc
deleted file mode 100644 (file)
index 10b1789..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "midair.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qc b/qcsrc/common/mutators/mutator/midair/sv_midair.qc
new file mode 100644 (file)
index 0000000..40747ff
--- /dev/null
@@ -0,0 +1,48 @@
+#include "sv_midair.qh"
+
+float autocvar_g_midair_shieldtime;
+
+REGISTER_MUTATOR(midair, cvar("g_midair"));
+
+.float midair_shieldtime;
+
+MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+
+       if(IS_PLAYER(frag_attacker))
+       if(IS_PLAYER(frag_target))
+       if(time < frag_target.midair_shieldtime)
+               M_ARGV(4, float) = 0;
+}
+
+MUTATOR_HOOKFUNCTION(midair, PlayerPowerups)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(time >= game_starttime)
+       if(IS_ONGROUND(player))
+       {
+               player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
+               player.midair_shieldtime = max(player.midair_shieldtime, time + autocvar_g_midair_shieldtime);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(midair, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(IS_BOT_CLIENT(player))
+               player.bot_moveskill = 0; // disable bunnyhopping
+}
+
+MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":midair");
+}
+
+MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Midair");
+}
diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qh b/qcsrc/common/mutators/mutator/midair/sv_midair.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/multijump/module.inc b/qcsrc/common/mutators/mutator/multijump/module.inc
deleted file mode 100644 (file)
index 3103320..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifndef MENUQC
-#include "multijump.qc"
-#endif
index 780836d4986166d4c6f6cce4457dbc2a0e90d1a0..ecedc475961585184fd61bad6785d2986f00f23e 100644 (file)
@@ -1,4 +1,7 @@
-#ifdef IMPLEMENTATION
+#include "multijump.qh"
+
+#ifdef GAMEQC
+
 #ifdef SVQC
        #include <server/antilag.qh>
 #endif
@@ -60,7 +63,7 @@ MUTATOR_HOOKFUNCTION(multijump, PlayerJump)
 
        int phys_multijump = PHYS_MULTIJUMP(player);
 
-       if(!M_ARGV(2, bool) && player.multijump_ready && (PHYS_MULTIJUMP_COUNT(player) < phys_multijump || phys_multijump == -1) && player.velocity_z > PHYS_MULTIJUMP_SPEED(player) && 
+       if(!M_ARGV(2, bool) && player.multijump_ready && (PHYS_MULTIJUMP_COUNT(player) < phys_multijump || phys_multijump == -1) && player.velocity_z > PHYS_MULTIJUMP_SPEED(player) &&
                (!PHYS_MULTIJUMP_MAXSPEED(player) || vdist(player.velocity, <=, PHYS_MULTIJUMP_MAXSPEED(player))))
        {
                if (PHYS_MULTIJUMP(player))
@@ -126,4 +129,5 @@ MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString)
 }
 
 #endif
+
 #endif
diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qh b/qcsrc/common/mutators/mutator/multijump/multijump.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/nades/module.inc b/qcsrc/common/mutators/mutator/nades/module.inc
deleted file mode 100644 (file)
index e03900d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "nades.qc"
-#ifndef MENUQC
-#include "net.qc"
-#endif
index 8a7337f6103a9020849196832b0e24484f9d21be..bcdbe0cd920973e256c142f27d1d216ef8e90f02 100644 (file)
@@ -1,4 +1,4 @@
-#ifndef MENUQC
+#ifdef GAMEQC
 #define NADE_PROJECTILE(i, projectile, trail) MACRO_BEGIN { \
     this.m_projectile[i] = projectile; \
     this.m_trail[i] = trail; \
index 1d5a8c37b38b2bf9f167bf041830a957dabb5bbd..c09e221e469ec96b7e912b3bc0491dd372cc68e7 100644 (file)
@@ -1,7 +1,5 @@
 #include "nades.qh"
 
-#ifdef IMPLEMENTATION
-
 #ifdef SVQC
 bool autocvar_g_nades_nade_small;
 float autocvar_g_nades_spread = 0.04;
@@ -9,7 +7,7 @@ float autocvar_g_nades_spread = 0.04;
 
 REGISTER_STAT(NADES_SMALL, int, autocvar_g_nades_nade_small)
 
-#ifndef MENUQC
+#ifdef GAMEQC
 entity Nade_TrailEffect(int proj, int nade_team)
 {
     switch (proj)
@@ -94,7 +92,7 @@ MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile)
                proj.maxs = '16 16 16';
        }
        proj.colormod = nade_type.m_color;
-       proj.move_movetype = MOVETYPE_BOUNCE;
+       set_movetype(proj, MOVETYPE_BOUNCE);
        settouch(proj, func_null);
        proj.scale = 1.5;
        proj.avelocity = randomvec() * 720;
@@ -147,23 +145,25 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan
 
 #ifdef SVQC
 
-#include <common/gamemodes/all.qh>
-#include <common/monsters/spawn.qh>
+#include <common/gamemodes/_mod.qh>
+#include <common/monsters/sv_spawn.qh>
 #include <common/monsters/sv_monsters.qh>
 #include <server/g_subs.qh>
 
 REGISTER_MUTATOR(nades, cvar("g_nades"));
 
 .float nade_time_primed;
+.float nade_lifetime;
 
 .entity nade_spawnloc;
 
+
 void nade_timer_think(entity this)
 {
-       this.skin = 8 - (this.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
+       this.skin = 8 - (this.owner.wait - time) / (this.owner.nade_lifetime / 10);
        this.nextthink = time;
        if(!this.owner || wasfreed(this.owner))
-               remove(this);
+               delete(this);
 }
 
 void nade_burn_spawn(entity _nade)
@@ -213,7 +213,7 @@ void napalm_damage(entity this, float dist, float damage, float edgedamage, floa
                        if(d < dist)
                        {
                                e.fireball_impactvec = p;
-                               RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+                               RandomSelection_AddEnt(e, 1 / (1 + d), !Fire_IsBurning(e));
                        }
                }
        if(RandomSelection_chosen_ent)
@@ -232,13 +232,13 @@ void napalm_ball_think(entity this)
        if(round_handler_IsActive())
        if(!round_handler_IsRoundStarted())
        {
-               remove(this);
+               delete(this);
                return;
        }
 
        if(time > this.pushltime)
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -273,7 +273,7 @@ void nade_napalm_ball(entity this)
        proj.team = this.owner.team;
        proj.bot_dodge = true;
        proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
-       proj.movetype = MOVETYPE_BOUNCE;
+       set_movetype(proj, MOVETYPE_BOUNCE);
        proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id;
        PROJECTILE_MAKETRIGGER(proj);
        setmodel(proj, MDL_Null);
@@ -294,6 +294,8 @@ void nade_napalm_ball(entity this)
 
        proj.angles = vectoangles(proj.velocity);
        proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
 
        //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true);
@@ -306,13 +308,13 @@ void napalm_fountain_think(entity this)
        if(round_handler_IsActive())
        if(!round_handler_IsRoundStarted())
        {
-               remove(this);
+               delete(this);
                return;
        }
 
        if(time >= this.ltime)
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -350,13 +352,16 @@ void nade_napalm_boom(entity this)
        fountain.owner = this.owner;
        fountain.realowner = this.realowner;
        fountain.origin = this.origin;
+       fountain.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, fountain);
+       IL_PUSH(g_bot_dodge, fountain);
        setorigin(fountain, fountain.origin);
        setthink(fountain, napalm_fountain_think);
        fountain.nextthink = time;
        fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
        fountain.pushltime = fountain.ltime;
        fountain.team = this.team;
-       fountain.movetype = MOVETYPE_TOSS;
+       set_movetype(fountain, MOVETYPE_TOSS);
        fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id;
        fountain.bot_dodge = true;
        fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage;
@@ -379,7 +384,7 @@ void nade_ice_think(entity this)
        if(round_handler_IsActive())
        if(!round_handler_IsRoundStarted())
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -396,7 +401,7 @@ void nade_ice_think(entity this)
                        Damage_DamageInfo(this.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
                                autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, this.projectiledeathtype, 0, this);
                }
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -426,16 +431,13 @@ void nade_ice_think(entity this)
 
        float current_freeze_time = this.ltime - time - 0.1;
 
-       entity e;
-       for(e = findradius(this.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
-       if(e != this)
-       if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, this.realowner) || e == this.realowner))
-       if(e.takedamage && !IS_DEAD(e))
-       if(e.health > 0)
-       if(!e.revival_time || ((time - e.revival_time) >= 1.5))
-       if(!STAT(FROZEN, e))
-       if(current_freeze_time > 0)
-               nade_ice_freeze(this, e, current_freeze_time);
+       FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && current_freeze_time > 0,
+       {
+               if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(it, this.realowner) || it == this.realowner))
+               if(!it.revival_time || ((time - it.revival_time) >= 1.5))
+               if(!STAT(FROZEN, it))
+                       nade_ice_freeze(this, it, current_freeze_time);
+       });
 }
 
 void nade_ice_boom(entity this)
@@ -451,7 +453,7 @@ void nade_ice_boom(entity this)
        fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
        fountain.pushltime = fountain.wait = fountain.ltime;
        fountain.team = this.team;
-       fountain.movetype = MOVETYPE_TOSS;
+       set_movetype(fountain, MOVETYPE_TOSS);
        fountain.projectiledeathtype = DEATH_NADE_ICE.m_id;
        fountain.bot_dodge = false;
        setsize(fountain, '-16 -16 -16', '16 16 16');
@@ -497,7 +499,7 @@ void nade_spawn_boom(entity this)
        entity spawnloc = spawn();
        setorigin(spawnloc, this.origin);
        setsize(spawnloc, this.realowner.mins, this.realowner.maxs);
-       spawnloc.movetype = MOVETYPE_NONE;
+       set_movetype(spawnloc, MOVETYPE_NONE);
        spawnloc.solid = SOLID_NOT;
        spawnloc.drawonlytoclient = this.realowner;
        spawnloc.effects = EF_STARDUST;
@@ -505,7 +507,7 @@ void nade_spawn_boom(entity this)
 
        if(this.realowner.nade_spawnloc)
        {
-               remove(this.realowner.nade_spawnloc);
+               delete(this.realowner.nade_spawnloc);
                this.realowner.nade_spawnloc = NULL;
        }
 
@@ -516,7 +518,7 @@ void nades_orb_think(entity this)
 {
        if(time >= this.ltime)
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -562,35 +564,33 @@ entity nades_spawn_orb(entity own, entity realown, vector org, float orb_ltime,
        return orb;
 }
 
-void nade_entrap_touch(entity this)
+void nade_entrap_touch(entity this, entity toucher)
 {
-       if(DIFF_TEAM(other, this.realowner)) // TODO: what if realowner changes team or disconnects?
+       if(DIFF_TEAM(toucher, this.realowner)) // TODO: what if realowner changes team or disconnects?
        {
-               if (!isPushable(other))
+               if (!isPushable(toucher))
                        return;
 
-               float pushdeltatime = time - other.lastpushtime;
+               float pushdeltatime = time - toucher.lastpushtime;
                if (pushdeltatime > 0.15) pushdeltatime = 0;
-               other.lastpushtime = time;
+               toucher.lastpushtime = time;
                if(!pushdeltatime) return;
 
                // div0: ticrate independent, 1 = identity (not 20)
-#ifdef SVQC
-               other.velocity = other.velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime);
+               toucher.velocity = toucher.velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime);
 
-               UpdateCSQCProjectile(other);
-#elif defined(CSQC)
-               other.move_velocity = other.move_velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime);
-#endif
+       #ifdef SVQC
+               UpdateCSQCProjectile(toucher);
+       #endif
        }
 
-       if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) || IS_MONSTER(other) )
+       if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) )
        {
-               entity show_tint = (IS_VEHICLE(other)) ? other.owner : other;
+               entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher;
                STAT(ENTRAP_ORB, show_tint) = time + 0.1;
 
                float tint_alpha = 0.75;
-               if(SAME_TEAM(other, this.realowner))
+               if(SAME_TEAM(toucher, this.realowner))
                        tint_alpha = 0.45;
                STAT(ENTRAP_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime;
        }
@@ -604,43 +604,43 @@ void nade_entrap_boom(entity this)
        orb.colormod = NADE_TYPE_ENTRAP.m_color;
 }
 
-void nade_heal_touch(entity this)
+void nade_heal_touch(entity this, entity toucher)
 {
        float maxhealth;
        float health_factor;
-       if(IS_PLAYER(other) || IS_MONSTER(other))
-       if(!IS_DEAD(other))
-       if(!STAT(FROZEN, other))
+       if(IS_PLAYER(toucher) || IS_MONSTER(toucher))
+       if(!IS_DEAD(toucher))
+       if(!STAT(FROZEN, toucher))
        {
                health_factor = autocvar_g_nades_heal_rate*frametime/2;
-               if ( other != this.realowner )
+               if ( toucher != this.realowner )
                {
-                       if ( SAME_TEAM(other,this) )
+                       if ( SAME_TEAM(toucher,this) )
                                health_factor *= autocvar_g_nades_heal_friend;
                        else
                                health_factor *= autocvar_g_nades_heal_foe;
                }
                if ( health_factor > 0 )
                {
-                       maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max;
-                       if ( other.health < maxhealth )
+                       maxhealth = (IS_MONSTER(toucher)) ? toucher.max_health : g_pickup_healthmega_max;
+                       if ( toucher.health < maxhealth )
                        {
                                if ( this.nade_show_particles )
-                                       Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1);
-                               other.health = min(other.health+health_factor, maxhealth);
+                                       Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1);
+                               toucher.health = min(toucher.health+health_factor, maxhealth);
                        }
-                       other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+                       toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
                }
                else if ( health_factor < 0 )
                {
-                       Damage(other,this,this.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0');
+                       Damage(toucher,this,this.realowner,-health_factor,DEATH_NADE_HEAL.m_id,toucher.origin,'0 0 0');
                }
 
        }
 
-       if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) )
+       if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) )
        {
-               entity show_red = (IS_VEHICLE(other)) ? other.owner : other;
+               entity show_red = (IS_VEHICLE(toucher)) ? toucher.owner : toucher;
                show_red.stat_healing_orb = time+0.1;
                show_red.stat_healing_orb_alpha = 0.75 * (this.ltime - time) / this.orb_lifetime;
        }
@@ -656,7 +656,7 @@ void nade_heal_boom(entity this)
 
 void nade_monster_boom(entity this)
 {
-       entity e = spawnmonster(this.pokenade_type, 0, this.realowner, this.realowner, this.origin, false, false, 1);
+       entity e = spawnmonster(spawn(), this.pokenade_type, 0, this.realowner, this.realowner, this.origin, false, false, 1);
 
        if(autocvar_g_nades_pokenade_monster_lifetime > 0)
                e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
@@ -736,13 +736,12 @@ void nade_boom(entity this)
                case NADE_TYPE_ENTRAP: nade_entrap_boom(this); break;
        }
 
-       FOREACH_ENTITY_ENT(aiment, this,
+       IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this,
        {
-               if(it.classname == "grapplinghook")
-                       RemoveGrapplingHook(it.realowner);
+               RemoveGrapplingHook(it.realowner);
        });
 
-       remove(this);
+       delete(this);
 }
 
 void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, string pntype);
@@ -759,24 +758,24 @@ void nade_pickup(entity this, entity thenade)
 }
 
 bool CanThrowNade(entity this);
-void nade_touch(entity this)
+void nade_touch(entity this, entity toucher)
 {
-       if(other)
+       if(toucher)
                UpdateCSQCProjectile(this);
 
-       if(other == this.realowner)
+       if(toucher == this.realowner)
                return; // no this impacts
 
        if(autocvar_g_nades_pickup)
        if(time >= this.spawnshieldtime)
-       if(!other.nade && this.health == this.max_health) // no boosted shot pickups, thank you very much
-       if(!other.frozen)
-       if(CanThrowNade(other)) // prevent some obvious things, like dead players
-       if(IS_REAL_CLIENT(other)) // above checks for IS_PLAYER, don't need to do it here
+       if(!toucher.nade && this.health == this.max_health) // no boosted shot pickups, thank you very much
+       if(!STAT(FROZEN, toucher))
+       if(CanThrowNade(toucher)) // prevent some obvious things, like dead players
+       if(IS_REAL_CLIENT(toucher)) // above checks for IS_PLAYER, don't need to do it here
        {
-               nade_pickup(other, this);
+               nade_pickup(toucher, this);
                sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
-               remove(this);
+               delete(this);
                return;
        }
        /*float is_weapclip = 0;
@@ -786,16 +785,15 @@ void nade_touch(entity this)
                is_weapclip = 1;*/
        if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip)
        {
-               FOREACH_ENTITY_ENT(aiment, this,
+               IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this,
                {
-                       if(it.classname == "grapplinghook")
-                               RemoveGrapplingHook(it.realowner);
+                       RemoveGrapplingHook(it.realowner);
                });
-               remove(this);
+               delete(this);
                return;
        }
 
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        //setsize(this, '-2 -2 -2', '2 2 2');
        //UpdateCSQCProjectile(this);
@@ -805,7 +803,7 @@ void nade_touch(entity this)
                return;
        }
 
-       this.enemy = other;
+       this.enemy = toucher;
        nade_boom(this);
 }
 
@@ -866,7 +864,7 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i
        if(this.health == this.max_health)
        {
                sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
-               this.nextthink = max(time + autocvar_g_nades_nade_lifetime, time);
+               this.nextthink = max(time + this.nade_lifetime, time);
                setthink(this, nade_beep);
        }
 
@@ -889,12 +887,13 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time)
        entity _nade = e.nade;
        e.nade = NULL;
 
-       remove(e.fake_nade);
+       delete(e.fake_nade);
        e.fake_nade = NULL;
 
        makevectors(e.v_angle);
 
-       W_SetupShot(e, false, false, SND_Null, CH_WEAPON_A, 0);
+       // NOTE: always throw from first weapon entity?
+       W_SetupShot(e, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0);
 
        Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_NADES);
 
@@ -912,9 +911,9 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time)
                setsize(_nade, '-8 -8 -8', '8 8 8');
        else
                setsize(_nade, '-16 -16 -16', '16 16 16');
-       _nade.movetype = MOVETYPE_BOUNCE;
+       set_movetype(_nade, MOVETYPE_BOUNCE);
 
-       tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade);
+       tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, MOVE_NOMONSTERS, _nade);
        if (trace_startsolid)
                setorigin(_nade, e.origin);
 
@@ -944,8 +943,11 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time)
        _nade.gravity = 1;
        _nade.missile_flags = MIF_SPLASH | MIF_ARC;
        _nade.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, _nade);
        _nade.angles = vectoangles(_nade.velocity);
        _nade.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, _nade);
+       IL_PUSH(g_bot_dodge, _nade);
        _nade.projectiledeathtype = DEATH_NADE.m_id;
        _nade.toss_time = time;
        _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX);
@@ -1002,10 +1004,10 @@ MUTATOR_HOOKFUNCTION(nades, PutClientInServer)
        nades_RemoveBonus(player);
 }
 
-float nade_customize(entity this)
+bool nade_customize(entity this, entity client)
 {
-       //if(IS_SPEC(other)) { return false; }
-       if(other == this.exteriormodeltoclient || (IS_SPEC(other) && other.enemy == this.exteriormodeltoclient))
+       //if(IS_SPEC(client)) { return false; }
+       if(client == this.exteriormodeltoclient || (IS_SPEC(client) && client.enemy == this.exteriormodeltoclient))
        {
                // somewhat hide the model, but keep the glow
                //this.effects = 0;
@@ -1048,6 +1050,7 @@ void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, strin
        setthink(n, nade_beep);
        n.nextthink = max(n.wait - 3, time);
        n.projectiledeathtype = DEATH_NADE.m_id;
+       n.nade_lifetime = ntime;
 
        setmodel(fn, MDL_NADE_VIEW);
        .entity weaponentity = weaponentities[0]; // TODO: unhardcode
@@ -1070,10 +1073,10 @@ void nade_prime(entity this)
                return; // only allow bonus nades
 
        if(this.nade)
-               remove(this.nade);
+               delete(this.nade);
 
        if(this.fake_nade)
-               remove(this.fake_nade);
+               delete(this.fake_nade);
 
        int ntype;
        string pntype = this.pokenade_type;
@@ -1154,9 +1157,9 @@ void nades_CheckThrow(entity this)
 void nades_Clear(entity player)
 {
        if(player.nade)
-               remove(player.nade);
+               delete(player.nade);
        if(player.fake_nade)
-               remove(player.fake_nade);
+               delete(player.fake_nade);
 
        player.nade = player.fake_nade = NULL;
        player.nade_timer = 0;
@@ -1174,18 +1177,6 @@ CLASS(NadeOffhand, OffhandWeapon)
     METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed))
     {
        entity held_nade = player.nade;
-               if (held_nade)
-               {
-                       player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1);
-                       // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed);
-                       makevectors(player.angles);
-                       held_nade.velocity = player.velocity;
-                       setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
-                       held_nade.angles_y = player.angles.y;
-
-                       if (time + 0.1 >= held_nade.wait)
-                               toss_nade(player, false, '0 0 0', time + 0.05);
-               }
 
         if (!CanThrowNade(player)) return;
         if (!(time > player.nade_refire)) return;
@@ -1227,6 +1218,20 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
 
        if (player.nade && (player.offhand != OFFHAND_NADE || (player.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, player, player.nade_altbutton);
 
+       entity held_nade = player.nade;
+       if (held_nade)
+       {
+               player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / held_nade.nade_lifetime, 1);
+               // LOG_TRACEF("%d %d", player.nade_timer, time - held_nade.nade_time_primed);
+               makevectors(player.angles);
+               held_nade.velocity = player.velocity;
+               setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
+               held_nade.angles_y = player.angles.y;
+
+               if (time + 0.1 >= held_nade.wait)
+                       toss_nade(player, false, '0 0 0', time + 0.05);
+       }
+
        if(IS_PLAYER(player))
        {
                if ( autocvar_g_nades_bonus && autocvar_g_nades )
@@ -1354,7 +1359,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerSpawn)
 
                if(player.nade_spawnloc.cnt <= 0)
                {
-                       remove(player.nade_spawnloc);
+                       delete(player.nade_spawnloc);
                        player.nade_spawnloc = NULL;
                }
        }
@@ -1432,7 +1437,7 @@ MUTATOR_HOOKFUNCTION(nades, MonsterDies)
 MUTATOR_HOOKFUNCTION(nades, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        if(frag_target.nade)
                toss_nade(frag_target, true, '0 0 0', time + 0.05);
 }
@@ -1488,4 +1493,3 @@ MUTATOR_HOOKFUNCTION(nades, BuildGameplayTipsString)
 }
 
 #endif
-#endif
index 2f48d9579cba95cfb8272464fc3a8df0f54433f2..fd8d26902a4f5b6665dd2238c83c4d0f9708a26d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef NADES_ALL_H
-#define NADES_ALL_H
+#pragma once
 
 #include <common/teams.qh>
 
@@ -28,12 +27,12 @@ REGISTRY_CHECK(Nades)
 #define REGISTER_NADE(id) REGISTER(Nades, NADE_TYPE, id, m_id, NEW(Nade))
 
 CLASS(Nade, Object)
-    ATTRIB(Nade, m_id, int, 0)
-    ATTRIB(Nade, m_color, vector, '0 0 0')
-    ATTRIB(Nade, m_name, string, _("Grenade"))
-    ATTRIB(Nade, m_icon, string, "nade_normal")
-    ATTRIBARRAY(Nade, m_projectile, int, 2)
-    ATTRIBARRAY(Nade, m_trail, entity, 2)
+    ATTRIB(Nade, m_id, int, 0);
+    ATTRIB(Nade, m_color, vector, '0 0 0');
+    ATTRIB(Nade, m_name, string, _("Grenade"));
+    ATTRIB(Nade, m_icon, string, "nade_normal");
+    ATTRIBARRAY(Nade, m_projectile, int, 2);
+    ATTRIBARRAY(Nade, m_trail, entity, 2);
     METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) {
         returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon));
     }
@@ -52,7 +51,7 @@ Nade Nade_FromProjectile(int proj)
     return NADE_TYPE_Null;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 #include "effects.inc"
 #endif
 
@@ -86,7 +85,7 @@ bool orb_send(entity this, entity to, int sf);
 void nades_Clear(entity player);
 
 // Give a bonus grenade to a player
-void(entity player, float score) nades_GiveBonus;
+void nades_GiveBonus(entity player, float score);
 
 /**
  * called to adjust nade damage and force on hit
@@ -102,5 +101,3 @@ void(entity player, float score) nades_GiveBonus;
 MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage);
 
 #endif
-
-#endif
index af320d9c15555c866f30505e72dee4d0c41c237a..498d878d2d272341bd2ba9b04fd8908393a5e713 100644 (file)
@@ -1,6 +1,8 @@
-#include "nades.qh"
+#include "net.qh"
+
+#ifdef GAMEQC
 
-#ifdef IMPLEMENTATION
+#include "nades.qh"
 
 #ifdef CSQC
 .float ltime;
@@ -29,12 +31,13 @@ void orb_setup(entity e)
        e.orb_radius = e.orb_radius/model_radius*0.6;
 
        e.draw = orb_draw;
+       IL_PUSH(g_drawables, e);
        e.health = 255;
-       e.movetype = MOVETYPE_NONE;
+       set_movetype(e, MOVETYPE_NONE);
        e.solid = SOLID_NOT;
        e.drawmask = MASK_NORMAL;
        e.scale = 0.01;
-       e.avelocity = e.move_avelocity = '7 0 11';
+       e.avelocity = '7 0 11';
        e.renderflags |= RF_ADDITIVE;
 }
 #endif
diff --git a/qcsrc/common/mutators/mutator/nades/net.qh b/qcsrc/common/mutators/mutator/nades/net.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 90e9811ea6c0a3196e660f8ccb73f5d50e5c7298..67ee4f5345734f58eaaea95f827fbbef7a20ec90 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/new_toys/new_toys.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/new_toys/sv_new_toys.qc>
+#endif
index ec3b8105fe0e7b88cc61ebce4b6035f944957c78..97f88a519200c6ce1c25866b027453560a9e6d9f 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/new_toys/new_toys.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/new_toys/sv_new_toys.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/new_toys/module.inc b/qcsrc/common/mutators/mutator/new_toys/module.inc
deleted file mode 100644 (file)
index 1217177..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "new_toys.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc
deleted file mode 100644 (file)
index a52fc46..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-#ifdef IMPLEMENTATION
-/*
-
-CORE    laser   vortex     lg      rl      cry     gl      elec    hagar   fireb   hook
-                                                                       vaporizer  porto
-                                                                       tuba
-
-NEW             rifle   hlac    minel                           seeker
-IDEAS                                   OPEN    flak    OPEN            FUN FUN FUN FUN
-
-
-
-How this mutator works:
- =======================
-
-When a gun tries to spawn, this mutator is called. It will provide alternate
-weaponreplace lists.
-
-Entity:
-
-{
-"classname" "weapon_vortex"
-"new_toys" "rifle"
-}
--> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise.
-
-{
-"classname" "weapon_vortext"
-"new_toys" "vortex rifle"
-}
--> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise.
-
-{
-"classname" "weapon_vortex"
-"new_toys" "vortex"
-}
--> This is always a Vortex.
-
-If the map specifies no "new_toys" argument
-
-There will be two default replacements selectable: "replace all" and "replace random".
-In "replace all" mode, e.g. Vortex will have the default replacement "rifle".
-In "replace random" mode, Vortex will have the default replacement "vortex rifle".
-
-This mutator's replacements run BEFORE regular weaponreplace!
-
-The New Toys guns do NOT get a spawn function, so they can only ever be spawned
-when this mutator is active.
-
-Likewise, warmup, give all, give ALL and impulse 99 will not give them unless
-this mutator is active.
-
-Outside this mutator, they still can be spawned by:
-- setting their start weapon cvar to 1
-- give weaponname
-- weaponreplace
-- weaponarena (but all and most weapons arena again won't include them)
-
-This mutator performs the default replacements on the DEFAULTS of the
-start weapon selection.
-
-These weapons appear in the menu's priority list, BUT get a suffix
-"(Mutator weapon)".
-
-Picking up a "new toys" weapon will not play standard weapon pickup sound, but
-roflsound "New toys, new toys!" sound.
-
-*/
-
-bool nt_IsNewToy(int w);
-
-REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill"))
-{
-       MUTATOR_ONADD
-       {
-               if(time > 1) // game loads at time 1
-                       error("This cannot be added at runtime\n");
-
-               // mark the guns as ok to use by e.g. impulse 99
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       if(nt_IsNewToy(it.m_id))
-                               it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-               ));
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       if(nt_IsNewToy(it.m_id))
-                               it.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
-               ));
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This cannot be removed at runtime\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-.string new_toys;
-
-float autocvar_g_new_toys_autoreplace;
-bool autocvar_g_new_toys_use_pickupsound = true;
-const float NT_AUTOREPLACE_NEVER = 0;
-const float NT_AUTOREPLACE_ALWAYS = 1;
-const float NT_AUTOREPLACE_RANDOM = 2;
-
-MUTATOR_HOOKFUNCTION(nt, SetModname)
-{
-       M_ARGV(0, string) = "NewToys";
-}
-
-bool nt_IsNewToy(int w)
-{
-       switch(w)
-       {
-               case WEP_SEEKER.m_id:
-               case WEP_MINE_LAYER.m_id:
-               case WEP_HLAC.m_id:
-               case WEP_RIFLE.m_id:
-                       return true;
-               default:
-                       return false;
-       }
-}
-
-string nt_GetFullReplacement(string w)
-{
-       switch(w)
-       {
-               case "hagar": return "seeker";
-               case "devastator": return "minelayer";
-               case "machinegun": return "hlac";
-               case "vortex": return "rifle";
-               //case "shotgun": return "shockwave";
-               default: return string_null;
-       }
-}
-
-string nt_GetReplacement(string w, float m)
-{
-       if(m == NT_AUTOREPLACE_NEVER)
-               return w;
-       string s = nt_GetFullReplacement(w);
-       if (!s)
-               return w;
-       if(m == NT_AUTOREPLACE_RANDOM)
-               s = strcat(w, " ", s);
-       return s;
-}
-
-MUTATOR_HOOKFUNCTION(nt, SetStartItems)
-{
-       // rearrange start_weapon_default
-       // apply those bits that are set by start_weapon_defaultmask
-       // same for warmup
-
-       float j, n;
-
-       WepSet newdefault;
-       WepSet warmup_newdefault;
-
-       newdefault = '0 0 0';
-       warmup_newdefault = '0 0 0';
-
-       WepSet seti = '0 0 0';
-
-       FOREACH(Weapons, it != WEP_Null, LAMBDA(
-               seti = it.m_wepset;
-               n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace));
-
-               for(j = 0; j < n; ++j)
-                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                               if(it.netname == argv(j))
-                               {
-                                       WepSet setk = it.m_wepset;
-                                       if(start_weapons & seti) newdefault |= setk;
-                                       if(warmup_start_weapons & seti) warmup_newdefault |= setk;
-                               }
-                       ));
-       ));
-
-       newdefault &= start_weapons_defaultmask;
-       start_weapons &= ~start_weapons_defaultmask;
-       start_weapons |= newdefault;
-
-       warmup_newdefault &= warmup_start_weapons_defaultmask;
-       warmup_start_weapons &= ~warmup_start_weapons_defaultmask;
-       warmup_start_weapons |= warmup_newdefault;
-}
-
-MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace)
-{
-       entity wep = M_ARGV(0, entity);
-       entity wepinfo = M_ARGV(1, entity);
-       string ret_string = M_ARGV(2, string);
-
-       // otherwise, we do replace
-       if(wep.new_toys)
-       {
-               // map defined replacement:
-               ret_string = wep.new_toys;
-       }
-       else
-       {
-               // auto replacement:
-               ret_string = nt_GetReplacement(wepinfo.netname, autocvar_g_new_toys_autoreplace);
-       }
-
-       // apply regular weaponreplace
-       ret_string = W_Apply_Weaponreplace(ret_string);
-
-       M_ARGV(2, string) = ret_string;
-}
-
-MUTATOR_HOOKFUNCTION(nt, FilterItem)
-{
-       entity item = M_ARGV(0, entity);
-
-       if(nt_IsNewToy(item.weapon) && autocvar_g_new_toys_use_pickupsound) {
-               item.item_pickupsound = string_null;
-               item.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS;
-       }
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc
new file mode 100644 (file)
index 0000000..6c0647b
--- /dev/null
@@ -0,0 +1,229 @@
+#include "sv_new_toys.qh"
+
+/*
+
+CORE    laser   vortex     lg      rl      cry     gl      elec    hagar   fireb   hook
+                                                                       vaporizer  porto
+                                                                       tuba
+
+NEW             rifle   hlac    minel                           seeker
+IDEAS                                   OPEN    flak    OPEN            FUN FUN FUN FUN
+
+
+
+How this mutator works:
+ =======================
+
+When a gun tries to spawn, this mutator is called. It will provide alternate
+weaponreplace lists.
+
+Entity:
+
+{
+"classname" "weapon_vortex"
+"new_toys" "rifle"
+}
+-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise.
+
+{
+"classname" "weapon_vortext"
+"new_toys" "vortex rifle"
+}
+-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise.
+
+{
+"classname" "weapon_vortex"
+"new_toys" "vortex"
+}
+-> This is always a Vortex.
+
+If the map specifies no "new_toys" argument
+
+There will be two default replacements selectable: "replace all" and "replace random".
+In "replace all" mode, e.g. Vortex will have the default replacement "rifle".
+In "replace random" mode, Vortex will have the default replacement "vortex rifle".
+
+This mutator's replacements run BEFORE regular weaponreplace!
+
+The New Toys guns do NOT get a spawn function, so they can only ever be spawned
+when this mutator is active.
+
+Likewise, warmup, give all, give ALL and impulse 99 will not give them unless
+this mutator is active.
+
+Outside this mutator, they still can be spawned by:
+- setting their start weapon cvar to 1
+- give weaponname
+- weaponreplace
+- weaponarena (but all and most weapons arena again won't include them)
+
+This mutator performs the default replacements on the DEFAULTS of the
+start weapon selection.
+
+These weapons appear in the menu's priority list, BUT get a suffix
+"(Mutator weapon)".
+
+Picking up a "new toys" weapon will not play standard weapon pickup sound, but
+roflsound "New toys, new toys!" sound.
+
+*/
+
+bool nt_IsNewToy(int w);
+
+REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill"))
+{
+       MUTATOR_ONADD
+       {
+               if(time > 1) // game loads at time 1
+                       error("This cannot be added at runtime\n");
+
+               // mark the guns as ok to use by e.g. impulse 99
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(nt_IsNewToy(it.m_id))
+                               it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+               ));
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(nt_IsNewToy(it.m_id))
+                               it.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+               ));
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This cannot be removed at runtime\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+.string new_toys;
+
+float autocvar_g_new_toys_autoreplace;
+bool autocvar_g_new_toys_use_pickupsound = true;
+const float NT_AUTOREPLACE_NEVER = 0;
+const float NT_AUTOREPLACE_ALWAYS = 1;
+const float NT_AUTOREPLACE_RANDOM = 2;
+
+MUTATOR_HOOKFUNCTION(nt, SetModname)
+{
+       M_ARGV(0, string) = "NewToys";
+}
+
+bool nt_IsNewToy(int w)
+{
+       switch(w)
+       {
+               case WEP_SEEKER.m_id:
+               case WEP_MINE_LAYER.m_id:
+               case WEP_HLAC.m_id:
+               case WEP_RIFLE.m_id:
+               case WEP_SHOCKWAVE.m_id:
+                       return true;
+               default:
+                       return false;
+       }
+}
+
+string nt_GetFullReplacement(string w)
+{
+       switch(w)
+       {
+               case "hagar": return "seeker";
+               case "devastator": return "minelayer";
+               case "machinegun": return "hlac";
+               case "vortex": return "rifle";
+               //case "shotgun": return "shockwave";
+               default: return string_null;
+       }
+}
+
+string nt_GetReplacement(string w, float m)
+{
+       if(m == NT_AUTOREPLACE_NEVER)
+               return w;
+       string s = nt_GetFullReplacement(w);
+       if (!s)
+               return w;
+       if(m == NT_AUTOREPLACE_RANDOM)
+               s = strcat(w, " ", s);
+       return s;
+}
+
+MUTATOR_HOOKFUNCTION(nt, SetStartItems)
+{
+       // rearrange start_weapon_default
+       // apply those bits that are set by start_weapon_defaultmask
+       // same for warmup
+
+       float j, n;
+
+       WepSet newdefault;
+       WepSet warmup_newdefault;
+
+       newdefault = '0 0 0';
+       warmup_newdefault = '0 0 0';
+
+       WepSet seti = '0 0 0';
+
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               seti = it.m_wepset;
+               n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace));
+
+               for(j = 0; j < n; ++j)
+                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                               if(it.netname == argv(j))
+                               {
+                                       WepSet setk = it.m_wepset;
+                                       if(start_weapons & seti) newdefault |= setk;
+                                       if(warmup_start_weapons & seti) warmup_newdefault |= setk;
+                               }
+                       ));
+       ));
+
+       newdefault &= start_weapons_defaultmask;
+       start_weapons &= ~start_weapons_defaultmask;
+       start_weapons |= newdefault;
+
+       warmup_newdefault &= warmup_start_weapons_defaultmask;
+       warmup_start_weapons &= ~warmup_start_weapons_defaultmask;
+       warmup_start_weapons |= warmup_newdefault;
+}
+
+MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace)
+{
+       entity wep = M_ARGV(0, entity);
+       entity wepinfo = M_ARGV(1, entity);
+       string ret_string = M_ARGV(2, string);
+
+       // otherwise, we do replace
+       if(wep.new_toys)
+       {
+               // map defined replacement:
+               ret_string = wep.new_toys;
+       }
+       else
+       {
+               // auto replacement:
+               ret_string = nt_GetReplacement(wepinfo.netname, autocvar_g_new_toys_autoreplace);
+       }
+
+       // apply regular weaponreplace
+       ret_string = W_Apply_Weaponreplace(ret_string);
+
+       M_ARGV(2, string) = ret_string;
+}
+
+MUTATOR_HOOKFUNCTION(nt, FilterItem)
+{
+       entity item = M_ARGV(0, entity);
+
+       if(nt_IsNewToy(item.weapon) && autocvar_g_new_toys_use_pickupsound) {
+               item.item_pickupsound = string_null;
+               item.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS;
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index a669175da2ebb221b895e42cf63407202f1dccb3..3c3141d6fb1455d2fcaecd6a86fb911ef8fe1794 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/nix/nix.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/nix/sv_nix.qc>
+#endif
index 6c012fe65c5b09c7c92f63291faa920367c429eb..affbae20a90d35e726d0bd07f01ce9cf1441e930 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/nix/nix.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/nix/sv_nix.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/nix/module.inc b/qcsrc/common/mutators/mutator/nix/module.inc
deleted file mode 100644 (file)
index fb4f9ec..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "nix.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/nix/nix.qc b/qcsrc/common/mutators/mutator/nix/nix.qc
deleted file mode 100644 (file)
index f52d75a..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-#ifdef IMPLEMENTATION
-int autocvar_g_balance_nix_ammo_cells;
-int autocvar_g_balance_nix_ammo_plasma;
-int autocvar_g_balance_nix_ammo_fuel;
-int autocvar_g_balance_nix_ammo_nails;
-int autocvar_g_balance_nix_ammo_rockets;
-int autocvar_g_balance_nix_ammo_shells;
-int autocvar_g_balance_nix_ammoincr_cells;
-int autocvar_g_balance_nix_ammoincr_plasma;
-int autocvar_g_balance_nix_ammoincr_fuel;
-int autocvar_g_balance_nix_ammoincr_nails;
-int autocvar_g_balance_nix_ammoincr_rockets;
-int autocvar_g_balance_nix_ammoincr_shells;
-float autocvar_g_balance_nix_incrtime;
-float autocvar_g_balance_nix_roundtime;
-bool autocvar_g_nix_with_healtharmor;
-bool autocvar_g_nix_with_blaster;
-bool autocvar_g_nix_with_powerups;
-int autocvar_g_pickup_cells_max;
-int autocvar_g_pickup_plasma_max;
-int autocvar_g_pickup_fuel_max;
-int autocvar_g_pickup_nails_max;
-int autocvar_g_pickup_rockets_max;
-int autocvar_g_pickup_shells_max;
-
-float g_nix_with_blaster;
-// WEAPONTODO
-int nix_weapon;
-float nix_nextchange;
-float nix_nextweapon;
-.float nix_lastchange_id;
-.float nix_lastinfotime;
-.float nix_nextincr;
-
-bool NIX_CanChooseWeapon(int wpn);
-
-REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"))
-{
-       MUTATOR_ONADD
-       {
-               g_nix_with_blaster = autocvar_g_nix_with_blaster;
-
-               nix_nextchange = 0;
-               nix_nextweapon = 0;
-
-               FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it)));
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // nothing to roll back
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
-               FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
-                       it.ammo_cells = start_ammo_cells;
-                       it.ammo_plasma = start_ammo_plasma;
-                       it.ammo_shells = start_ammo_shells;
-                       it.ammo_nails = start_ammo_nails;
-                       it.ammo_rockets = start_ammo_rockets;
-                       it.ammo_fuel = start_ammo_fuel;
-                       it.weapons = start_weapons;
-                       if(!client_hasweapon(it, PS(it).m_weapon, true, false))
-                               PS(it).m_switchweapon = w_getbestweapon(it);
-               });
-       }
-
-       return false;
-}
-
-bool NIX_CanChooseWeapon(int wpn)
-{
-       entity e = Weapons_from(wpn);
-       if (e == WEP_Null) return false; // skip dummies
-       if(g_weaponarena)
-       {
-               if(!(g_weaponarena_weapons & e.m_wepset))
-                       return false;
-       }
-       else
-       {
-               if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster)
-                       return false;
-               if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
-                       return false;
-               if (!(e.spawnflags & WEP_FLAG_NORMAL))
-                       return false;
-       }
-       return true;
-}
-void NIX_ChooseNextWeapon()
-{
-       RandomSelection_Init();
-       FOREACH(Weapons, it != WEP_Null, LAMBDA(
-               if(NIX_CanChooseWeapon(it.m_id))
-                       RandomSelection_Add(NULL, it.m_id, string_null, 1, (it.m_id != nix_weapon));
-       ));
-       nix_nextweapon = RandomSelection_chosen_float;
-}
-
-void NIX_GiveCurrentWeapon(entity this)
-{
-       float dt;
-
-       if(!nix_nextweapon)
-               NIX_ChooseNextWeapon();
-
-       dt = ceil(nix_nextchange - time);
-
-       if(dt <= 0)
-       {
-               nix_weapon = nix_nextweapon;
-               nix_nextweapon = 0;
-               if (!nix_nextchange) // no round played yet?
-                       nix_nextchange = time; // start the first round now!
-               else
-                       nix_nextchange = time + autocvar_g_balance_nix_roundtime;
-               // Weapon w = Weapons_from(nix_weapon);
-               // w.wr_init(w); // forget it, too slow
-       }
-
-       // get weapon info
-       entity e = Weapons_from(nix_weapon);
-
-       if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round!
-       {
-               this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0;
-
-               if(this.items & IT_UNLIMITED_WEAPON_AMMO)
-               {
-                       switch(e.ammo_field)
-                       {
-                               case ammo_shells:  this.ammo_shells  = autocvar_g_pickup_shells_max;  break;
-                               case ammo_nails:   this.ammo_nails   = autocvar_g_pickup_nails_max;   break;
-                               case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break;
-                               case ammo_cells:   this.ammo_cells   = autocvar_g_pickup_cells_max;   break;
-                               case ammo_plasma:  this.ammo_plasma  = autocvar_g_pickup_plasma_max;   break;
-                               case ammo_fuel:    this.ammo_fuel    = autocvar_g_pickup_fuel_max;    break;
-                       }
-               }
-               else
-               {
-                       switch(e.ammo_field)
-                       {
-                               case ammo_shells:  this.ammo_shells  = autocvar_g_balance_nix_ammo_shells;  break;
-                               case ammo_nails:   this.ammo_nails   = autocvar_g_balance_nix_ammo_nails;   break;
-                               case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break;
-                               case ammo_cells:   this.ammo_cells   = autocvar_g_balance_nix_ammo_cells;   break;
-                               case ammo_plasma:  this.ammo_plasma  = autocvar_g_balance_nix_ammo_plasma;   break;
-                               case ammo_fuel:    this.ammo_fuel    = autocvar_g_balance_nix_ammo_fuel;    break;
-                       }
-               }
-
-               this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
-               if(dt >= 1 && dt <= 5)
-                       this.nix_lastinfotime = -42;
-               else
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon);
-
-               e.wr_resetplayer(e, this);
-
-               // all weapons must be fully loaded when we spawn
-               if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
-                       this.(weapon_load[nix_weapon]) = e.reloading_ammo;
-
-               // vortex too
-               if(WEP_CVAR(vortex, charge))
-               {
-                       if(WEP_CVAR_SEC(vortex, chargepool))
-                               this.vortex_chargepool_ammo = 1;
-                       this.vortex_charge = WEP_CVAR(vortex, charge_start);
-               }
-
-               // set last change info
-               this.nix_lastchange_id = nix_nextchange;
-       }
-       if(this.nix_lastinfotime != dt)
-       {
-               this.nix_lastinfotime = dt; // initial value 0 should count as "not seen"
-               if(dt >= 1 && dt <= 5)
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt);
-       }
-
-       if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr)
-       {
-               switch(e.ammo_field)
-               {
-                       case ammo_shells:  this.ammo_shells  += autocvar_g_balance_nix_ammoincr_shells;  break;
-                       case ammo_nails:   this.ammo_nails   += autocvar_g_balance_nix_ammoincr_nails;   break;
-                       case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break;
-                       case ammo_cells:   this.ammo_cells   += autocvar_g_balance_nix_ammoincr_cells;   break;
-                       case ammo_plasma:  this.ammo_plasma  += autocvar_g_balance_nix_ammoincr_plasma;   break;
-                       case ammo_fuel:    this.ammo_fuel    += autocvar_g_balance_nix_ammoincr_fuel;    break;
-               }
-
-               this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
-       }
-
-       this.weapons = '0 0 0';
-       if(g_nix_with_blaster)
-               this.weapons |= WEPSET(BLASTER);
-       this.weapons |= e.m_wepset;
-
-    Weapon w = Weapons_from(nix_weapon);
-       if(PS(this).m_switchweapon != w)
-               if(!client_hasweapon(this, PS(this).m_switchweapon, true, false))
-               {
-                       if(client_hasweapon(this, w, true, false))
-                               W_SwitchWeapon(this, w);
-               }
-}
-
-MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon)
-{
-       return true; // no throwing in NIX
-}
-
-MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX");
-}
-
-MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX");
-}
-
-MUTATOR_HOOKFUNCTION(nix, FilterItem)
-{
-       entity item = M_ARGV(0, entity);
-
-       switch (item.items)
-       {
-               case ITEM_HealthSmall.m_itemid:
-               case ITEM_HealthMedium.m_itemid:
-               case ITEM_HealthLarge.m_itemid:
-               case ITEM_HealthMega.m_itemid:
-               case ITEM_ArmorSmall.m_itemid:
-               case ITEM_ArmorMedium.m_itemid:
-               case ITEM_ArmorLarge.m_itemid:
-               case ITEM_ArmorMega.m_itemid:
-                       if (autocvar_g_nix_with_healtharmor)
-                               return false;
-                       break;
-               case ITEM_Strength.m_itemid:
-               case ITEM_Shield.m_itemid:
-                       if (autocvar_g_nix_with_powerups)
-                               return false;
-                       break;
-       }
-
-       return true; // delete all other items
-}
-
-MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn)
-{
-       entity ent = M_ARGV(0, entity);
-
-       if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo)
-               return true;
-}
-
-MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(!intermission_running)
-       if(!IS_DEAD(player))
-       if(IS_PLAYER(player))
-               NIX_GiveCurrentWeapon(player);
-}
-
-MUTATOR_HOOKFUNCTION(nix, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.nix_lastchange_id = -1;
-       NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning
-       player.items |= IT_UNLIMITED_SUPERWEAPONS;
-}
-
-MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST)
-{
-       M_ARGV(0, string) = "NIX";
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc
new file mode 100644 (file)
index 0000000..76e735e
--- /dev/null
@@ -0,0 +1,277 @@
+#include "sv_nix.qh"
+
+int autocvar_g_balance_nix_ammo_cells;
+int autocvar_g_balance_nix_ammo_plasma;
+int autocvar_g_balance_nix_ammo_fuel;
+int autocvar_g_balance_nix_ammo_nails;
+int autocvar_g_balance_nix_ammo_rockets;
+int autocvar_g_balance_nix_ammo_shells;
+int autocvar_g_balance_nix_ammoincr_cells;
+int autocvar_g_balance_nix_ammoincr_plasma;
+int autocvar_g_balance_nix_ammoincr_fuel;
+int autocvar_g_balance_nix_ammoincr_nails;
+int autocvar_g_balance_nix_ammoincr_rockets;
+int autocvar_g_balance_nix_ammoincr_shells;
+float autocvar_g_balance_nix_incrtime;
+float autocvar_g_balance_nix_roundtime;
+bool autocvar_g_nix_with_healtharmor;
+bool autocvar_g_nix_with_blaster;
+bool autocvar_g_nix_with_powerups;
+int autocvar_g_pickup_cells_max;
+int autocvar_g_pickup_plasma_max;
+int autocvar_g_pickup_fuel_max;
+int autocvar_g_pickup_nails_max;
+int autocvar_g_pickup_rockets_max;
+int autocvar_g_pickup_shells_max;
+
+float g_nix_with_blaster;
+// WEAPONTODO
+int nix_weapon;
+float nix_nextchange;
+float nix_nextweapon;
+.float nix_lastchange_id;
+.float nix_lastinfotime;
+.float nix_nextincr;
+
+bool NIX_CanChooseWeapon(int wpn);
+
+REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"))
+{
+       MUTATOR_ONADD
+       {
+               g_nix_with_blaster = autocvar_g_nix_with_blaster;
+
+               nix_nextchange = 0;
+               nix_nextweapon = 0;
+
+               FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it)));
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // nothing to roll back
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
+               FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
+                       it.ammo_cells = start_ammo_cells;
+                       it.ammo_plasma = start_ammo_plasma;
+                       it.ammo_shells = start_ammo_shells;
+                       it.ammo_nails = start_ammo_nails;
+                       it.ammo_rockets = start_ammo_rockets;
+                       it.ammo_fuel = start_ammo_fuel;
+                       it.weapons = start_weapons;
+                       if(!client_hasweapon(it, PS(it).m_weapon, true, false))
+                               PS(it).m_switchweapon = w_getbestweapon(it);
+               });
+       }
+
+       return false;
+}
+
+bool NIX_CanChooseWeapon(int wpn)
+{
+       entity e = Weapons_from(wpn);
+       if (e == WEP_Null) return false; // skip dummies
+       if(g_weaponarena)
+       {
+               if(!(g_weaponarena_weapons & e.m_wepset))
+                       return false;
+       }
+       else
+       {
+               if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster)
+                       return false;
+               if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
+                       return false;
+               if (!(e.spawnflags & WEP_FLAG_NORMAL))
+                       return false;
+       }
+       return true;
+}
+void NIX_ChooseNextWeapon()
+{
+       RandomSelection_Init();
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               if(NIX_CanChooseWeapon(it.m_id))
+                       RandomSelection_AddFloat(it.m_id, 1, (it.m_id != nix_weapon));
+       ));
+       nix_nextweapon = RandomSelection_chosen_float;
+}
+
+void NIX_GiveCurrentWeapon(entity this)
+{
+       float dt;
+
+       if(!nix_nextweapon)
+               NIX_ChooseNextWeapon();
+
+       dt = ceil(nix_nextchange - time);
+
+       if(dt <= 0)
+       {
+               nix_weapon = nix_nextweapon;
+               nix_nextweapon = 0;
+               if (!nix_nextchange) // no round played yet?
+                       nix_nextchange = time; // start the first round now!
+               else
+                       nix_nextchange = time + autocvar_g_balance_nix_roundtime;
+               // Weapon w = Weapons_from(nix_weapon);
+               // w.wr_init(w); // forget it, too slow
+       }
+
+       // get weapon info
+       entity e = Weapons_from(nix_weapon);
+
+       if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round!
+       {
+               this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0;
+
+               if(this.items & IT_UNLIMITED_WEAPON_AMMO)
+               {
+                       switch(e.ammo_field)
+                       {
+                               case ammo_shells:  this.ammo_shells  = autocvar_g_pickup_shells_max;  break;
+                               case ammo_nails:   this.ammo_nails   = autocvar_g_pickup_nails_max;   break;
+                               case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break;
+                               case ammo_cells:   this.ammo_cells   = autocvar_g_pickup_cells_max;   break;
+                               case ammo_plasma:  this.ammo_plasma  = autocvar_g_pickup_plasma_max;   break;
+                               case ammo_fuel:    this.ammo_fuel    = autocvar_g_pickup_fuel_max;    break;
+                       }
+               }
+               else
+               {
+                       switch(e.ammo_field)
+                       {
+                               case ammo_shells:  this.ammo_shells  = autocvar_g_balance_nix_ammo_shells;  break;
+                               case ammo_nails:   this.ammo_nails   = autocvar_g_balance_nix_ammo_nails;   break;
+                               case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break;
+                               case ammo_cells:   this.ammo_cells   = autocvar_g_balance_nix_ammo_cells;   break;
+                               case ammo_plasma:  this.ammo_plasma  = autocvar_g_balance_nix_ammo_plasma;   break;
+                               case ammo_fuel:    this.ammo_fuel    = autocvar_g_balance_nix_ammo_fuel;    break;
+                       }
+               }
+
+               this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
+               if(dt >= 1 && dt <= 5)
+                       this.nix_lastinfotime = -42;
+               else
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon);
+
+               e.wr_resetplayer(e, this);
+
+               // all weapons must be fully loaded when we spawn
+               if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
+                       this.(weapon_load[nix_weapon]) = e.reloading_ammo;
+
+               // vortex too
+               if(WEP_CVAR(vortex, charge))
+               {
+                       if(WEP_CVAR_SEC(vortex, chargepool))
+                               this.vortex_chargepool_ammo = 1;
+                       this.vortex_charge = WEP_CVAR(vortex, charge_start);
+               }
+
+               // set last change info
+               this.nix_lastchange_id = nix_nextchange;
+       }
+       if(this.nix_lastinfotime != dt)
+       {
+               this.nix_lastinfotime = dt; // initial value 0 should count as "not seen"
+               if(dt >= 1 && dt <= 5)
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt);
+       }
+
+       if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr)
+       {
+               switch(e.ammo_field)
+               {
+                       case ammo_shells:  this.ammo_shells  += autocvar_g_balance_nix_ammoincr_shells;  break;
+                       case ammo_nails:   this.ammo_nails   += autocvar_g_balance_nix_ammoincr_nails;   break;
+                       case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break;
+                       case ammo_cells:   this.ammo_cells   += autocvar_g_balance_nix_ammoincr_cells;   break;
+                       case ammo_plasma:  this.ammo_plasma  += autocvar_g_balance_nix_ammoincr_plasma;   break;
+                       case ammo_fuel:    this.ammo_fuel    += autocvar_g_balance_nix_ammoincr_fuel;    break;
+               }
+
+               this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
+       }
+
+       this.weapons = '0 0 0';
+       if(g_nix_with_blaster)
+               this.weapons |= WEPSET(BLASTER);
+       this.weapons |= e.m_wepset;
+
+    Weapon w = Weapons_from(nix_weapon);
+       if(PS(this).m_switchweapon != w)
+               if(!client_hasweapon(this, PS(this).m_switchweapon, true, false))
+               {
+                       if(client_hasweapon(this, w, true, false))
+                               W_SwitchWeapon(this, w);
+               }
+}
+
+MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon)
+{
+       return true; // no throwing in NIX
+}
+
+MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX");
+}
+
+MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX");
+}
+
+MUTATOR_HOOKFUNCTION(nix, FilterItem)
+{
+       entity item = M_ARGV(0, entity);
+
+       if(item.itemdef.instanceOfHealth || item.itemdef.instanceOfArmor)
+       {
+               return !autocvar_g_nix_with_healtharmor;
+       }
+       else if(item.itemdef.instanceOfPowerup)
+       {
+               return !autocvar_g_nix_with_powerups;
+       }
+
+       return true; // delete all other items
+}
+
+MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn)
+{
+       entity ent = M_ARGV(0, entity);
+
+       if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo)
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(!gameover)
+       if(!IS_DEAD(player))
+       if(IS_PLAYER(player))
+               NIX_GiveCurrentWeapon(player);
+}
+
+MUTATOR_HOOKFUNCTION(nix, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.nix_lastchange_id = -1;
+       NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning
+       player.items |= IT_UNLIMITED_SUPERWEAPONS;
+}
+
+MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST)
+{
+       M_ARGV(0, string) = "NIX";
+}
diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qh b/qcsrc/common/mutators/mutator/nix/sv_nix.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 5b42a4dd11fec6420d530a25a3096edb5b334f5e..0552173c1a0874c8c8ca6765ac0a922ff60ff01b 100644 (file)
@@ -1,4 +1,10 @@
 // generated file; do not modify
 #include <common/mutators/mutator/overkill/hmg.qc>
 #include <common/mutators/mutator/overkill/overkill.qc>
+#ifdef CSQC
+    #include <common/mutators/mutator/overkill/cl_overkill.qc>
+#endif
+#ifdef SVQC
+    #include <common/mutators/mutator/overkill/sv_overkill.qc>
+#endif
 #include <common/mutators/mutator/overkill/rpc.qc>
index 7a46694446e351814ae7ca31eee7091146cc2901..13e42431b32ebb3a28c11504a38fb822e69d7640 100644 (file)
@@ -1,4 +1,10 @@
 // generated file; do not modify
 #include <common/mutators/mutator/overkill/hmg.qh>
 #include <common/mutators/mutator/overkill/overkill.qh>
+#ifdef CSQC
+    #include <common/mutators/mutator/overkill/cl_overkill.qh>
+#endif
+#ifdef SVQC
+    #include <common/mutators/mutator/overkill/sv_overkill.qh>
+#endif
 #include <common/mutators/mutator/overkill/rpc.qh>
diff --git a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc
new file mode 100644 (file)
index 0000000..eb21953
--- /dev/null
@@ -0,0 +1,11 @@
+#include "cl_overkill.qh"
+
+REGISTER_MUTATOR(ok, false)
+{
+       MUTATOR_ONADD {
+               cvar_settemp("g_overkill", "1");
+               WEP_SHOTGUN.mdl = "ok_shotgun";
+               WEP_MACHINEGUN.mdl = "ok_mg";
+               WEP_VORTEX.mdl = "ok_sniper";
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index e0ae539318c3db8dd949079bf789dda1521a9e43..024fdb1cc768397f28409097e9f2e5166fa57b0a 100644 (file)
@@ -1,47 +1,5 @@
-#ifndef IMPLEMENTATION
-CLASS(HeavyMachineGun, Weapon)
-/* ammotype  */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails)
-/* impulse   */ ATTRIB(HeavyMachineGun, impulse, int, 3)
-/* flags     */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
-/* rating    */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
-/* color     */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0');
-/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg");
-#ifndef MENUQC
-/* model     */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM);
-#endif
-/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi");
-/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6);
-/* wepimg    */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg");
-/* refname   */ ATTRIB(HeavyMachineGun, netname, string, "hmg");
-/* wepname   */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun"));
-
-#define X(BEGIN, P, END, class, prefix) \
-       BEGIN(class) \
-               P(class, prefix, ammo, float, NONE) \
-               P(class, prefix, damage, float, NONE) \
-               P(class, prefix, force, float, NONE) \
-               P(class, prefix, refire, float, NONE) \
-               P(class, prefix, reload_ammo, float, NONE) \
-               P(class, prefix, reload_time, float, NONE) \
-               P(class, prefix, solidpenetration, float, NONE) \
-               P(class, prefix, spread_add, float, NONE) \
-               P(class, prefix, spread_max, float, NONE) \
-               P(class, prefix, spread_min, float, NONE) \
-               P(class, prefix, switchdelay_drop, float, NONE) \
-               P(class, prefix, switchdelay_raise, float, NONE) \
-               P(class, prefix, weaponreplace, string, NONE) \
-               P(class, prefix, weaponstartoverride, float, NONE) \
-               P(class, prefix, weaponstart, float, NONE) \
-               P(class, prefix, weaponthrowable, float, NONE) \
-    END()
-    W_PROPS(X, HeavyMachineGun, hmg)
-#undef X
-
-ENDCLASS(HeavyMachineGun)
-REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun));
+#include "hmg.qh"
 
-#endif
-#ifdef IMPLEMENTATION
 #ifdef SVQC
 
 REGISTER_MUTATOR(hmg_nadesupport, true);
@@ -62,8 +20,7 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone
                return;
        }
 
-       if(!thiswep.wr_checkammo1(thiswep, actor))
-       if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+       if((!thiswep.wr_checkammo1(thiswep, actor) && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (!(actor.items & IT_SUPERWEAPON) && !(actor.items & IT_UNLIMITED_SUPERWEAPONS)))
        {
                W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
                w_ready(thiswep, actor, weaponentity, fire);
@@ -72,7 +29,7 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone
 
        W_DecreaseAmmo(WEP_HMG, actor, WEP_CVAR(hmg, ammo));
 
-       W_SetupShot (actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(hmg, damage));
+       W_SetupShot (actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(hmg, damage));
 
        if(!autocvar_g_norecoil)
        {
@@ -87,11 +44,14 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone
 
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
-       W_MachineGun_MuzzleFlash(actor);
-       W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
+       W_MachineGun_MuzzleFlash(actor, weaponentity);
+       W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
 
        if (autocvar_g_casings >= 2) // casing code
-               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+       {
+               makevectors(actor.v_angle); // for some reason, this is lost
+               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+       }
 
        int slot = weaponslot(weaponentity);
        ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(actor);
@@ -143,7 +103,7 @@ METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep, entity actor))
 
 METHOD(HeavyMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, WEP_CVAR(hmg, ammo), SND_RELOAD);
+    W_Reload(actor, weaponentity, WEP_CVAR(hmg, ammo), SND_RELOAD);
 }
 
 METHOD(HeavyMachineGun, wr_suicidemessage, Notification(entity thiswep))
@@ -172,4 +132,3 @@ METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep, entity actor))
 }
 
 #endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qh b/qcsrc/common/mutators/mutator/overkill/hmg.qh
new file mode 100644 (file)
index 0000000..7219fd1
--- /dev/null
@@ -0,0 +1,42 @@
+#pragma once
+
+CLASS(HeavyMachineGun, Weapon)
+/* ammotype  */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails);
+/* impulse   */ ATTRIB(HeavyMachineGun, impulse, int, 3);
+/* flags     */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
+/* rating    */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
+/* color     */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0');
+/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg");
+#ifdef GAMEQC
+/* model     */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM);
+#endif
+/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi");
+/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6);
+/* wepimg    */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg");
+/* refname   */ ATTRIB(HeavyMachineGun, netname, string, "hmg");
+/* wepname   */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun"));
+
+#define X(BEGIN, P, END, class, prefix) \
+       BEGIN(class) \
+               P(class, prefix, ammo, float, NONE) \
+               P(class, prefix, damage, float, NONE) \
+               P(class, prefix, force, float, NONE) \
+               P(class, prefix, refire, float, NONE) \
+               P(class, prefix, reload_ammo, float, NONE) \
+               P(class, prefix, reload_time, float, NONE) \
+               P(class, prefix, solidpenetration, float, NONE) \
+               P(class, prefix, spread_add, float, NONE) \
+               P(class, prefix, spread_max, float, NONE) \
+               P(class, prefix, spread_min, float, NONE) \
+               P(class, prefix, switchdelay_drop, float, NONE) \
+               P(class, prefix, switchdelay_raise, float, NONE) \
+               P(class, prefix, weaponreplace, string, NONE) \
+               P(class, prefix, weaponstartoverride, float, NONE) \
+               P(class, prefix, weaponstart, float, NONE) \
+               P(class, prefix, weaponthrowable, float, NONE) \
+    END()
+    W_PROPS(X, HeavyMachineGun, hmg)
+#undef X
+
+ENDCLASS(HeavyMachineGun)
+REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun));
diff --git a/qcsrc/common/mutators/mutator/overkill/module.inc b/qcsrc/common/mutators/mutator/overkill/module.inc
deleted file mode 100644 (file)
index a7acff5..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "hmg.qc"
-#include "rpc.qc"
-
-#ifdef SVQC
-       #include "overkill.qc"
-#endif
-#ifdef CSQC
-       #ifdef IMPLEMENTATION
-               REGISTER_MUTATOR(ok, false)
-               {
-                       MUTATOR_ONADD {
-                               cvar_settemp("g_overkill", "1");
-                               WEP_SHOTGUN.mdl = "ok_shotgun";
-                               WEP_MACHINEGUN.mdl = "ok_mg";
-                               WEP_VORTEX.mdl = "ok_sniper";
-                       }
-               }
-       #endif
-#endif
index 2fb75b3f8d9696b8ce644748f5f837d0e19a89f5..3cb64ce9234761703a1876d0310c873fa2bbe5e8 100644 (file)
@@ -1,409 +1 @@
-#ifdef IMPLEMENTATION
-bool autocvar_g_overkill_powerups_replace;
-float autocvar_g_overkill_superguns_respawn_time;
-bool autocvar_g_overkill_100h_anyway;
-bool autocvar_g_overkill_100a_anyway;
-bool autocvar_g_overkill_ammo_charge;
-float autocvar_g_overkill_ammo_charge_notice;
-float autocvar_g_overkill_ammo_charge_limit;
-
-.vector ok_deathloc;
-.float ok_spawnsys_timer;
-.float ok_lastwep;
-.float ok_item;
-
-.float ok_notice_time;
-.float ammo_charge[Weapons_MAX];
-.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE);
-.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL);
-
-.float ok_pauseregen_finished;
-
-void(entity ent, float wep) ok_DecreaseCharge;
-
-void ok_Initialize();
-
-REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
-{
-       MUTATOR_ONADD
-       {
-               ok_Initialize();
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
-               WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo)
-{
-       entity actor = M_ARGV(0, entity);
-       if (actor.ok_use_ammocharge)
-       {
-               ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id);
-               return true;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(ok, W_Reload)
-{
-       entity actor = M_ARGV(0, entity);
-       return actor.ok_use_ammocharge;
-}
-
-void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float);
-spawnfunc(weapon_hmg);
-spawnfunc(weapon_rpc);
-
-void ok_DecreaseCharge(entity ent, int wep)
-{
-       if(!ent.ok_use_ammocharge) return;
-
-       entity wepent = Weapons_from(wep);
-
-       if (wepent == WEP_Null) return;  // dummy
-
-       ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
-}
-
-void ok_IncreaseCharge(entity ent, int wep)
-{
-       entity wepent = Weapons_from(wep);
-
-       if (wepent == WEP_Null) return;  // dummy
-
-       if(ent.ok_use_ammocharge)
-       if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking?
-               ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
-}
-
-float ok_CheckWeaponCharge(entity ent, int wep)
-{
-       if(!ent.ok_use_ammocharge) return true;
-
-       entity wepent = Weapons_from(wep);
-
-       if(wepent == WEP_Null) return false;  // dummy
-
-       return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-       float frag_deathtype = M_ARGV(3, float);
-
-       if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
-       if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
-       {
-               if(frag_attacker != frag_target)
-               if(frag_target.health > 0)
-               if(STAT(FROZEN, frag_target) == 0)
-               if(!IS_DEAD(frag_target))
-               {
-                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
-                       M_ARGV(6, vector) = '0 0 0';
-               }
-
-               M_ARGV(4, float) = 0;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor)
-{
-       entity frag_target = M_ARGV(2, entity);
-       float damage_take = M_ARGV(4, float);
-
-       if(damage_take)
-               frag_target.ok_pauseregen_finished = max(frag_target.ok_pauseregen_finished, time + 2);
-}
-
-void ok_DropItem(entity this, entity targ)
-{
-       entity e = new(droppedweapon); // hax
-       e.ok_item = true;
-       e.noalign = true;
-       e.pickup_anyway = true;
-       e.spawnfunc_checked = true;
-       spawnfunc_item_armor_small(e);
-       if (!wasfreed(e)) { // might have been blocked by a mutator
-        e.movetype = MOVETYPE_TOSS;
-        e.gravity = 1;
-        e.reset = SUB_Remove;
-        setorigin(e, this.origin + '0 0 32');
-        e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500;
-        SUB_SetFade(e, time + 5, 1);
-       }
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerDies)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-
-       entity targ = ((frag_attacker) ? frag_attacker : frag_target);
-
-       ok_DropItem(frag_target, targ);
-
-       frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id;
-}
-
-MUTATOR_HOOKFUNCTION(ok, MonsterDropItem)
-{
-       entity mon = M_ARGV(0, entity);
-       entity olditem = M_ARGV(1, entity);
-       entity frag_attacker = M_ARGV(2, entity);
-
-       remove(olditem);
-
-       M_ARGV(1, entity) = NULL;
-
-       ok_DropItem(mon, frag_attacker);
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerRegen)
-{
-       entity player = M_ARGV(0, entity);
-
-       // overkill's values are different, so use custom regen
-       if(!STAT(FROZEN, player))
-       {
-               player.armorvalue = CalcRotRegen(player.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear,
-                       1 * frametime * (time > player.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > player.pauserotarmor_finished), autocvar_g_balance_armor_limit);
-               player.health = CalcRotRegen(player.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > player.ok_pauseregen_finished), 200, 0,
-                       autocvar_g_balance_health_rotlinear, 1 * frametime * (time > player.pauserothealth_finished), autocvar_g_balance_health_limit);
-
-               float minf, maxf, limitf;
-
-               maxf = autocvar_g_balance_fuel_rotstable;
-               minf = autocvar_g_balance_fuel_regenstable;
-               limitf = autocvar_g_balance_fuel_limit;
-
-               player.ammo_fuel = CalcRotRegen(player.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear,
-                       frametime * (time > player.pauseregen_finished) * ((player.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > player.pauserotfuel_finished), limitf);
-       }
-       return true; // return true anyway, as frozen uses no regen
-}
-
-MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
-{
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
-{
-       if(intermission_running || gameover)
-               return;
-
-       entity player = M_ARGV(0, entity);
-
-       if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player))
-               return;
-
-       if(player.ok_lastwep)
-       {
-               Weapon newwep = Weapons_from(player.ok_lastwep);
-               if(player.ok_lastwep == WEP_HMG.m_id)
-                       newwep = WEP_MACHINEGUN;
-               if(player.ok_lastwep == WEP_RPC.m_id)
-                       newwep = WEP_VORTEX;
-               PS(player).m_switchweapon = newwep;
-               player.ok_lastwep = 0;
-       }
-
-       ok_IncreaseCharge(player, PS(player).m_weapon.m_id);
-
-       if(PHYS_INPUT_BUTTON_ATCK2(player))
-       if(!forbidWeaponUse(player) || player.weapon_blocked) // allow if weapon is blocked
-       if(time >= player.jump_interval)
-       {
-               player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player);
-               makevectors(player.v_angle);
-
-               Weapon oldwep = PS(player).m_weapon;
-               PS(player).m_weapon = WEP_BLASTER;
-               W_Blaster_Attack(
-                       player,
-                       WEP_BLASTER.m_id | HITTYPE_SECONDARY,
-                       WEP_CVAR_SEC(vaporizer, shotangle),
-                       WEP_CVAR_SEC(vaporizer, damage),
-                       WEP_CVAR_SEC(vaporizer, edgedamage),
-                       WEP_CVAR_SEC(vaporizer, radius),
-                       WEP_CVAR_SEC(vaporizer, force),
-                       WEP_CVAR_SEC(vaporizer, speed),
-                       WEP_CVAR_SEC(vaporizer, spread),
-                       WEP_CVAR_SEC(vaporizer, delay),
-                       WEP_CVAR_SEC(vaporizer, lifetime)
-               );
-               PS(player).m_weapon = oldwep;
-       }
-
-       player.weapon_blocked = false;
-
-       player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id];
-
-       if(player.ok_use_ammocharge)
-       if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id))
-       {
-               if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon)
-               {
-                       //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE);
-                       player.ok_notice_time = time + 2;
-                       play2(player, SND(DRYFIRE));
-               }
-               Weapon wpn = PS(player).m_weapon;
-               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-               if(player.(weaponentity).state != WS_CLEAR)
-                       w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1));
-
-               player.weapon_blocked = true;
-       }
-
-       PHYS_INPUT_BUTTON_ATCK2(player) = false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(autocvar_g_overkill_ammo_charge)
-       {
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit));
-
-               player.ok_use_ammocharge = 1;
-               player.ok_notice_time = time;
-       }
-       else
-               player.ok_use_ammocharge = 0;
-
-       // if player changed their weapon while dead, don't switch to their death weapon
-       if(player.impulse)
-               player.ok_lastwep = 0;
-
-       player.ok_pauseregen_finished = time + 2;
-}
-
-void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); }
-void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); }
-
-MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
-{
-       entity ent = M_ARGV(0, entity);
-
-       if(autocvar_g_powerups)
-       if(autocvar_g_overkill_powerups_replace)
-       {
-               if(ent.classname == "item_strength")
-               {
-                       entity wep = new(weapon_hmg);
-                       setorigin(wep, ent.origin);
-                       setmodel(wep, MDL_OK_HMG);
-                       wep.ok_item = true;
-                       wep.noalign = ent.noalign;
-                       wep.cnt = ent.cnt;
-                       wep.team = ent.team;
-                       wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
-                       wep.pickup_anyway = true;
-                       wep.spawnfunc_checked = true;
-                       setthink(wep, self_spawnfunc_weapon_hmg);
-                       wep.nextthink = time + 0.1;
-                       return true;
-               }
-
-               if(ent.classname == "item_invincible")
-               {
-                       entity wep = new(weapon_rpc);
-                       setorigin(wep, ent.origin);
-                       setmodel(wep, MDL_OK_RPC);
-                       wep.ok_item = true;
-                       wep.noalign = ent.noalign;
-                       wep.cnt = ent.cnt;
-                       wep.team = ent.team;
-                       wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
-                       wep.pickup_anyway = true;
-                       wep.spawnfunc_checked = true;
-                       setthink(wep, self_spawnfunc_weapon_rpc);
-                       wep.nextthink = time + 0.1;
-                       return true;
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(ok, FilterItem)
-{
-       entity item = M_ARGV(0, entity);
-
-       if(item.ok_item)
-               return;
-
-       switch(item.items)
-       {
-               case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway);
-               case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway);
-       }
-
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(ok, SpectateCopy)
-{
-       entity spectatee = M_ARGV(0, entity);
-       entity client = M_ARGV(1, entity);
-
-       client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id];
-       client.ok_use_ammocharge = spectatee.ok_use_ammocharge;
-}
-
-MUTATOR_HOOKFUNCTION(ok, SetStartItems)
-{
-       WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
-
-       if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
-       if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
-
-       start_items |= IT_UNLIMITED_WEAPON_AMMO;
-       start_weapons = warmup_start_weapons = ok_start_items;
-}
-
-MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK");
-}
-
-MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill");
-}
-
-MUTATOR_HOOKFUNCTION(ok, SetModname)
-{
-       M_ARGV(0, string) = "Overkill";
-       return true;
-}
-
-void ok_SetCvars()
-{
-       // hack to force overkill playermodels
-       cvar_settemp("sv_defaultcharacter", "1");
-       cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
-       cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm");
-       cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
-}
-
-void ok_Initialize()
-{
-       ok_SetCvars();
-
-       precache_all_playermodels("models/ok_player/*.dpm");
-
-       WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-       WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-
-       WEP_SHOTGUN.mdl = "ok_shotgun";
-       WEP_MACHINEGUN.mdl = "ok_mg";
-       WEP_VORTEX.mdl = "ok_sniper";
-}
-#endif
+#include "overkill.qh"
diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qh b/qcsrc/common/mutators/mutator/overkill/overkill.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 90c671a6094d36f39fa46d060972b44bcdf4879a..ad2ee3f02994ca9e8098e794fcb7fa0d6c036f5c 100644 (file)
@@ -1,72 +1,30 @@
-#ifndef IMPLEMENTATION
-CLASS(RocketPropelledChainsaw, Weapon)
-/* ammotype  */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets)
-/* impulse   */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7)
-/* flags     */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
-/* rating    */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
-/* color     */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0');
-/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl");
-#ifndef MENUQC
-/* model     */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM);
-#endif
-/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher");
-/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6);
-/* wepimg    */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc");
-/* refname   */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc");
-/* wepname   */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw"));
-
-#define X(BEGIN, P, END, class, prefix) \
-       BEGIN(class) \
-               P(class, prefix, ammo, float, NONE) \
-               P(class, prefix, animtime, float, NONE) \
-               P(class, prefix, damage2, float, NONE) \
-               P(class, prefix, damageforcescale, float, NONE) \
-               P(class, prefix, damage, float, NONE) \
-               P(class, prefix, edgedamage, float, NONE) \
-               P(class, prefix, force, float, NONE) \
-               P(class, prefix, health, float, NONE) \
-               P(class, prefix, lifetime, float, NONE) \
-               P(class, prefix, radius, float, NONE) \
-               P(class, prefix, refire, float, NONE) \
-               P(class, prefix, reload_ammo, float, NONE) \
-        P(class, prefix, reload_time, float, NONE) \
-               P(class, prefix, speedaccel, float, NONE) \
-               P(class, prefix, speed, float, NONE) \
-        P(class, prefix, switchdelay_drop, float, NONE) \
-        P(class, prefix, switchdelay_raise, float, NONE) \
-        P(class, prefix, weaponreplace, string, NONE) \
-        P(class, prefix, weaponstartoverride, float, NONE) \
-        P(class, prefix, weaponstart, float, NONE) \
-        P(class, prefix, weaponthrowable, float, NONE) \
-    END()
-    W_PROPS(X, RocketPropelledChainsaw, rpc)
-#undef X
-
-ENDCLASS(RocketPropelledChainsaw)
-REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw));
+#include "rpc.qh"
 
-#endif
-#ifdef IMPLEMENTATION
 #ifdef SVQC
 spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); }
 
-void W_RocketPropelledChainsaw_Explode(entity this)
+void W_RocketPropelledChainsaw_Explode(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       RadiusDamage (this, this.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), NULL, NULL, WEP_CVAR(rpc, force), this.projectiledeathtype, other);
+       RadiusDamage (this, this.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), NULL, NULL, WEP_CVAR(rpc, force), this.projectiledeathtype, directhitentity);
+
+       delete (this);
+}
 
-       remove (this);
+void W_RocketPropelledChainsaw_Explode_think(entity this)
+{
+       W_RocketPropelledChainsaw_Explode(this, NULL);
 }
 
-void W_RocketPropelledChainsaw_Touch (entity this)
+void W_RocketPropelledChainsaw_Touch (entity this, entity toucher)
 {
-       if(WarpZone_Projectile_Touch(this))
+       if(WarpZone_Projectile_Touch(this, toucher))
                if(wasfreed(this))
                        return;
 
-       W_RocketPropelledChainsaw_Explode(this);
+       W_RocketPropelledChainsaw_Explode(this, toucher);
 }
 
 void W_RocketPropelledChainsaw_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -80,14 +38,14 @@ void W_RocketPropelledChainsaw_Damage(entity this, entity inflictor, entity atta
        this.health = this.health - damage;
 
        if (this.health <= 0)
-               W_PrepareExplosionByDamage(this, attacker, W_RocketPropelledChainsaw_Explode);
+               W_PrepareExplosionByDamage(this, attacker, W_RocketPropelledChainsaw_Explode_think);
 }
 
 void W_RocketPropelledChainsaw_Think(entity this)
 {
        if(this.cnt <= time)
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -105,13 +63,13 @@ void W_RocketPropelledChainsaw_Think(entity this)
        this.nextthink = time;
 }
 
-void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor)
+void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(actor);
        entity flash = spawn ();
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR(rpc, ammo));
-       W_SetupShot_ProjectileSize (actor, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage));
+       W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
        PROJECTILE_MAKETRIGGER(missile);
 
@@ -124,7 +82,8 @@ void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor)
        missile.health = WEP_CVAR(rpc, health);
        missile.event_damage = W_RocketPropelledChainsaw_Damage;
        missile.damagedbycontents = true;
-       missile.movetype = MOVETYPE_FLY;
+       IL_PUSH(g_damagedbycontents, missile);
+       set_movetype(missile, MOVETYPE_FLY);
 
        missile.projectiledeathtype = WEP_RPC.m_id;
        setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
@@ -138,13 +97,15 @@ void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor)
        missile.cnt = time + WEP_CVAR(rpc, lifetime);
        missile.nextthink = time;
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
 
        CSQCProjectile(missile, true, PROJECTILE_RPC, false);
 
        setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below
        SUB_SetFade (flash, time, 0.1);
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
-       W_AttachToShotorg(actor, flash, '5 0 0');
+       W_AttachToShotorg(actor, weaponentity, flash, '5 0 0');
        missile.pos1 = missile.velocity;
 
        MUTATOR_CALLHOOK(EditProjectile, actor, missile);
@@ -165,7 +126,7 @@ METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .en
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire)))
             {
-                W_RocketPropelledChainsaw_Attack(thiswep, actor);
+                W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
             }
         }
@@ -191,7 +152,7 @@ METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep, entity actor
 
 METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, WEP_CVAR(rpc, ammo), SND_RELOAD);
+    W_Reload(actor, weaponentity, WEP_CVAR(rpc, ammo), SND_RELOAD);
 }
 
 METHOD(RocketPropelledChainsaw, wr_suicidemessage, Notification(entity thiswep))
@@ -226,4 +187,3 @@ METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep, entity act
 }
 
 #endif
-#endif
diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qh b/qcsrc/common/mutators/mutator/overkill/rpc.qh
new file mode 100644 (file)
index 0000000..535fa55
--- /dev/null
@@ -0,0 +1,47 @@
+#pragma once
+
+CLASS(RocketPropelledChainsaw, Weapon)
+/* ammotype  */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets);
+/* impulse   */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7);
+/* flags     */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
+/* rating    */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
+/* color     */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0');
+/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl");
+#ifdef GAMEQC
+/* model     */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM);
+#endif
+/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher");
+/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6);
+/* wepimg    */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc");
+/* refname   */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc");
+/* wepname   */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw"));
+
+#define X(BEGIN, P, END, class, prefix) \
+       BEGIN(class) \
+               P(class, prefix, ammo, float, NONE) \
+               P(class, prefix, animtime, float, NONE) \
+               P(class, prefix, damage2, float, NONE) \
+               P(class, prefix, damageforcescale, float, NONE) \
+               P(class, prefix, damage, float, NONE) \
+               P(class, prefix, edgedamage, float, NONE) \
+               P(class, prefix, force, float, NONE) \
+               P(class, prefix, health, float, NONE) \
+               P(class, prefix, lifetime, float, NONE) \
+               P(class, prefix, radius, float, NONE) \
+               P(class, prefix, refire, float, NONE) \
+               P(class, prefix, reload_ammo, float, NONE) \
+        P(class, prefix, reload_time, float, NONE) \
+               P(class, prefix, speedaccel, float, NONE) \
+               P(class, prefix, speed, float, NONE) \
+        P(class, prefix, switchdelay_drop, float, NONE) \
+        P(class, prefix, switchdelay_raise, float, NONE) \
+        P(class, prefix, weaponreplace, string, NONE) \
+        P(class, prefix, weaponstartoverride, float, NONE) \
+        P(class, prefix, weaponstart, float, NONE) \
+        P(class, prefix, weaponthrowable, float, NONE) \
+    END()
+    W_PROPS(X, RocketPropelledChainsaw, rpc)
+#undef X
+
+ENDCLASS(RocketPropelledChainsaw)
+REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw));
diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
new file mode 100644 (file)
index 0000000..56bc8d1
--- /dev/null
@@ -0,0 +1,385 @@
+#include "sv_overkill.qh"
+
+#include "hmg.qh"
+#include "rpc.qh"
+
+bool autocvar_g_overkill_powerups_replace;
+bool autocvar_g_overkill_ammo_charge;
+float autocvar_g_overkill_ammo_charge_notice;
+float autocvar_g_overkill_ammo_charge_limit;
+
+bool autocvar_g_overkill_filter_healthmega;
+bool autocvar_g_overkill_filter_armormedium;
+bool autocvar_g_overkill_filter_armorbig;
+bool autocvar_g_overkill_filter_armormega;
+
+.float ok_lastwep;
+.float ok_item;
+
+.float ok_notice_time;
+.float ammo_charge[Weapons_MAX];
+.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE);
+.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL);
+
+void(entity ent, float wep) ok_DecreaseCharge;
+
+void ok_Initialize();
+
+REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
+{
+       MUTATOR_ONADD
+       {
+               ok_Initialize();
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+               WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo)
+{
+       entity actor = M_ARGV(0, entity);
+       if (actor.ok_use_ammocharge)
+       {
+               ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id);
+               return true;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(ok, W_Reload)
+{
+       entity actor = M_ARGV(0, entity);
+       return actor.ok_use_ammocharge;
+}
+
+void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float);
+spawnfunc(weapon_hmg);
+spawnfunc(weapon_rpc);
+
+void ok_DecreaseCharge(entity ent, int wep)
+{
+       if(!ent.ok_use_ammocharge) return;
+
+       entity wepent = Weapons_from(wep);
+
+       if (wepent == WEP_Null) return;  // dummy
+
+       ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
+}
+
+void ok_IncreaseCharge(entity ent, int wep)
+{
+       entity wepent = Weapons_from(wep);
+
+       if (wepent == WEP_Null) return;  // dummy
+
+       if(ent.ok_use_ammocharge)
+       if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking?
+               ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
+}
+
+float ok_CheckWeaponCharge(entity ent, int wep)
+{
+       if(!ent.ok_use_ammocharge) return true;
+
+       entity wepent = Weapons_from(wep);
+
+       if(wepent == WEP_Null) return false;  // dummy
+
+       return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(3, float);
+
+       if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
+       if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
+       {
+               if(frag_attacker != frag_target)
+               if(frag_target.health > 0)
+               if(STAT(FROZEN, frag_target) == 0)
+               if(!IS_DEAD(frag_target))
+               {
+                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
+                       M_ARGV(6, vector) = '0 0 0'; // force
+               }
+
+               M_ARGV(4, float) = 0; // damage
+       }
+}
+
+void ok_DropItem(entity this, entity targ)
+{
+       entity e = new(droppedweapon); // hax
+       e.ok_item = true;
+       e.noalign = true;
+       e.pickup_anyway = true;
+       e.spawnfunc_checked = true;
+       spawnfunc_item_armor_small(e);
+       if (!wasfreed(e)) { // might have been blocked by a mutator
+        set_movetype(e, MOVETYPE_TOSS);
+        e.gravity = 1;
+        e.reset = SUB_Remove;
+        setorigin(e, this.origin + '0 0 32');
+        e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500;
+        SUB_SetFade(e, time + 5, 1);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerDies)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+
+       entity targ = ((frag_attacker) ? frag_attacker : frag_target);
+
+       ok_DropItem(frag_target, targ);
+
+       frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id;
+}
+
+MUTATOR_HOOKFUNCTION(ok, MonsterDropItem)
+{
+       entity mon = M_ARGV(0, entity);
+       entity olditem = M_ARGV(1, entity);
+       entity frag_attacker = M_ARGV(2, entity);
+
+       delete(olditem);
+
+       M_ARGV(1, entity) = NULL;
+
+       ok_DropItem(mon, frag_attacker);
+}
+
+MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
+{
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
+{
+       if(gameover)
+               return;
+
+       entity player = M_ARGV(0, entity);
+
+       if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player))
+               return;
+
+       if(player.ok_lastwep)
+       {
+               Weapon newwep = Weapons_from(player.ok_lastwep);
+               if(player.ok_lastwep == WEP_HMG.m_id)
+                       newwep = WEP_MACHINEGUN;
+               if(player.ok_lastwep == WEP_RPC.m_id)
+                       newwep = WEP_VORTEX;
+               PS(player).m_switchweapon = newwep;
+               player.ok_lastwep = 0;
+       }
+
+       ok_IncreaseCharge(player, PS(player).m_weapon.m_id);
+
+       if(PHYS_INPUT_BUTTON_ATCK2(player))
+       if( !forbidWeaponUse(player) || player.weapon_blocked // allow if weapon is blocked
+               || (round_handler_IsActive() && !round_handler_IsRoundStarted()) )
+       if(time >= player.jump_interval)
+       {
+               player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player);
+               makevectors(player.v_angle);
+
+               Weapon oldwep = PS(player).m_weapon;
+               PS(player).m_weapon = WEP_BLASTER;
+               W_Blaster_Attack(
+                       player,
+                       weaponentities[0], // TODO: unhardcode
+                       WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+                       WEP_CVAR_SEC(vaporizer, shotangle),
+                       WEP_CVAR_SEC(vaporizer, damage),
+                       WEP_CVAR_SEC(vaporizer, edgedamage),
+                       WEP_CVAR_SEC(vaporizer, radius),
+                       WEP_CVAR_SEC(vaporizer, force),
+                       WEP_CVAR_SEC(vaporizer, speed),
+                       WEP_CVAR_SEC(vaporizer, spread),
+                       WEP_CVAR_SEC(vaporizer, delay),
+                       WEP_CVAR_SEC(vaporizer, lifetime)
+               );
+               PS(player).m_weapon = oldwep;
+       }
+
+       player.weapon_blocked = false;
+
+       player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id];
+
+       if(player.ok_use_ammocharge)
+       if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id))
+       {
+               if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon)
+               {
+                       //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE);
+                       player.ok_notice_time = time + 2;
+                       play2(player, SND(DRYFIRE));
+               }
+               Weapon wpn = PS(player).m_weapon;
+               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+               if(player.(weaponentity).state != WS_CLEAR)
+                       w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1));
+
+               player.weapon_blocked = true;
+       }
+
+       PHYS_INPUT_BUTTON_ATCK2(player) = false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(autocvar_g_overkill_ammo_charge)
+       {
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit));
+
+               player.ok_use_ammocharge = 1;
+               player.ok_notice_time = time;
+       }
+       else
+               player.ok_use_ammocharge = 0;
+
+       // if player changed their weapon while dead, don't switch to their death weapon
+       if(player.impulse)
+               player.ok_lastwep = 0;
+}
+
+void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); }
+void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); }
+
+MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
+{
+       entity ent = M_ARGV(0, entity);
+
+       if(autocvar_g_powerups)
+       if(autocvar_g_overkill_powerups_replace)
+       {
+               if(ent.classname == "item_strength")
+               {
+                       entity wep = new(weapon_hmg);
+                       setorigin(wep, ent.origin);
+                       setmodel(wep, MDL_OK_HMG);
+                       wep.ok_item = true;
+                       wep.noalign = ent.noalign;
+                       wep.cnt = ent.cnt;
+                       wep.team = ent.team;
+                       wep.respawntime = g_pickup_respawntime_superweapon;
+                       wep.pickup_anyway = true;
+                       wep.spawnfunc_checked = true;
+                       setthink(wep, self_spawnfunc_weapon_hmg);
+                       wep.nextthink = time + 0.1;
+                       return true;
+               }
+
+               if(ent.classname == "item_invincible")
+               {
+                       entity wep = new(weapon_rpc);
+                       setorigin(wep, ent.origin);
+                       setmodel(wep, MDL_OK_RPC);
+                       wep.ok_item = true;
+                       wep.noalign = ent.noalign;
+                       wep.cnt = ent.cnt;
+                       wep.team = ent.team;
+                       wep.respawntime = g_pickup_respawntime_superweapon;
+                       wep.pickup_anyway = true;
+                       wep.spawnfunc_checked = true;
+                       setthink(wep, self_spawnfunc_weapon_rpc);
+                       wep.nextthink = time + 0.1;
+                       return true;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(ok, FilterItem)
+{
+       entity item = M_ARGV(0, entity);
+
+       if(item.ok_item)
+               return false;
+
+       switch(item.itemdef)
+       {
+               case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega;
+               case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium;
+               case ITEM_ArmorBig: return autocvar_g_overkill_filter_armorbig;
+               case ITEM_ArmorMega: return autocvar_g_overkill_filter_armormega;
+       }
+
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(ok, SpectateCopy)
+{
+       entity spectatee = M_ARGV(0, entity);
+       entity client = M_ARGV(1, entity);
+
+       client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id];
+       client.ok_use_ammocharge = spectatee.ok_use_ammocharge;
+}
+
+MUTATOR_HOOKFUNCTION(ok, SetStartItems, CBC_ORDER_LAST)
+{
+       WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
+
+       if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
+       if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
+
+       start_items |= IT_UNLIMITED_WEAPON_AMMO;
+       start_weapons = warmup_start_weapons = ok_start_items;
+}
+
+MUTATOR_HOOKFUNCTION(ok, SetWeaponArena)
+{
+       // turn weapon arena off
+       M_ARGV(0, string) = "off";
+}
+
+MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK");
+}
+
+MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill");
+}
+
+MUTATOR_HOOKFUNCTION(ok, SetModname)
+{
+       M_ARGV(0, string) = "Overkill";
+       return true;
+}
+
+void ok_SetCvars()
+{
+       // hack to force overkill playermodels
+       cvar_settemp("sv_defaultcharacter", "1");
+       cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
+       cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm");
+       cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
+}
+
+void ok_Initialize()
+{
+       ok_SetCvars();
+
+       precache_all_playermodels("models/ok_player/*.dpm");
+
+       WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+       WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+
+       WEP_SHOTGUN.mdl = "ok_shotgun";
+       WEP_MACHINEGUN.mdl = "ok_mg";
+       WEP_VORTEX.mdl = "ok_sniper";
+}
diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 4d4ef59f8ac581b438f48effb461e31b48404e87..e99d4e257a3f7b401868aeda75d8b68c1bdbfa94 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/physical_items/physical_items.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/physical_items/sv_physical_items.qc>
+#endif
index a347cec04e7381d7ea5ba3c558b3b4ae1a6671a9..1aab8b0a84e36f53d672c9b12455a536fffc9d26 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/physical_items/physical_items.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/physical_items/sv_physical_items.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/physical_items/module.inc b/qcsrc/common/mutators/mutator/physical_items/module.inc
deleted file mode 100644 (file)
index 7ed9b03..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "physical_items.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc
deleted file mode 100644 (file)
index 10ec0cf..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#ifdef IMPLEMENTATION
-int autocvar_g_physical_items;
-float autocvar_g_physical_items_damageforcescale;
-float autocvar_g_physical_items_reset;
-
-REGISTER_MUTATOR(physical_items, cvar("g_physical_items"))
-{
-       // check if we have a physics engine
-       MUTATOR_ONADD
-       {
-               if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")))
-               {
-                       LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n");
-                       return -1;
-               }
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // nothing to roll back
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This cannot be removed at runtime\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-.vector spawn_origin, spawn_angles;
-
-void physical_item_think(entity this)
-{
-       this.nextthink = time;
-
-       this.alpha = this.owner.alpha; // apply fading and ghosting
-
-       if(!this.cnt) // map item, not dropped
-       {
-               // copy ghost item properties
-               this.colormap = this.owner.colormap;
-               this.colormod = this.owner.colormod;
-               this.glowmod = this.owner.glowmod;
-
-               // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there
-               if(autocvar_g_physical_items_reset)
-               {
-                       if(this.owner.wait > time) // awaiting respawn
-                       {
-                               setorigin(this, this.spawn_origin);
-                               this.angles = this.spawn_angles;
-                               this.solid = SOLID_NOT;
-                               this.alpha = -1;
-                               this.movetype = MOVETYPE_NONE;
-                       }
-                       else
-                       {
-                               this.alpha = 1;
-                               this.solid = SOLID_CORPSE;
-                               this.movetype = MOVETYPE_PHYSICS;
-                       }
-               }
-       }
-
-       if(!this.owner.modelindex)
-               remove(this); // the real item is gone, remove this
-}
-
-void physical_item_touch(entity this)
-{
-       if(!this.cnt) // not for dropped items
-       if (ITEM_TOUCH_NEEDKILL())
-       {
-               setorigin(this, this.spawn_origin);
-               this.angles = this.spawn_angles;
-       }
-}
-
-void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if(!this.cnt) // not for dropped items
-       if(ITEM_DAMAGE_NEEDKILL(deathtype))
-       {
-               setorigin(this, this.spawn_origin);
-               this.angles = this.spawn_angles;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn)
-{
-       entity item = M_ARGV(0, entity);
-
-       if(item.owner == NULL && autocvar_g_physical_items <= 1)
-               return;
-       if (item.spawnflags & 1) // floating item
-               return;
-
-       // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics.
-       // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed.
-       entity wep;
-       wep = spawn();
-       _setmodel(wep, item.model);
-       setsize(wep, item.mins, item.maxs);
-       setorigin(wep, item.origin);
-       wep.angles = item.angles;
-       wep.velocity = item.velocity;
-
-       wep.owner = item;
-       wep.solid = SOLID_CORPSE;
-       wep.movetype = MOVETYPE_PHYSICS;
-       wep.takedamage = DAMAGE_AIM;
-       wep.effects |= EF_NOMODELFLAGS; // disable the spinning
-       wep.colormap = item.owner.colormap;
-       wep.glowmod = item.owner.glowmod;
-       wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
-       wep.dphitcontentsmask = item.dphitcontentsmask;
-       wep.cnt = (item.owner != NULL);
-
-       setthink(wep, physical_item_think);
-       wep.nextthink = time;
-       settouch(wep, physical_item_touch);
-       wep.event_damage = physical_item_damage;
-
-       if(!wep.cnt)
-       {
-               // fix the spawn origin
-               setorigin(wep, wep.origin + '0 0 1');
-               droptofloor(wep);
-       }
-
-       wep.spawn_origin = wep.origin;
-       wep.spawn_angles = item.angles;
-
-       item.effects |= EF_NODRAW; // hide the original weapon
-       item.movetype = MOVETYPE_FOLLOW;
-       item.aiment = wep; // attach the original weapon
-       setSendEntity(item, func_null);
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc
new file mode 100644 (file)
index 0000000..62b30e2
--- /dev/null
@@ -0,0 +1,141 @@
+#include "sv_physical_items.qh"
+
+int autocvar_g_physical_items;
+float autocvar_g_physical_items_damageforcescale;
+float autocvar_g_physical_items_reset;
+
+REGISTER_MUTATOR(physical_items, cvar("g_physical_items"))
+{
+       // check if we have a physics engine
+       MUTATOR_ONADD
+       {
+               if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")))
+               {
+                       LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.");
+                       return -1;
+               }
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // nothing to roll back
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This cannot be removed at runtime\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+.vector spawn_origin, spawn_angles;
+
+void physical_item_think(entity this)
+{
+       this.nextthink = time;
+
+       this.alpha = this.owner.alpha; // apply fading and ghosting
+
+       if(!this.cnt) // map item, not dropped
+       {
+               // copy ghost item properties
+               this.colormap = this.owner.colormap;
+               this.colormod = this.owner.colormod;
+               this.glowmod = this.owner.glowmod;
+
+               // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there
+               if(autocvar_g_physical_items_reset)
+               {
+                       if(this.owner.wait > time) // awaiting respawn
+                       {
+                               setorigin(this, this.spawn_origin);
+                               this.angles = this.spawn_angles;
+                               this.solid = SOLID_NOT;
+                               this.alpha = -1;
+                               set_movetype(this, MOVETYPE_NONE);
+                       }
+                       else
+                       {
+                               this.alpha = 1;
+                               this.solid = SOLID_CORPSE;
+                               set_movetype(this, MOVETYPE_PHYSICS);
+                       }
+               }
+       }
+
+       if(!this.owner.modelindex)
+               delete(this); // the real item is gone, remove this
+}
+
+void physical_item_touch(entity this, entity toucher)
+{
+       if(!this.cnt) // not for dropped items
+       if (ITEM_TOUCH_NEEDKILL())
+       {
+               setorigin(this, this.spawn_origin);
+               this.angles = this.spawn_angles;
+       }
+}
+
+void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if(!this.cnt) // not for dropped items
+       if(ITEM_DAMAGE_NEEDKILL(deathtype))
+       {
+               setorigin(this, this.spawn_origin);
+               this.angles = this.spawn_angles;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn)
+{
+       entity item = M_ARGV(0, entity);
+
+       if(item.owner == NULL && autocvar_g_physical_items <= 1)
+               return;
+       if (item.spawnflags & 1) // floating item
+               return;
+
+       // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics.
+       // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed.
+       entity wep;
+       wep = spawn();
+       _setmodel(wep, item.model);
+       setsize(wep, item.mins, item.maxs);
+       setorigin(wep, item.origin);
+       wep.angles = item.angles;
+       wep.velocity = item.velocity;
+
+       wep.owner = item;
+       wep.solid = SOLID_CORPSE;
+       set_movetype(wep, MOVETYPE_PHYSICS);
+       wep.takedamage = DAMAGE_AIM;
+       wep.effects |= EF_NOMODELFLAGS; // disable the spinning
+       wep.colormap = item.owner.colormap;
+       wep.glowmod = item.owner.glowmod;
+       wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
+       wep.dphitcontentsmask = item.dphitcontentsmask;
+       wep.cnt = (item.owner != NULL);
+
+       setthink(wep, physical_item_think);
+       wep.nextthink = time;
+       settouch(wep, physical_item_touch);
+       wep.event_damage = physical_item_damage;
+
+       if(!wep.cnt)
+       {
+               // fix the spawn origin
+               setorigin(wep, wep.origin + '0 0 1');
+               droptofloor(wep);
+       }
+
+       wep.spawn_origin = wep.origin;
+       wep.spawn_angles = item.angles;
+
+       item.effects |= EF_NODRAW; // hide the original weapon
+       set_movetype(item, MOVETYPE_FOLLOW);
+       item.aiment = wep; // attach the original weapon
+       setSendEntity(item, func_null);
+}
diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index a0bd94d00eab025aa6f843161be5fac9528649ff..5859c55107462dcdfdf9ce339ea99684abf40f21 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/pinata/pinata.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/pinata/sv_pinata.qc>
+#endif
index 1602640e096a854dd01b4687244005e947592550..fdb51ed25ba3f9346487eaafbf8cef9404657a83 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/pinata/pinata.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/pinata/sv_pinata.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/pinata/module.inc b/qcsrc/common/mutators/mutator/pinata/module.inc
deleted file mode 100644 (file)
index 4e22966..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "pinata.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/pinata/pinata.qc b/qcsrc/common/mutators/mutator/pinata/pinata.qc
deleted file mode 100644 (file)
index 10866a2..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill"));
-
-MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
-{
-       entity frag_target = M_ARGV(2, entity);
-       
-       FOREACH(Weapons, it != WEP_Null, LAMBDA(
-               if(frag_target.weapons & WepSet_FromWeapon(it))
-               if(PS(frag_target).m_switchweapon != it)
-               if(W_IsWeaponThrowable(frag_target, it.m_id))
-                       W_ThrowNewWeapon(frag_target, it.m_id, false, CENTER_OR_VIEWOFS(frag_target), randomvec() * 175 + '0 0 325');
-       ));
-
-       return true;
-}
-
-MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Pinata");
-}
-
-MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Piñata");
-}
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc
new file mode 100644 (file)
index 0000000..bc3887e
--- /dev/null
@@ -0,0 +1,27 @@
+#include "sv_pinata.qh"
+
+REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill"));
+
+MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
+{
+       entity frag_target = M_ARGV(2, entity);
+
+       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               if(frag_target.weapons & WepSet_FromWeapon(it))
+               if(PS(frag_target).m_switchweapon != it)
+               if(W_IsWeaponThrowable(frag_target, it.m_id))
+                       W_ThrowNewWeapon(frag_target, it.m_id, false, CENTER_OR_VIEWOFS(frag_target), randomvec() * 175 + '0 0 325');
+       ));
+
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Pinata");
+}
+
+MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Piñata");
+}
diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index feeaec8d6966e8319a0d242ae0cd55420b85c42c..846bd8bf82e222f9d2fc34d1d7c2327224bdbdbc 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/random_gravity/random_gravity.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/random_gravity/sv_random_gravity.qc>
+#endif
index 99a11ed63819ac4594feebff6e40a857f1abd956..2cdf724023280a22955a6d307b03df293cd48fe2 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/random_gravity/random_gravity.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/random_gravity/sv_random_gravity.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/random_gravity/module.inc b/qcsrc/common/mutators/mutator/random_gravity/module.inc
deleted file mode 100644 (file)
index 91baa43..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "random_gravity.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc
deleted file mode 100644 (file)
index a384a2b..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifdef IMPLEMENTATION
-// Random Gravity
-//
-// Mutator by Mario
-// Inspired by Player 2
-
-float autocvar_g_random_gravity_negative_chance;
-float autocvar_g_random_gravity_min;
-float autocvar_g_random_gravity_max;
-float autocvar_g_random_gravity_positive;
-float autocvar_g_random_gravity_negative;
-float autocvar_g_random_gravity_delay;
-
-REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity"))
-{
-       MUTATOR_ONADD
-       {
-               cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end
-       }
-}
-
-float gravity_delay;
-
-MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame)
-{
-       if(gameover || !cvar("g_random_gravity")) return false;
-       if(time < gravity_delay) return false;
-       if(time < game_starttime) return false;
-       if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false;
-
-    if(random() >= autocvar_g_random_gravity_negative_chance)
-        cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max)));
-    else
-        cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max)));
-
-       gravity_delay = time + autocvar_g_random_gravity_delay;
-
-       LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n");
-}
-
-MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RandomGravity");
-}
-
-MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random gravity");
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc
new file mode 100644 (file)
index 0000000..f6f28a7
--- /dev/null
@@ -0,0 +1,50 @@
+#include "sv_random_gravity.qh"
+
+// Random Gravity
+
+// Mutator by Mario
+// Inspired by Player 2
+
+float autocvar_g_random_gravity_negative_chance;
+float autocvar_g_random_gravity_min;
+float autocvar_g_random_gravity_max;
+float autocvar_g_random_gravity_positive;
+float autocvar_g_random_gravity_negative;
+float autocvar_g_random_gravity_delay;
+
+REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity"))
+{
+       MUTATOR_ONADD
+       {
+               cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end
+       }
+}
+
+float gravity_delay;
+
+MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame)
+{
+       if(gameover || !cvar("g_random_gravity")) return false;
+       if(time < gravity_delay) return false;
+       if(time < game_starttime) return false;
+       if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false;
+
+    if(random() >= autocvar_g_random_gravity_negative_chance)
+        cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max)));
+    else
+        cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max)));
+
+       gravity_delay = time + autocvar_g_random_gravity_delay;
+
+       LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity));
+}
+
+MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RandomGravity");
+}
+
+MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random gravity");
+}
diff --git a/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 0841ae680b745135b4b8ee8845a06a9dd158724f..537a2b37b020116516e125b11118e5dcbcfce0bd 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/rocketflying/rocketflying.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/rocketflying/sv_rocketflying.qc>
+#endif
index 75ca141bf0d9ed83bf42576bb9fc9d32eedfab53..b24545f8c41a10e2ed4e7918f86688463cf217b7 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/rocketflying/rocketflying.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/rocketflying/sv_rocketflying.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/rocketflying/module.inc b/qcsrc/common/mutators/mutator/rocketflying/module.inc
deleted file mode 100644 (file)
index 7036bc4..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "rocketflying.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc
deleted file mode 100644 (file)
index da7e1c3..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying"));
-
-MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile)
-{
-       entity proj = M_ARGV(1, entity);
-
-       if(proj.classname == "rocket" || proj.classname == "mine")
-       {
-               // kill detonate delay of rockets
-               proj.spawnshieldtime = time;
-       }
-}
-
-MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RocketFlying");
-}
-
-MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Rocket Flying");
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc
new file mode 100644 (file)
index 0000000..9f0d8fb
--- /dev/null
@@ -0,0 +1,24 @@
+#include "sv_rocketflying.qh"
+
+REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying"));
+
+MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile)
+{
+       entity proj = M_ARGV(1, entity);
+
+       if(proj.classname == "rocket" || proj.classname == "mine")
+       {
+               // kill detonate delay of rockets
+               proj.spawnshieldtime = time;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RocketFlying");
+}
+
+MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Rocket Flying");
+}
diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index bc579ec512d070e8171e16d2a161c007ce9c6d54..bb75554badaf418395aa5f74c09cfbb18a920364 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/rocketminsta/rocketminsta.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/rocketminsta/sv_rocketminsta.qc>
+#endif
index 29a367d3cf869ddb8c70c64c41a2139d2653682c..832c5da2cb6ccd1ab29155df5c647973ffe66527 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/rocketminsta/rocketminsta.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/rocketminsta/sv_rocketminsta.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/rocketminsta/module.inc b/qcsrc/common/mutators/mutator/rocketminsta/module.inc
deleted file mode 100644 (file)
index b7d02a9..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "rocketminsta.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc
deleted file mode 100644 (file)
index b0a7403..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifdef IMPLEMENTATION
-#include <common/deathtypes/all.qh>
-#include <server/round_handler.qh>
-
-REGISTER_MUTATOR(rm, cvar("g_instagib"));
-
-MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate)
-{
-       // we do it this way, so rm can be toggled during the match
-       if(!autocvar_g_rm) { return; }
-
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-       float frag_deathtype = M_ARGV(3, float);
-       float frag_damage = M_ARGV(4, float);
-
-       if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR))
-       if(frag_attacker == frag_target || frag_target.classname == "nade")
-               frag_damage = 0;
-
-       if(autocvar_g_rm_laser)
-       if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
-       if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
-               frag_damage = 0;
-
-       M_ARGV(4, float) = frag_damage;
-}
-
-MUTATOR_HOOKFUNCTION(rm, PlayerDies)
-{
-       // we do it this way, so rm can be toggled during the match
-       if(!autocvar_g_rm) { return; }
-
-       float frag_deathtype = M_ARGV(3, float);
-
-       if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
-               M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
-}
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc
new file mode 100644 (file)
index 0000000..04d8099
--- /dev/null
@@ -0,0 +1,39 @@
+#include "sv_rocketminsta.qh"
+
+#include <common/deathtypes/all.qh>
+#include <server/round_handler.qh>
+
+REGISTER_MUTATOR(rm, cvar("g_instagib"));
+
+MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate)
+{
+       // we do it this way, so rm can be toggled during the match
+       if(!autocvar_g_rm) { return; }
+
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(3, float);
+       float frag_damage = M_ARGV(4, float);
+
+       if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR))
+       if(frag_attacker == frag_target || frag_target.classname == "nade")
+               frag_damage = 0;
+
+       if(autocvar_g_rm_laser)
+       if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
+       if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
+               frag_damage = 0;
+
+       M_ARGV(4, float) = frag_damage;
+}
+
+MUTATOR_HOOKFUNCTION(rm, PlayerDies)
+{
+       // we do it this way, so rm can be toggled during the match
+       if(!autocvar_g_rm) { return; }
+
+       float frag_deathtype = M_ARGV(3, float);
+
+       if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
+               M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
+}
diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index f88b36a534e3bd57ffde4f4df0f841ceec1e747f..d1db34cf759dba2ecb3cf072f8d89a2fa9308fc8 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/running_guns/running_guns.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/running_guns/sv_running_guns.qc>
+#endif
index 559be4c987bc745bc9467da3a0c50b8a0cd47381..cc0c58eff406110fa57b4eed192ce85470212cd2 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/running_guns/running_guns.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/running_guns/sv_running_guns.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/running_guns/module.inc b/qcsrc/common/mutators/mutator/running_guns/module.inc
deleted file mode 100644 (file)
index 036b70f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "running_guns.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/running_guns/running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/running_guns.qc
deleted file mode 100644 (file)
index cad4d5f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifdef IMPLEMENTATION
-
-bool autocvar_g_running_guns;
-
-REGISTER_MUTATOR(running_guns, autocvar_g_running_guns);
-
-MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha)
-{
-       default_player_alpha = -1;
-       default_weapon_alpha = +1;
-       return true;
-}
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc
new file mode 100644 (file)
index 0000000..797108e
--- /dev/null
@@ -0,0 +1,11 @@
+#include "sv_running_guns.qh"
+
+bool autocvar_g_running_guns;
+REGISTER_MUTATOR(running_guns, autocvar_g_running_guns);
+
+MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha)
+{
+       default_player_alpha = -1;
+       default_weapon_alpha = +1;
+       return true;
+}
diff --git a/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 8e54c1f95e62e2690766d611e71dedccb9c8827c..569c25319b9ed268fbb16ab7aede4fd45624134d 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/sandbox/sandbox.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/sandbox/sv_sandbox.qc>
+#endif
index 81e250c7fa149ad4b10e35cd51eff3243d369bab..86b112934dd8bdc58d00e1bdbd11d7b93a452d3e 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/sandbox/sandbox.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/sandbox/sv_sandbox.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/sandbox/module.inc b/qcsrc/common/mutators/mutator/sandbox/module.inc
deleted file mode 100644 (file)
index 0715d5b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifdef SVQC
-#include "sandbox.qc"
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc
deleted file mode 100644 (file)
index bc4cb67..0000000
+++ /dev/null
@@ -1,828 +0,0 @@
-#ifdef IMPLEMENTATION
-int autocvar_g_sandbox_info;
-bool autocvar_g_sandbox_readonly;
-string autocvar_g_sandbox_storage_name;
-float autocvar_g_sandbox_storage_autosave;
-bool autocvar_g_sandbox_storage_autoload;
-float autocvar_g_sandbox_editor_flood;
-int autocvar_g_sandbox_editor_maxobjects;
-int autocvar_g_sandbox_editor_free;
-float autocvar_g_sandbox_editor_distance_spawn;
-float autocvar_g_sandbox_editor_distance_edit;
-float autocvar_g_sandbox_object_scale_min;
-float autocvar_g_sandbox_object_scale_max;
-float autocvar_g_sandbox_object_material_velocity_min;
-float autocvar_g_sandbox_object_material_velocity_factor;
-
-float autosave_time;
-void sandbox_Database_Load();
-
-REGISTER_MUTATOR(sandbox, cvar("g_sandbox"))
-{
-       MUTATOR_ONADD
-       {
-               autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
-               if(autocvar_g_sandbox_storage_autoload)
-                       sandbox_Database_Load();
-       }
-}
-
-const float MAX_STORAGE_ATTACHMENTS = 16;
-float object_count;
-.float object_flood;
-.entity object_attach;
-.string material;
-
-.float touch_timer;
-void sandbox_ObjectFunction_Touch(entity this)
-{
-       // apply material impact effects
-
-       if(!this.material)
-               return;
-       if(this.touch_timer > time)
-               return; // don't execute each frame
-       this.touch_timer = time + 0.1;
-
-       // make particle count and sound volume depend on impact speed
-       float intensity;
-       intensity = vlen(this.velocity) + vlen(other.velocity);
-       if(intensity) // avoid divisions by 0
-               intensity /= 2; // average the two velocities
-       if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min))
-               return; // impact not strong enough to do anything
-       // now offset intensity and apply it to the effects
-       intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity
-       intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1);
-
-       _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM);
-       Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
-}
-
-void sandbox_ObjectFunction_Think(entity this)
-{
-       // decide if and how this object can be grabbed
-       if(autocvar_g_sandbox_readonly)
-               this.grab = 0; // no grabbing
-       else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp)
-               this.grab = 1; // owner only
-       else
-               this.grab = 3; // anyone
-
-       // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
-       // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
-       // since if the owning player disconnects, the object's owner should also be reset.
-
-       // bots can't have objects
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA(
-               if(this.crypto_idfp == it.crypto_idfp)
-               {
-                       this.realowner = it;
-                       break;
-               }
-               this.realowner = NULL;
-       ));
-
-       this.nextthink = time;
-
-       CSQCMODEL_AUTOUPDATE(this);
-}
-
-.float old_solid, old_movetype;
-entity sandbox_ObjectEdit_Get(entity this, float permissions)
-{
-       // Returns the traced entity if the player can edit it, and NULL if not.
-       // If permissions if false, the object is returned regardless of editing rights.
-       // Attached objects are SOLID_NOT and do not get traced.
-
-       crosshair_trace_plusvisibletriggers(this);
-       if(vdist(this.origin - trace_ent.origin, >, autocvar_g_sandbox_editor_distance_edit))
-               return NULL; // out of trace range
-       if(trace_ent.classname != "object")
-               return NULL; // entity is not an object
-       if(!permissions)
-               return trace_ent; // don't check permissions, anyone can edit this object
-       if(trace_ent.crypto_idfp == "")
-               return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
-       if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2))
-               return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
-       return NULL;
-}
-
-void sandbox_ObjectEdit_Scale(entity e, float f)
-{
-       e.scale = f;
-       if(e.scale)
-       {
-               e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
-               _setmodel(e, e.model); // reset mins and maxs based on mesh
-               setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
-       }
-}
-
-void sandbox_ObjectAttach_Remove(entity e);
-void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
-{
-       // attaches e to parent on string s
-
-       // we can't attach to an attachment, for obvious reasons
-       sandbox_ObjectAttach_Remove(e);
-
-       e.old_solid = e.solid; // persist solidity
-       e.old_movetype = e.movetype; // persist physics
-       e.movetype = MOVETYPE_FOLLOW;
-       e.solid = SOLID_NOT;
-       e.takedamage = DAMAGE_NO;
-
-       setattachment(e, parent, s);
-       e.owner = parent;
-}
-
-void sandbox_ObjectAttach_Remove(entity e)
-{
-       // detaches any object attached to e
-
-       entity head;
-       for(head = NULL; (head = find(head, classname, "object")); )
-       {
-               if(head.owner == e)
-               {
-                       vector org;
-                       org = gettaginfo(head, 0);
-                       setattachment(head, NULL, "");
-                       head.owner = NULL;
-
-                       // objects change origin and angles when detached, so apply previous position
-                       setorigin(head, org);
-                       head.angles = e.angles; // don't allow detached objects to spin or roll
-
-                       head.solid = head.old_solid; // restore persisted solidity
-                       head.movetype = head.old_movetype; // restore persisted physics
-                       head.takedamage = DAMAGE_AIM;
-               }
-       }
-}
-
-entity sandbox_ObjectSpawn(entity this, float database)
-{
-       // spawn a new object with default properties
-
-       entity e = new(object);
-       e.takedamage = DAMAGE_AIM;
-       e.damageforcescale = 1;
-       e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly
-       e.movetype = MOVETYPE_TOSS;
-       e.frame = 0;
-       e.skin = 0;
-       e.material = string_null;
-       settouch(e, sandbox_ObjectFunction_Touch);
-       setthink(e, sandbox_ObjectFunction_Think);
-       e.nextthink = time;
-       //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
-
-       if(!database)
-       {
-               // set the object's owner via player UID
-               // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone
-               if(this.crypto_idfp != "")
-                       e.crypto_idfp = strzone(this.crypto_idfp);
-               else
-                       print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!");
-
-               // set public object information
-               e.netname = strzone(this.netname); // name of the owner
-               e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time
-               e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time
-
-               // set origin and direction based on player position and view angle
-               makevectors(this.v_angle);
-               WarpZone_TraceLine(this.origin + this.view_ofs, this.origin + this.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, this);
-               setorigin(e, trace_endpos);
-               e.angles_y = this.v_angle.y;
-       }
-
-       CSQCMODEL_AUTOINIT(e);
-
-       object_count += 1;
-       return e;
-}
-
-void sandbox_ObjectRemove(entity e)
-{
-       sandbox_ObjectAttach_Remove(e); // detach child objects
-
-       // if the object being removed has been selected for attachment by a player, unset it
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, LAMBDA(it.object_attach = NULL));
-
-       if(e.material)  {       strunzone(e.material);  e.material = string_null;       }
-       if(e.crypto_idfp)       {       strunzone(e.crypto_idfp);       e.crypto_idfp = string_null;    }
-       if(e.netname)   {       strunzone(e.netname);   e.netname = string_null;        }
-       if(e.message)   {       strunzone(e.message);   e.message = string_null;        }
-       if(e.message2)  {       strunzone(e.message2);  e.message2 = string_null;       }
-       remove(e);
-       e = NULL;
-
-       object_count -= 1;
-}
-
-string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
-
-string sandbox_ObjectPort_Save(entity e, float database)
-{
-       // save object properties, and return them as a string
-       float i = 0;
-       string s;
-       entity head;
-
-       for(head = NULL; (head = find(head, classname, "object")); )
-       {
-               // the main object needs to be first in the array [0] with attached objects following
-               float slot, physics, solidity;
-               if(head == e) // this is the main object, place it first
-               {
-                       slot = 0;
-                       solidity = head.solid; // applied solidity is normal solidity for children
-                       physics = head.movetype; // applied physics are normal physics for parents
-               }
-               else if(head.owner == e) // child object, list them in order
-               {
-                       i += 1; // children start from 1
-                       slot = i;
-                       solidity = head.old_solid; // persisted solidity is normal solidity for children
-                       physics = head.old_movetype; // persisted physics are normal physics for children
-                       gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below
-               }
-               else
-                       continue;
-
-               // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
-               if(slot)
-               {
-                       // properties stored only for child objects
-                       if(gettaginfo_name)     port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" ");    else    port_string[slot] = strcat(port_string[slot], "\"\" "); // none
-               }
-               else
-               {
-                       // properties stored only for parent objects
-                       if(database)
-                       {
-                               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " ");
-                               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " ");
-                       }
-               }
-               // properties stored for all objects
-               port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" ");
-               port_string[slot] = strcat(port_string[slot], ftos(head.skin), " ");
-               port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " ");
-               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " ");
-               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " ");
-               port_string[slot] = strcat(port_string[slot], ftos(head.frame), " ");
-               port_string[slot] = strcat(port_string[slot], ftos(head.scale), " ");
-               port_string[slot] = strcat(port_string[slot], ftos(solidity), " ");
-               port_string[slot] = strcat(port_string[slot], ftos(physics), " ");
-               port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " ");
-               if(head.material)       port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" ");      else    port_string[slot] = strcat(port_string[slot], "\"\" "); // none
-               if(database)
-               {
-                       // properties stored only for the database
-                       if(head.crypto_idfp)    port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" ");   else    port_string[slot] = strcat(port_string[slot], "\"\" "); // none
-                       port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" ");
-                       port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" ");
-                       port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" ");
-               }
-       }
-
-       // now apply the array to a simple string, with the ; symbol separating objects
-       s = "";
-       for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
-       {
-               if(port_string[i])
-                       s = strcat(s, port_string[i], "; ");
-               port_string[i] = string_null; // fully clear the string
-       }
-
-       return s;
-}
-
-entity sandbox_ObjectPort_Load(entity this, string s, float database)
-{
-       // load object properties, and spawn a new object with them
-       float n, i;
-       entity e = NULL, parent = NULL;
-
-       // separate objects between the ; symbols
-       n = tokenizebyseparator(s, "; ");
-       for(i = 0; i < n; ++i)
-               port_string[i] = argv(i);
-
-       // now separate and apply the properties of each object
-       for(i = 0; i < n; ++i)
-       {
-               float argv_num;
-               string tagname = string_null;
-               argv_num = 0;
-               tokenize_console(port_string[i]);
-               e = sandbox_ObjectSpawn(this, database);
-
-               // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
-               if(i)
-               {
-                       // properties stored only for child objects
-                       if(argv(argv_num) != "")        tagname = argv(argv_num);       else tagname = string_null;     ++argv_num;
-               }
-               else
-               {
-                       // properties stored only for parent objects
-                       if(database)
-                       {
-                               setorigin(e, stov(argv(argv_num)));     ++argv_num;
-                               e.angles = stov(argv(argv_num));        ++argv_num;
-                       }
-                       parent = e; // mark parent objects as such
-               }
-               // properties stored for all objects
-               _setmodel(e, argv(argv_num));   ++argv_num;
-               e.skin = stof(argv(argv_num));  ++argv_num;
-               e.alpha = stof(argv(argv_num)); ++argv_num;
-               e.colormod = stov(argv(argv_num));      ++argv_num;
-               e.glowmod = stov(argv(argv_num));       ++argv_num;
-               e.frame = stof(argv(argv_num)); ++argv_num;
-               sandbox_ObjectEdit_Scale(e, stof(argv(argv_num)));      ++argv_num;
-               e.solid = e.old_solid = stof(argv(argv_num));   ++argv_num;
-               e.movetype = e.old_movetype = stof(argv(argv_num));     ++argv_num;
-               e.damageforcescale = stof(argv(argv_num));      ++argv_num;
-               if(e.material)  strunzone(e.material);  if(argv(argv_num) != "")        e.material = strzone(argv(argv_num));   else    e.material = string_null;       ++argv_num;
-               if(database)
-               {
-                       // properties stored only for the database
-                       if(e.crypto_idfp)       strunzone(e.crypto_idfp);       if(argv(argv_num) != "")        e.crypto_idfp = strzone(argv(argv_num));        else    e.crypto_idfp = string_null;    ++argv_num;
-                       if(e.netname)   strunzone(e.netname);   e.netname = strzone(argv(argv_num));    ++argv_num;
-                       if(e.message)   strunzone(e.message);   e.message = strzone(argv(argv_num));    ++argv_num;
-                       if(e.message2)  strunzone(e.message2);  e.message2 = strzone(argv(argv_num));   ++argv_num;
-               }
-
-               // attach last
-               if(i)
-                       sandbox_ObjectAttach_Set(e, parent, tagname);
-       }
-
-       for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
-               port_string[i] = string_null; // fully clear the string
-
-       return e;
-}
-
-void sandbox_Database_Save()
-{
-       // saves all objects to the database file
-       entity head;
-       string file_name;
-       float file_get;
-
-       file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
-       file_get = fopen(file_name, FILE_WRITE);
-       fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
-       fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
-
-       for(head = NULL; (head = find(head, classname, "object")); )
-       {
-               // attached objects are persisted separately, ignore them here
-               if(head.owner != NULL)
-                       continue;
-
-               // use a line of text for each object, listing all properties
-               fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n"));
-       }
-       fclose(file_get);
-}
-
-void sandbox_Database_Load()
-{
-       // loads all objects from the database file
-       string file_read, file_name;
-       float file_get, i;
-
-       file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
-       file_get = fopen(file_name, FILE_READ);
-       if(file_get < 0)
-       {
-               if(autocvar_g_sandbox_info > 0)
-                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n"));
-       }
-       else
-       {
-               for (;;)
-               {
-                       file_read = fgets(file_get);
-                       if(file_read == "")
-                               break;
-                       if(substring(file_read, 0, 2) == "//")
-                               continue;
-                       if(substring(file_read, 0, 1) == "#")
-                               continue;
-
-                       entity e;
-                       e = sandbox_ObjectPort_Load(NULL, file_read, true);
-
-                       if(e.material)
-                       {
-                               // since objects are being loaded for the first time, precache material sounds for each
-                               for (i = 1; i <= 5; i++) // 5 sounds in total
-                                       precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
-                       }
-               }
-               if(autocvar_g_sandbox_info > 0)
-                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n"));
-       }
-       fclose(file_get);
-}
-
-MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand)
-{
-       if(MUTATOR_RETURNVALUE) // command was already handled?
-               return;
-
-       entity player = M_ARGV(0, entity);
-       string cmd_name = M_ARGV(1, string);
-       int cmd_argc = M_ARGV(2, int);
-
-       if(cmd_name == "g_sandbox")
-       {
-               if(autocvar_g_sandbox_readonly)
-               {
-                       print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
-                       return true;
-               }
-               if(cmd_argc < 2)
-               {
-                       print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
-                       return true;
-               }
-
-               switch(argv(1))
-               {
-                       entity e;
-                       float i;
-                       string s;
-
-                       // ---------------- COMMAND: HELP ----------------
-                       case "help":
-                               print_to(player, "You can use the following sandbox commands:");
-                               print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
-                               print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
-                               print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
-                               print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar");
-                               print_to(player, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\"");
-                               print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
-                               print_to(player, "^3get ^7- selects the object you are facing as the object to be attached");
-                               print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
-                               print_to(player, "^3remove ^7- detaches all objects from the object you are facing");
-                               print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
-                               print_to(player, "^3skin value ^7- changes the skin of the object");
-                               print_to(player, "^3alpha value ^7- sets object transparency");
-                               print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color");
-                               print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
-                               print_to(player, "^3frame value ^7- object animation frame, for self-animated models");
-                               print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
-                               print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
-                               print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
-                               print_to(player, "^3force value ^7- amount of force applied to objects that are shot");
-                               print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
-                               print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
-                               print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object");
-                               print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
-                               print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
-                               print_to(player, "^3attachments ^7- prints information about the object's attachments");
-                               print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, SPAWN ----------------
-                       case "object_spawn":
-                               if(time < player.object_flood)
-                               {
-                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
-                                       return true;
-                               }
-                               player.object_flood = time + autocvar_g_sandbox_editor_flood;
-                               if(object_count >= autocvar_g_sandbox_editor_maxobjects)
-                               {
-                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
-                                       return true;
-                               }
-                               if(cmd_argc < 3)
-                               {
-                                       print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
-                                       return true;
-                               }
-                               if (!(fexists(argv(2))))
-                               {
-                                       print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
-                                       return true;
-                               }
-
-                               e = sandbox_ObjectSpawn(player, false);
-                               _setmodel(e, argv(2));
-
-                               if(autocvar_g_sandbox_info > 0)
-                                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin), "\n"));
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, REMOVE ----------------
-                       case "object_remove":
-                               e = sandbox_ObjectEdit_Get(player, true);
-                               if(e != NULL)
-                               {
-                                       if(autocvar_g_sandbox_info > 0)
-                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin), "\n"));
-                                       sandbox_ObjectRemove(e);
-                                       return true;
-                               }
-
-                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
-                       case "object_duplicate":
-                               switch(argv(2))
-                               {
-                                       case "copy":
-                                               // copies customizable properties of the selected object to the clipboard cvar
-                                               e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
-                                               if(e != NULL)
-                                               {
-                                                       s = sandbox_ObjectPort_Save(e, false);
-                                                       s = strreplace("\"", "\\\"", s);
-                                                       stuffcmd(player, strcat("set ", argv(3), " \"", s, "\""));
-
-                                                       print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard");
-                                                       return true;
-                                               }
-                                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
-                                               return true;
-
-                                       case "paste":
-                                               // spawns a new object using the properties in the player's clipboard cvar
-                                               if(time < player.object_flood)
-                                               {
-                                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
-                                                       return true;
-                                               }
-                                               player.object_flood = time + autocvar_g_sandbox_editor_flood;
-                                               if(argv(3) == "") // no object in clipboard
-                                               {
-                                                       print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
-                                                       return true;
-                                               }
-                                               if(object_count >= autocvar_g_sandbox_editor_maxobjects)
-                                               {
-                                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
-                                                       return true;
-                                               }
-                                               e = sandbox_ObjectPort_Load(player, argv(3), false);
-
-                                               print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully");
-                                               if(autocvar_g_sandbox_info > 0)
-                                                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin), "\n"));
-                                               return true;
-                               }
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, ATTACH ----------------
-                       case "object_attach":
-                               switch(argv(2))
-                               {
-                                       case "get":
-                                               // select e as the object as meant to be attached
-                                               e = sandbox_ObjectEdit_Get(player, true);
-                                               if(e != NULL)
-                                               {
-                                                       player.object_attach = e;
-                                                       print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment");
-                                                       return true;
-                                               }
-                                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over");
-                                               return true;
-                                       case "set":
-                                               if(player.object_attach == NULL)
-                                               {
-                                                       print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
-                                                       return true;
-                                               }
-
-                                               // attaches the previously selected object to e
-                                               e = sandbox_ObjectEdit_Get(player, true);
-                                               if(e != NULL)
-                                               {
-                                                       sandbox_ObjectAttach_Set(player.object_attach, e, argv(3));
-                                                       player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment
-                                                       print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully");
-                                                       if(autocvar_g_sandbox_info > 1)
-                                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin), "\n"));
-                                                       return true;
-                                               }
-                                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over");
-                                               return true;
-                                       case "remove":
-                                               // removes e if it was attached
-                                               e = sandbox_ObjectEdit_Get(player, true);
-                                               if(e != NULL)
-                                               {
-                                                       sandbox_ObjectAttach_Remove(e);
-                                                       print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully");
-                                                       if(autocvar_g_sandbox_info > 1)
-                                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin), "\n"));
-                                                       return true;
-                                               }
-                                               print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
-                                               return true;
-                               }
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, EDIT ----------------
-                       case "object_edit":
-                               if(argv(2) == "")
-                               {
-                                       print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
-                                       return true;
-                               }
-
-                               e = sandbox_ObjectEdit_Get(player, true);
-                               if(e != NULL)
-                               {
-                                       switch(argv(2))
-                                       {
-                                               case "skin":
-                                                       e.skin = stof(argv(3));
-                                                       break;
-                                               case "alpha":
-                                                       e.alpha = stof(argv(3));
-                                                       break;
-                                               case "color_main":
-                                                       e.colormod = stov(argv(3));
-                                                       break;
-                                               case "color_glow":
-                                                       e.glowmod = stov(argv(3));
-                                                       break;
-                                               case "frame":
-                                                       e.frame = stof(argv(3));
-                                                       break;
-                                               case "scale":
-                                                       sandbox_ObjectEdit_Scale(e, stof(argv(3)));
-                                                       break;
-                                               case "solidity":
-                                                       switch(argv(3))
-                                                       {
-                                                               case "0": // non-solid
-                                                                       e.solid = SOLID_TRIGGER;
-                                                                       break;
-                                                               case "1": // solid
-                                                                       e.solid = SOLID_BBOX;
-                                                                       break;
-                                                               default:
-                                                                       break;
-                                                       }
-                                               case "physics":
-                                                       switch(argv(3))
-                                                       {
-                                                               case "0": // static
-                                                                       e.movetype = MOVETYPE_NONE;
-                                                                       break;
-                                                               case "1": // movable
-                                                                       e.movetype = MOVETYPE_TOSS;
-                                                                       break;
-                                                               case "2": // physical
-                                                                       e.movetype = MOVETYPE_PHYSICS;
-                                                                       break;
-                                                               default:
-                                                                       break;
-                                                       }
-                                                       break;
-                                               case "force":
-                                                       e.damageforcescale = stof(argv(3));
-                                                       break;
-                                               case "material":
-                                                       if(e.material)  strunzone(e.material);
-                                                       if(argv(3))
-                                                       {
-                                                               for (i = 1; i <= 5; i++) // precache material sounds, 5 in total
-                                                                       precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav"));
-                                                               e.material = strzone(argv(3));
-                                                       }
-                                                       else
-                                                               e.material = string_null; // no material
-                                                       break;
-                                               default:
-                                                       print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
-                                                       return true;
-                                       }
-
-                                       // update last editing time
-                                       if(e.message2)  strunzone(e.message2);
-                                       e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S"));
-
-                                       if(autocvar_g_sandbox_info > 1)
-                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n"));
-                                       return true;
-                               }
-
-                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, CLAIM ----------------
-                       case "object_claim":
-                               // if the player can edit an object but is not its owner, this can be used to claim that object
-                               if(player.crypto_idfp == "")
-                               {
-                                       print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
-                                       return true;
-                               }
-                               e = sandbox_ObjectEdit_Get(player, true);
-                               if(e != NULL)
-                               {
-                                       // update the owner's name
-                                       // Do this before checking if you're already the owner and skipping if such, so we
-                                       // also update the player's nickname if he changed it (but has the same player UID)
-                                       if(e.netname != player.netname)
-                                       {
-                                               if(e.netname)   strunzone(e.netname);
-                                               e.netname = strzone(player.netname);
-                                               print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
-                                       }
-
-                                       if(e.crypto_idfp == player.crypto_idfp)
-                                       {
-                                               print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
-                                               return true;
-                                       }
-
-                                       if(e.crypto_idfp)       strunzone(e.crypto_idfp);
-                                       e.crypto_idfp = strzone(player.crypto_idfp);
-
-                                       print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
-                               }
-                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
-                               return true;
-
-                       // ---------------- COMMAND: OBJECT, INFO ----------------
-                       case "object_info":
-                               // prints public information about the object to the player
-                               e = sandbox_ObjectEdit_Get(player, false);
-                               if(e != NULL)
-                               {
-                                       switch(argv(2))
-                                       {
-                                               case "object":
-                                                       print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
-                                                       return true;
-                                               case "mesh":
-                                                       s = "";
-                                                       FOR_EACH_TAG(e)
-                                                               s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
-                                                       print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
-                                                       return true;
-                                               case "attachments":
-                                                       // this should show the same info as 'mesh' but for attachments
-                                                       s = "";
-                                                       entity head;
-                                                       i = 0;
-                                                       for(head = NULL; (head = find(head, classname, "object")); )
-                                                       {
-                                                               if(head.owner == e)
-                                                               {
-                                                                       ++i; // start from 1
-                                                                       gettaginfo(e, head.tag_index);
-                                                                       s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame));
-                                                                       s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
-                                                               }
-                                                       }
-                                                       if(i) // object contains attachments
-                                                               print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s));
-                                                       else
-                                                               print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments");
-                                                       return true;
-                                       }
-                               }
-                               print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
-                               return true;
-
-                       // ---------------- COMMAND: DEFAULT ----------------
-                       default:
-                               print_to(player, "Invalid command. For usage information, type 'sandbox help'");
-                               return true;
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
-{
-       if(!autocvar_g_sandbox_storage_autosave)
-               return;
-       if(time < autosave_time)
-               return;
-       autosave_time = time + autocvar_g_sandbox_storage_autosave;
-
-       sandbox_Database_Save();
-
-       return true;
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc
new file mode 100644 (file)
index 0000000..a2211fe
--- /dev/null
@@ -0,0 +1,823 @@
+#include "sv_sandbox.qh"
+
+int autocvar_g_sandbox_info;
+bool autocvar_g_sandbox_readonly;
+string autocvar_g_sandbox_storage_name;
+float autocvar_g_sandbox_storage_autosave;
+bool autocvar_g_sandbox_storage_autoload;
+float autocvar_g_sandbox_editor_flood;
+int autocvar_g_sandbox_editor_maxobjects;
+int autocvar_g_sandbox_editor_free;
+float autocvar_g_sandbox_editor_distance_spawn;
+float autocvar_g_sandbox_editor_distance_edit;
+float autocvar_g_sandbox_object_scale_min;
+float autocvar_g_sandbox_object_scale_max;
+float autocvar_g_sandbox_object_material_velocity_min;
+float autocvar_g_sandbox_object_material_velocity_factor;
+
+float autosave_time;
+void sandbox_Database_Load();
+
+REGISTER_MUTATOR(sandbox, cvar("g_sandbox"))
+{
+       MUTATOR_ONADD
+       {
+               autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
+               if(autocvar_g_sandbox_storage_autoload)
+                       sandbox_Database_Load();
+       }
+}
+
+const float MAX_STORAGE_ATTACHMENTS = 16;
+float object_count;
+.float object_flood;
+.entity object_attach;
+.string material;
+
+.float touch_timer;
+void sandbox_ObjectFunction_Touch(entity this, entity toucher)
+{
+       // apply material impact effects
+
+       if(!this.material)
+               return;
+       if(this.touch_timer > time)
+               return; // don't execute each frame
+       this.touch_timer = time + 0.1;
+
+       // make particle count and sound volume depend on impact speed
+       float intensity;
+       intensity = vlen(this.velocity) + vlen(toucher.velocity);
+       if(intensity) // avoid divisions by 0
+               intensity /= 2; // average the two velocities
+       if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min))
+               return; // impact not strong enough to do anything
+       // now offset intensity and apply it to the effects
+       intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity
+       intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1);
+
+       _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM);
+       Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
+}
+
+void sandbox_ObjectFunction_Think(entity this)
+{
+       // decide if and how this object can be grabbed
+       if(autocvar_g_sandbox_readonly)
+               this.grab = 0; // no grabbing
+       else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp)
+               this.grab = 1; // owner only
+       else
+               this.grab = 3; // anyone
+
+       // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
+       // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
+       // since if the owning player disconnects, the object's owner should also be reset.
+
+       // bots can't have objects
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA(
+               if(this.crypto_idfp == it.crypto_idfp)
+               {
+                       this.realowner = it;
+                       break;
+               }
+               this.realowner = NULL;
+       ));
+
+       this.nextthink = time;
+
+       CSQCMODEL_AUTOUPDATE(this);
+}
+
+.float old_solid, old_movetype;
+entity sandbox_ObjectEdit_Get(entity this, float permissions)
+{
+       // Returns the traced entity if the player can edit it, and NULL if not.
+       // If permissions if false, the object is returned regardless of editing rights.
+       // Attached objects are SOLID_NOT and do not get traced.
+
+       crosshair_trace_plusvisibletriggers(this);
+       if(vdist(this.origin - trace_ent.origin, >, autocvar_g_sandbox_editor_distance_edit))
+               return NULL; // out of trace range
+       if(trace_ent.classname != "object")
+               return NULL; // entity is not an object
+       if(!permissions)
+               return trace_ent; // don't check permissions, anyone can edit this object
+       if(trace_ent.crypto_idfp == "")
+               return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
+       if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2))
+               return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
+       return NULL;
+}
+
+void sandbox_ObjectEdit_Scale(entity e, float f)
+{
+       e.scale = f;
+       if(e.scale)
+       {
+               e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
+               _setmodel(e, e.model); // reset mins and maxs based on mesh
+               setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
+       }
+}
+
+void sandbox_ObjectAttach_Remove(entity e);
+void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
+{
+       // attaches e to parent on string s
+
+       // we can't attach to an attachment, for obvious reasons
+       sandbox_ObjectAttach_Remove(e);
+
+       e.old_solid = e.solid; // persist solidity
+       e.old_movetype = e.move_movetype; // persist physics
+       set_movetype(e, MOVETYPE_FOLLOW);
+       e.solid = SOLID_NOT;
+       e.takedamage = DAMAGE_NO;
+
+       setattachment(e, parent, s);
+       e.owner = parent;
+}
+
+void sandbox_ObjectAttach_Remove(entity e)
+{
+       // detaches any object attached to e
+
+       IL_EACH(g_sandbox_objects, it.owner == e,
+       {
+               vector org = gettaginfo(it, 0);
+               setattachment(it, NULL, "");
+               it.owner = NULL;
+
+               // objects change origin and angles when detached, so apply previous position
+               setorigin(it, org);
+               it.angles = e.angles; // don't allow detached objects to spin or roll
+
+               it.solid = it.old_solid; // restore persisted solidity
+               set_movetype(it, it.old_movetype); // restore persisted physics
+               it.takedamage = DAMAGE_AIM;
+       });
+}
+
+entity sandbox_ObjectSpawn(entity this, float database)
+{
+       // spawn a new object with default properties
+
+       entity e = new(object);
+       IL_PUSH(g_sandbox_objects, e);
+       e.takedamage = DAMAGE_AIM;
+       e.damageforcescale = 1;
+       e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly
+       set_movetype(e, MOVETYPE_TOSS);
+       e.frame = 0;
+       e.skin = 0;
+       e.material = string_null;
+       settouch(e, sandbox_ObjectFunction_Touch);
+       setthink(e, sandbox_ObjectFunction_Think);
+       e.nextthink = time;
+       //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
+
+       if(!database)
+       {
+               // set the object's owner via player UID
+               // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone
+               if(this.crypto_idfp != "")
+                       e.crypto_idfp = strzone(this.crypto_idfp);
+               else
+                       print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!");
+
+               // set public object information
+               e.netname = strzone(this.netname); // name of the owner
+               e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time
+               e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time
+
+               // set origin and direction based on player position and view angle
+               makevectors(this.v_angle);
+               WarpZone_TraceLine(this.origin + this.view_ofs, this.origin + this.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, this);
+               setorigin(e, trace_endpos);
+               e.angles_y = this.v_angle.y;
+       }
+
+       CSQCMODEL_AUTOINIT(e);
+
+       object_count += 1;
+       return e;
+}
+
+void sandbox_ObjectRemove(entity e)
+{
+       sandbox_ObjectAttach_Remove(e); // detach child objects
+
+       // if the object being removed has been selected for attachment by a player, unset it
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, LAMBDA(it.object_attach = NULL));
+
+       if(e.material)  {       strunzone(e.material);  e.material = string_null;       }
+       if(e.crypto_idfp)       {       strunzone(e.crypto_idfp);       e.crypto_idfp = string_null;    }
+       if(e.netname)   {       strunzone(e.netname);   e.netname = string_null;        }
+       if(e.message)   {       strunzone(e.message);   e.message = string_null;        }
+       if(e.message2)  {       strunzone(e.message2);  e.message2 = string_null;       }
+       delete(e);
+       e = NULL;
+
+       object_count -= 1;
+}
+
+string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
+
+string sandbox_ObjectPort_Save(entity e, bool database)
+{
+       // save object properties, and return them as a string
+       int o = 0;
+
+       // order doesn't really matter, as we're writing the file fresh
+       IL_EACH(g_sandbox_objects, it == e || it.owner == e, LAMBDA(
+               // the main object needs to be first in the array [0] with attached objects following
+               int slot, physics, solidity;
+               if(it == e) // this is the main object, place it first
+               {
+                       slot = 0;
+                       solidity = it.solid; // applied solidity is normal solidity for children
+                       physics = it.move_movetype; // applied physics are normal physics for parents
+               }
+               else if(it.owner == e) // child object, list them in order
+               {
+                       o += 1; // children start from 1
+                       slot = o;
+                       solidity = it.old_solid; // persisted solidity is normal solidity for children
+                       physics = it.old_movetype; // persisted physics are normal physics for children
+                       gettaginfo(it.owner, it.tag_index); // get the name of the tag our object is attached to, used further below
+               }
+               else
+                       continue;
+
+               // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
+               if(slot)
+               {
+                       // properties stored only for child objects
+                       if(gettaginfo_name)
+                               port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" ");
+                       else
+                               port_string[slot] = strcat(port_string[slot], "\"\" "); // none
+               }
+               else
+               {
+                       // properties stored only for parent objects
+                       if(database)
+                       {
+                               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.origin), " ");
+                               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.angles), " ");
+                       }
+               }
+               // properties stored for all objects
+               port_string[slot] = strcat(port_string[slot], "\"", it.model, "\" ");
+               port_string[slot] = strcat(port_string[slot], ftos(it.skin), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(it.alpha), " ");
+               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.colormod), " ");
+               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.glowmod), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(it.frame), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(it.scale), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(solidity), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(physics), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(it.damageforcescale), " ");
+               if(it.material)
+                       port_string[slot] = strcat(port_string[slot], "\"", it.material, "\" ");
+               else
+                       port_string[slot] = strcat(port_string[slot], "\"\" "); // none
+               if(database)
+               {
+                       // properties stored only for the database
+                       if(it.crypto_idfp)
+                               port_string[slot] = strcat(port_string[slot], "\"", it.crypto_idfp, "\" ");
+                       else
+                               port_string[slot] = strcat(port_string[slot], "\"\" "); // none
+                       port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" ");
+                       port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" ");
+                       port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" ");
+               }
+       ));
+
+       // now apply the array to a simple string, with the ; symbol separating objects
+       string s = "";
+       for(int j = 0; j <= MAX_STORAGE_ATTACHMENTS; ++j)
+       {
+               if(port_string[j])
+                       s = strcat(s, port_string[j], "; ");
+               port_string[j] = string_null; // fully clear the string
+       }
+
+       return s;
+}
+
+entity sandbox_ObjectPort_Load(entity this, string s, float database)
+{
+       // load object properties, and spawn a new object with them
+       float n, i;
+       entity e = NULL, parent = NULL;
+
+       // separate objects between the ; symbols
+       n = tokenizebyseparator(s, "; ");
+       for(i = 0; i < n; ++i)
+               port_string[i] = argv(i);
+
+       // now separate and apply the properties of each object
+       for(i = 0; i < n; ++i)
+       {
+               float argv_num;
+               string tagname = string_null;
+               argv_num = 0;
+               tokenize_console(port_string[i]);
+               e = sandbox_ObjectSpawn(this, database);
+
+               // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
+               if(i)
+               {
+                       // properties stored only for child objects
+                       if(argv(argv_num) != "")        tagname = argv(argv_num);       else tagname = string_null;     ++argv_num;
+               }
+               else
+               {
+                       // properties stored only for parent objects
+                       if(database)
+                       {
+                               setorigin(e, stov(argv(argv_num)));     ++argv_num;
+                               e.angles = stov(argv(argv_num));        ++argv_num;
+                       }
+                       parent = e; // mark parent objects as such
+               }
+               // properties stored for all objects
+               _setmodel(e, argv(argv_num));   ++argv_num;
+               e.skin = stof(argv(argv_num));  ++argv_num;
+               e.alpha = stof(argv(argv_num)); ++argv_num;
+               e.colormod = stov(argv(argv_num));      ++argv_num;
+               e.glowmod = stov(argv(argv_num));       ++argv_num;
+               e.frame = stof(argv(argv_num)); ++argv_num;
+               sandbox_ObjectEdit_Scale(e, stof(argv(argv_num)));      ++argv_num;
+               e.solid = e.old_solid = stof(argv(argv_num));   ++argv_num;
+               e.old_movetype = stof(argv(argv_num));  ++argv_num;
+               set_movetype(e, e.old_movetype);
+               e.damageforcescale = stof(argv(argv_num));      ++argv_num;
+               if(e.material)  strunzone(e.material);  if(argv(argv_num) != "")        e.material = strzone(argv(argv_num));   else    e.material = string_null;       ++argv_num;
+               if(database)
+               {
+                       // properties stored only for the database
+                       if(e.crypto_idfp)       strunzone(e.crypto_idfp);       if(argv(argv_num) != "")        e.crypto_idfp = strzone(argv(argv_num));        else    e.crypto_idfp = string_null;    ++argv_num;
+                       if(e.netname)   strunzone(e.netname);   e.netname = strzone(argv(argv_num));    ++argv_num;
+                       if(e.message)   strunzone(e.message);   e.message = strzone(argv(argv_num));    ++argv_num;
+                       if(e.message2)  strunzone(e.message2);  e.message2 = strzone(argv(argv_num));   ++argv_num;
+               }
+
+               // attach last
+               if(i)
+                       sandbox_ObjectAttach_Set(e, parent, tagname);
+       }
+
+       for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
+               port_string[i] = string_null; // fully clear the string
+
+       return e;
+}
+
+void sandbox_Database_Save()
+{
+       // saves all objects to the database file
+       string file_name;
+       float file_get;
+
+       file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
+       file_get = fopen(file_name, FILE_WRITE);
+       fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
+       fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
+
+       IL_EACH(g_sandbox_objects, !it.owner, // attached objects are persisted separately, ignore them here
+       {
+               // use a line of text for each object, listing all properties
+               fputs(file_get, strcat(sandbox_ObjectPort_Save(it, true), "\n"));
+       });
+       fclose(file_get);
+}
+
+void sandbox_Database_Load()
+{
+       // loads all objects from the database file
+       string file_read, file_name;
+       float file_get, i;
+
+       file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
+       file_get = fopen(file_name, FILE_READ);
+       if(file_get < 0)
+       {
+               if(autocvar_g_sandbox_info > 0)
+                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n"));
+       }
+       else
+       {
+               for (;;)
+               {
+                       file_read = fgets(file_get);
+                       if(file_read == "")
+                               break;
+                       if(substring(file_read, 0, 2) == "//")
+                               continue;
+                       if(substring(file_read, 0, 1) == "#")
+                               continue;
+
+                       entity e;
+                       e = sandbox_ObjectPort_Load(NULL, file_read, true);
+
+                       if(e.material)
+                       {
+                               // since objects are being loaded for the first time, precache material sounds for each
+                               for (i = 1; i <= 5; i++) // 5 sounds in total
+                                       precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
+                       }
+               }
+               if(autocvar_g_sandbox_info > 0)
+                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n"));
+       }
+       fclose(file_get);
+}
+
+MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand)
+{
+       if(MUTATOR_RETURNVALUE) // command was already handled?
+               return;
+
+       entity player = M_ARGV(0, entity);
+       string cmd_name = M_ARGV(1, string);
+       int cmd_argc = M_ARGV(2, int);
+
+       if(cmd_name == "g_sandbox")
+       {
+               if(autocvar_g_sandbox_readonly)
+               {
+                       print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
+                       return true;
+               }
+               if(cmd_argc < 2)
+               {
+                       print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
+                       return true;
+               }
+
+               switch(argv(1))
+               {
+                       entity e;
+                       int j;
+                       string s;
+
+                       // ---------------- COMMAND: HELP ----------------
+                       case "help":
+                               print_to(player, "You can use the following sandbox commands:");
+                               print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
+                               print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
+                               print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
+                               print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar");
+                               print_to(player, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\"");
+                               print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
+                               print_to(player, "^3get ^7- selects the object you are facing as the object to be attached");
+                               print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
+                               print_to(player, "^3remove ^7- detaches all objects from the object you are facing");
+                               print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
+                               print_to(player, "^3skin value ^7- changes the skin of the object");
+                               print_to(player, "^3alpha value ^7- sets object transparency");
+                               print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color");
+                               print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
+                               print_to(player, "^3frame value ^7- object animation frame, for self-animated models");
+                               print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
+                               print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
+                               print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
+                               print_to(player, "^3force value ^7- amount of force applied to objects that are shot");
+                               print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
+                               print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
+                               print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object");
+                               print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
+                               print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
+                               print_to(player, "^3attachments ^7- prints information about the object's attachments");
+                               print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, SPAWN ----------------
+                       case "object_spawn":
+                               if(time < player.object_flood)
+                               {
+                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
+                                       return true;
+                               }
+                               player.object_flood = time + autocvar_g_sandbox_editor_flood;
+                               if(object_count >= autocvar_g_sandbox_editor_maxobjects)
+                               {
+                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
+                                       return true;
+                               }
+                               if(cmd_argc < 3)
+                               {
+                                       print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
+                                       return true;
+                               }
+                               if (!(fexists(argv(2))))
+                               {
+                                       print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
+                                       return true;
+                               }
+
+                               e = sandbox_ObjectSpawn(player, false);
+                               _setmodel(e, argv(2));
+
+                               if(autocvar_g_sandbox_info > 0)
+                                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin), "\n"));
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, REMOVE ----------------
+                       case "object_remove":
+                               e = sandbox_ObjectEdit_Get(player, true);
+                               if(e != NULL)
+                               {
+                                       if(autocvar_g_sandbox_info > 0)
+                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin), "\n"));
+                                       sandbox_ObjectRemove(e);
+                                       return true;
+                               }
+
+                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
+                       case "object_duplicate":
+                               switch(argv(2))
+                               {
+                                       case "copy":
+                                               // copies customizable properties of the selected object to the clipboard cvar
+                                               e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
+                                               if(e != NULL)
+                                               {
+                                                       s = sandbox_ObjectPort_Save(e, false);
+                                                       s = strreplace("\"", "\\\"", s);
+                                                       stuffcmd(player, strcat("set ", argv(3), " \"", s, "\""));
+
+                                                       print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard");
+                                                       return true;
+                                               }
+                                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
+                                               return true;
+
+                                       case "paste":
+                                               // spawns a new object using the properties in the player's clipboard cvar
+                                               if(time < player.object_flood)
+                                               {
+                                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
+                                                       return true;
+                                               }
+                                               player.object_flood = time + autocvar_g_sandbox_editor_flood;
+                                               if(argv(3) == "") // no object in clipboard
+                                               {
+                                                       print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
+                                                       return true;
+                                               }
+                                               if(object_count >= autocvar_g_sandbox_editor_maxobjects)
+                                               {
+                                                       print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
+                                                       return true;
+                                               }
+                                               e = sandbox_ObjectPort_Load(player, argv(3), false);
+
+                                               print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully");
+                                               if(autocvar_g_sandbox_info > 0)
+                                                       LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin), "\n"));
+                                               return true;
+                               }
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, ATTACH ----------------
+                       case "object_attach":
+                               switch(argv(2))
+                               {
+                                       case "get":
+                                               // select e as the object as meant to be attached
+                                               e = sandbox_ObjectEdit_Get(player, true);
+                                               if(e != NULL)
+                                               {
+                                                       player.object_attach = e;
+                                                       print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment");
+                                                       return true;
+                                               }
+                                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over");
+                                               return true;
+                                       case "set":
+                                               if(player.object_attach == NULL)
+                                               {
+                                                       print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
+                                                       return true;
+                                               }
+
+                                               // attaches the previously selected object to e
+                                               e = sandbox_ObjectEdit_Get(player, true);
+                                               if(e != NULL)
+                                               {
+                                                       sandbox_ObjectAttach_Set(player.object_attach, e, argv(3));
+                                                       player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment
+                                                       print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully");
+                                                       if(autocvar_g_sandbox_info > 1)
+                                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin), "\n"));
+                                                       return true;
+                                               }
+                                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over");
+                                               return true;
+                                       case "remove":
+                                               // removes e if it was attached
+                                               e = sandbox_ObjectEdit_Get(player, true);
+                                               if(e != NULL)
+                                               {
+                                                       sandbox_ObjectAttach_Remove(e);
+                                                       print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully");
+                                                       if(autocvar_g_sandbox_info > 1)
+                                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin), "\n"));
+                                                       return true;
+                                               }
+                                               print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
+                                               return true;
+                               }
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, EDIT ----------------
+                       case "object_edit":
+                               if(argv(2) == "")
+                               {
+                                       print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
+                                       return true;
+                               }
+
+                               e = sandbox_ObjectEdit_Get(player, true);
+                               if(e != NULL)
+                               {
+                                       switch(argv(2))
+                                       {
+                                               case "skin":
+                                                       e.skin = stof(argv(3));
+                                                       break;
+                                               case "alpha":
+                                                       e.alpha = stof(argv(3));
+                                                       break;
+                                               case "color_main":
+                                                       e.colormod = stov(argv(3));
+                                                       break;
+                                               case "color_glow":
+                                                       e.glowmod = stov(argv(3));
+                                                       break;
+                                               case "frame":
+                                                       e.frame = stof(argv(3));
+                                                       break;
+                                               case "scale":
+                                                       sandbox_ObjectEdit_Scale(e, stof(argv(3)));
+                                                       break;
+                                               case "solidity":
+                                                       switch(argv(3))
+                                                       {
+                                                               case "0": // non-solid
+                                                                       e.solid = SOLID_TRIGGER;
+                                                                       break;
+                                                               case "1": // solid
+                                                                       e.solid = SOLID_BBOX;
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                       }
+                                               case "physics":
+                                                       switch(argv(3))
+                                                       {
+                                                               case "0": // static
+                                                                       set_movetype(e, MOVETYPE_NONE);
+                                                                       break;
+                                                               case "1": // movable
+                                                                       set_movetype(e, MOVETYPE_TOSS);
+                                                                       break;
+                                                               case "2": // physical
+                                                                       set_movetype(e, MOVETYPE_PHYSICS);
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                       }
+                                                       break;
+                                               case "force":
+                                                       e.damageforcescale = stof(argv(3));
+                                                       break;
+                                               case "material":
+                                                       if(e.material)  strunzone(e.material);
+                                                       if(argv(3))
+                                                       {
+                                                               for (j = 1; j <= 5; j++) // precache material sounds, 5 in total
+                                                                       precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav"));
+                                                               e.material = strzone(argv(3));
+                                                       }
+                                                       else
+                                                               e.material = string_null; // no material
+                                                       break;
+                                               default:
+                                                       print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
+                                                       return true;
+                                       }
+
+                                       // update last editing time
+                                       if(e.message2)  strunzone(e.message2);
+                                       e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S"));
+
+                                       if(autocvar_g_sandbox_info > 1)
+                                               LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n"));
+                                       return true;
+                               }
+
+                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, CLAIM ----------------
+                       case "object_claim":
+                               // if the player can edit an object but is not its owner, this can be used to claim that object
+                               if(player.crypto_idfp == "")
+                               {
+                                       print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
+                                       return true;
+                               }
+                               e = sandbox_ObjectEdit_Get(player, true);
+                               if(e != NULL)
+                               {
+                                       // update the owner's name
+                                       // Do this before checking if you're already the owner and skipping if such, so we
+                                       // also update the player's nickname if he changed it (but has the same player UID)
+                                       if(e.netname != player.netname)
+                                       {
+                                               if(e.netname)   strunzone(e.netname);
+                                               e.netname = strzone(player.netname);
+                                               print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
+                                       }
+
+                                       if(e.crypto_idfp == player.crypto_idfp)
+                                       {
+                                               print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
+                                               return true;
+                                       }
+
+                                       if(e.crypto_idfp)       strunzone(e.crypto_idfp);
+                                       e.crypto_idfp = strzone(player.crypto_idfp);
+
+                                       print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
+                               }
+                               print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
+                               return true;
+
+                       // ---------------- COMMAND: OBJECT, INFO ----------------
+                       case "object_info":
+                               // prints public information about the object to the player
+                               e = sandbox_ObjectEdit_Get(player, false);
+                               if(e != NULL)
+                               {
+                                       switch(argv(2))
+                                       {
+                                               case "object":
+                                                       print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
+                                                       return true;
+                                               case "mesh":
+                                                       s = "";
+                                                       FOR_EACH_TAG(e)
+                                                               s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
+                                                       print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
+                                                       return true;
+                                               case "attachments":
+                                                       // this should show the same info as 'mesh' but for attachments
+                                                       s = "";
+                                                       j = 0;
+                                                       IL_EACH(g_sandbox_objects, it.owner == e,
+                                                       {
+                                                               ++j; // start from 1
+                                                               gettaginfo(e, it.tag_index);
+                                                               s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame));
+                                                               s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
+                                                       });
+                                                       if(j) // object contains attachments
+                                                               print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s));
+                                                       else
+                                                               print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments");
+                                                       return true;
+                                       }
+                               }
+                               print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
+                               return true;
+
+                       // ---------------- COMMAND: DEFAULT ----------------
+                       default:
+                               print_to(player, "Invalid command. For usage information, type 'sandbox help'");
+                               return true;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
+{
+       if(!autocvar_g_sandbox_storage_autosave)
+               return;
+       if(time < autosave_time)
+               return;
+       autosave_time = time + autocvar_g_sandbox_storage_autosave;
+
+       sandbox_Database_Save();
+
+       return true;
+}
diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh
new file mode 100644 (file)
index 0000000..f6a0afd
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+
+IntrusiveList g_sandbox_objects;
+STATIC_INIT(g_sandbox_objects) { g_sandbox_objects = IL_NEW(); }
index b7d3af7f4dd3124e9f5521daa0e814a386e9d357..fe3a6ebc5d612cd64ff0fd96d5df502f956176d2 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc>
+#endif
index 5f53e95c885857a293ecdc19b98f1415a5d1db85..b34f8f8f164c22c2bb724817ee2ca77fc3710453 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc
deleted file mode 100644 (file)
index f88a768..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "spawn_near_teammate.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc
deleted file mode 100644 (file)
index b16a6c9..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-#ifdef IMPLEMENTATION
-
-float autocvar_g_spawn_near_teammate_distance;
-int autocvar_g_spawn_near_teammate_ignore_spawnpoint;
-float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
-float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
-int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
-bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
-
-REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate"));
-
-.entity msnt_lookat;
-
-.float msnt_timer;
-.vector msnt_deathloc;
-
-.float cvar_cl_spawn_near_teammate;
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
-{
-       entity player = M_ARGV(0, entity);
-       entity spawn_spot = M_ARGV(1, entity);
-       vector spawn_score = M_ARGV(2, vector);
-
-       if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate))
-               return;
-
-       spawn_spot.msnt_lookat = NULL;
-
-       if(!teamplay)
-               return;
-
-       RandomSelection_Init();
-       FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), LAMBDA(
-               if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance))
-                       continue;
-               if(vdist(spawn_spot.origin - it.origin, <, 48))
-                       continue;
-               if(!checkpvs(spawn_spot.origin, it))
-                       continue;
-               RandomSelection_Add(it, 0, string_null, 1, 1);
-       ));
-
-       if(RandomSelection_chosen_ent)
-       {
-               spawn_spot.msnt_lookat = RandomSelection_chosen_ent;
-               spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
-       }
-       else if(player.team == spawn_spot.team)
-               spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
-
-       M_ARGV(2, vector) = spawn_score;
-}
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
-{
-       if(!teamplay) { return; }
-       entity player = M_ARGV(0, entity);
-       entity spawn_spot = M_ARGV(1, entity);
-
-       int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0;
-       FOREACH_CLIENT(IS_PLAYER(it),
-       {
-               switch(it.team)
-               {
-                       case NUM_TEAM_1: ++num_red; break;
-                       case NUM_TEAM_2: ++num_blue; break;
-                       case NUM_TEAM_3: ++num_yellow; break;
-                       case NUM_TEAM_4: ++num_pink; break;
-               }
-       });
-
-       if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1)
-               return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage!
-
-       // Note: when entering this, fixangle is already set.
-       if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate))
-       {
-               if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death)
-                       player.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
-
-               entity best_mate = NULL;
-               vector best_spot = '0 0 0';
-               float pc = 0, best_dist = 0, dist = 0;
-               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
-                       if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && it.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0)
-                       if(!IS_DEAD(it))
-                       if(it.msnt_timer < time)
-                       if(SAME_TEAM(player, it))
-                       if(time > it.spawnshieldtime) // spawn shielding
-                       if(STAT(FROZEN, it) == 0)
-                       if(it != player)
-                       {
-                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 100', MOVE_NOMONSTERS, it);
-                               if(trace_fraction != 1.0)
-                               if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
-                               {
-                                       pc = pointcontents(trace_endpos + '0 0 1');
-                                       if(pc == CONTENT_EMPTY)
-                                       {
-                                               if(vdist(it.velocity, >, 5))
-                                                       fixedmakevectors(vectoangles(it.velocity));
-                                               else
-                                                       fixedmakevectors(it.angles);
-
-                                               for(pc = 0; pc < 4; ++pc) // test 4 diffrent spots close to mate
-                                               {
-                                                       switch(pc)
-                                                       {
-                                                               case 0:
-                                                                       tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128, MOVE_NOMONSTERS, it);
-                                                                       break;
-                                                               case 1:
-                                                                       tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 , MOVE_NOMONSTERS, it);
-                                                                       break;
-                                                               case 2:
-                                                                       tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it);
-                                                                       break;
-                                                               case 3:
-                                                                       tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it);
-                                                                       break;
-                                                               //case 4:
-                                                                       //tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 128, MOVE_NOMONSTERS, it);
-                                                                       //break;
-                                                       }
-
-                                                       if(trace_fraction == 1.0)
-                                                       {
-                                                               traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NOMONSTERS, it);
-                                                               if(trace_fraction != 1.0)
-                                                               {
-                                                                       if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
-                                                                       {
-                                                                               dist = vlen(trace_endpos - player.msnt_deathloc);
-                                                                               if(dist < best_dist || best_dist == 0)
-                                                                               {
-                                                                                       best_dist = dist;
-                                                                                       best_spot = trace_endpos;
-                                                                                       best_mate = it;
-                                                                               }
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               setorigin(player, trace_endpos);
-                                                                               player.angles = it.angles;
-                                                                               player.angles_z = 0; // never spawn tilted even if the spot says to
-                                                                               it.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
-                                                                               return;
-                                                                       }
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               ));
-
-               if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
-               if(best_dist)
-               {
-                       setorigin(player, best_spot);
-                       player.angles = best_mate.angles;
-                       player.angles_z = 0; // never spawn tilted even if the spot says to
-                       best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
-               }
-       }
-       else if(spawn_spot.msnt_lookat)
-       {
-               player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin);
-               player.angles_x = -player.angles.x;
-               player.angles_z = 0; // never spawn tilted even if the spot says to
-               /*
-               sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
-               sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n");
-               sprint(player, "angles: ", vtos(player.angles), "\n");
-               */
-       }
-}
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies)
-{
-       entity frag_target = M_ARGV(0, entity);
-
-       frag_target.msnt_deathloc = frag_target.origin;
-}
-
-REPLICATE(cvar_cl_spawn_near_teammate, bool, "cl_spawn_near_teammate");
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
new file mode 100644 (file)
index 0000000..a0017f9
--- /dev/null
@@ -0,0 +1,223 @@
+#include "sv_spawn_near_teammate.qh"
+
+const float FLOAT_MAX = 340282346638528859811704183484516925440.0f;
+
+float autocvar_g_spawn_near_teammate_distance;
+int autocvar_g_spawn_near_teammate_ignore_spawnpoint;
+int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max;
+float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
+float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
+bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
+bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
+
+REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate"));
+
+.entity msnt_lookat;
+
+.float msnt_timer;
+
+.float cvar_cl_spawn_near_teammate;
+
+MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
+{
+       entity player = M_ARGV(0, entity);
+       entity spawn_spot = M_ARGV(1, entity);
+       vector spawn_score = M_ARGV(2, vector);
+
+       if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate))
+               return;
+
+       spawn_spot.msnt_lookat = NULL;
+
+       if(!teamplay)
+               return;
+
+       RandomSelection_Init();
+       FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), LAMBDA(
+               if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance))
+                       continue;
+               if(vdist(spawn_spot.origin - it.origin, <, 48))
+                       continue;
+               if(!checkpvs(spawn_spot.origin, it))
+                       continue;
+               RandomSelection_AddEnt(it, 1, 1);
+       ));
+
+       if(RandomSelection_chosen_ent)
+       {
+               spawn_spot.msnt_lookat = RandomSelection_chosen_ent;
+               spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
+       }
+       else if(player.team == spawn_spot.team)
+               spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
+
+       M_ARGV(2, vector) = spawn_score;
+}
+
+MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
+{
+       if(!teamplay) { return; }
+       entity player = M_ARGV(0, entity);
+       entity spawn_spot = M_ARGV(1, entity);
+
+       int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0;
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               switch(it.team)
+               {
+                       case NUM_TEAM_1: ++num_red; break;
+                       case NUM_TEAM_2: ++num_blue; break;
+                       case NUM_TEAM_3: ++num_yellow; break;
+                       case NUM_TEAM_4: ++num_pink; break;
+               }
+       });
+
+       if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1)
+               return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage!
+
+       // Note: when entering this, fixangle is already set.
+       if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate))
+       {
+               if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death)
+                       player.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
+
+               entity best_mate = NULL;
+               vector best_pos = '0 0 0';
+               float best_dist2 = FLOAT_MAX;
+               int tested = 0;
+               FOREACH_CLIENT_RANDOM(IS_PLAYER(it), LAMBDA(
+                       if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_max && tested >= autocvar_g_spawn_near_teammate_ignore_spawnpoint_max) break;
+
+                       if (PHYS_INPUT_BUTTON_CHAT(it)) continue;
+                       if (!SAME_TEAM(player, it)) continue;
+                       if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && it.health < autocvar_g_balance_health_regenstable) continue;
+                       if (IS_DEAD(it)) continue;
+                       if (time < it.msnt_timer) continue;
+                       if (time < it.spawnshieldtime) continue;
+                       if (forbidWeaponUse(it)) continue;
+                       if (it == player) continue;
+
+                       tested++; // i consider a teammate to be available when he passes the checks above
+
+                       vector horiz_vel = vec2(it.velocity);
+                       // when walking slowly sideways, we assume the player wants a clear shot ahead - spawn behind him according to where he's looking
+                       // when running fast, spawn behind him according to his direction of movement to prevent colliding with the newly spawned player
+                       if (vdist(horiz_vel, >, autocvar_sv_maxspeed + 50))
+                               fixedmakevectors(vectoangles(horiz_vel));
+                       else
+                               fixedmakevectors(it.angles); // .angles is the angle of the model - usually/always 0 pitch
+
+                       // test different spots close to mate - trace upwards so it works on uneven surfaces
+                       // don't spawn in front of player or directly behind to avoid players shooting each other
+                       // test the potential spots in pairs (first pair is better than second and so on) but don't prefer one side
+                       RandomSelection_Init();
+                       for(int i = 0; i < 6; ++i)
+                       {
+                               switch(i)
+                               {
+                                       case 0:
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 64 + v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               break;
+                                       case 1:
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 64 - v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               break;
+                                       case 2:
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin + v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               break;
+                                       case 3:
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               break;
+                                       case 4:
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 128 + v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               break;
+                                       case 5:
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 128 - v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               break;
+                               }
+
+                               vector horizontal_trace_endpos = trace_endpos;
+                               //te_lightning1(NULL, it.origin, horizontal_trace_endpos);
+                               if (trace_fraction != 1.0) goto skip;
+
+                               // 400 is about the height of a typical laser jump (in overkill)
+                               // not traceline because we need space for the whole player, not just his origin
+                               tracebox(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), horizontal_trace_endpos - '0 0 400', MOVE_NORMAL, it);
+                               vector vectical_trace_endpos = trace_endpos;
+                               //te_lightning1(NULL, horizontal_trace_endpos, vectical_trace_endpos);
+                               if (trace_startsolid) goto skip; // inside another player
+                               if (trace_fraction == 1.0) goto skip; // above void or too high
+                               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) goto skip;
+                               if (pointcontents(vectical_trace_endpos) != CONTENT_EMPTY) goto skip; // no lava or slime (or water which i assume would be annoying anyway)
+                               if (tracebox_hits_trigger_hurt(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), vectical_trace_endpos)) goto skip;
+
+                               // make sure the spawned player will have floor ahead (or at least a wall - he shouldn't fall as soon as he starts moving)
+                               vector floor_test_start = vectical_trace_endpos + v_up * STAT(PL_MAX, player).z + v_forward * STAT(PL_MAX, player).x; // top front of player's bbox - highest point we know is not inside solid
+                               traceline(floor_test_start, floor_test_start + v_forward * 100 - v_up * 128, MOVE_NOMONSTERS, it);
+                               //te_beam(NULL, floor_test_start, trace_endpos);
+                               if (trace_fraction == 1.0) goto skip;
+
+                               if (autocvar_g_nades) {
+                                       bool nade_in_range = false;
+                                       IL_EACH(g_projectiles, it.classname == "nade",
+                                       {
+                                               if (vdist(it.origin - vectical_trace_endpos, <, autocvar_g_nades_nade_radius)) {
+                                                       nade_in_range = true;
+                                                       goto skip;
+                                               }
+                                       });
+                                       if (nade_in_range) goto skip;
+                               }
+
+                               // here, we know we found a good spot
+                               RandomSelection_Add(it, 0, string_null, vectical_trace_endpos, 1, 1);
+                               //te_lightning1(NULL, vectical_trace_endpos, vectical_trace_endpos + v_forward * 10);
+
+LABEL(skip)
+                               if (i % 2 == 1 && RandomSelection_chosen_ent)
+                               {
+                                       if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
+                                       {
+                                               float dist2 = vlen2(RandomSelection_chosen_ent.origin - player.death_origin);
+                                               if (dist2 < best_dist2)
+                                               {
+                                                       best_dist2 = dist2;
+                                                       best_pos = RandomSelection_chosen_vec;
+                                                       best_mate = RandomSelection_chosen_ent;
+                                               }
+                                       }
+                                       else
+                                       {
+                                               setorigin(player, RandomSelection_chosen_vec);
+                                               player.angles = RandomSelection_chosen_ent.angles;
+                                               player.angles_z = 0; // never spawn tilted even if the spot says to
+                                               RandomSelection_chosen_ent.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
+                                               return;
+                                       }
+                                       break; // don't test the other spots near this teammate, go to the next one
+                               }
+                       }
+               ));
+
+               if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
+               if(best_mate)
+               {
+                       setorigin(player, best_pos);
+                       player.angles = best_mate.angles;
+                       player.angles_z = 0; // never spawn tilted even if the spot says to
+                       best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
+               }
+       }
+       else if(spawn_spot.msnt_lookat)
+       {
+               player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin);
+               player.angles_x = -player.angles.x;
+               player.angles_z = 0; // never spawn tilted even if the spot says to
+               /*
+               sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
+               sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n");
+               sprint(player, "angles: ", vtos(player.angles), "\n");
+               */
+       }
+}
+
+REPLICATE(cvar_cl_spawn_near_teammate, bool, "cl_spawn_near_teammate");
diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d5005242f241344d6cdf4571994dadeedd36b4d2..262c7fcdbc82e5fdf1948de2674b7036d9659171 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/superspec/superspec.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/superspec/sv_superspec.qc>
+#endif
index b544ffc611594eb103ef3d61b27e119f83d514b3..110087b6749652c0c4b11a16850c7bebdb1036c8 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/superspec/superspec.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/superspec/sv_superspec.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/superspec/module.inc b/qcsrc/common/mutators/mutator/superspec/module.inc
deleted file mode 100644 (file)
index 8e0a998..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "superspec.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/superspec/superspec.qc b/qcsrc/common/mutators/mutator/superspec/superspec.qc
deleted file mode 100644 (file)
index 7867968..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(superspec, cvar("g_superspectate"));
-
-#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
-#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false)
-
-const float ASF_STRENGTH               = BIT(0);
-const float ASF_SHIELD                         = BIT(1);
-const float ASF_MEGA_AR                = BIT(2);
-const float ASF_MEGA_HP                = BIT(3);
-const float ASF_FLAG_GRAB              = BIT(4);
-const float ASF_OBSERVER_ONLY  = BIT(5);
-const float ASF_SHOWWHAT               = BIT(6);
-const float ASF_SSIM                   = BIT(7);
-const float ASF_FOLLOWKILLER   = BIT(8);
-const float ASF_ALL                    = 0xFFFFFF;
-.float autospec_flags;
-
-const float SSF_SILENT = 1;
-const float SSF_VERBOSE = 2;
-const float SSF_ITEMMSG = 4;
-.float superspec_flags;
-
-.string superspec_itemfilter; //"classname1 classname2 ..."
-
-bool superspec_Spectate(entity this, entity targ)
-{
-       if(Spectate(this, targ) == 1)
-           TRANSMUTE(Spectator, this);
-
-       return true;
-}
-
-void superspec_save_client_conf(entity this)
-{
-       string fn = "superspec-local.options";
-       float fh;
-
-       if (!_ISLOCAL(this))
-       {
-               if(this.crypto_idfp == "")
-                       return;
-
-               fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp));
-       }
-
-       fh = fopen(fn, FILE_WRITE);
-       if(fh < 0)
-       {
-               LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n");
-       }
-       else
-       {
-               fputs(fh, _SSMAGIX);
-               fputs(fh, "\n");
-               fputs(fh, ftos(this.autospec_flags));
-               fputs(fh, "\n");
-               fputs(fh, ftos(this.superspec_flags));
-               fputs(fh, "\n");
-               fputs(fh, this.superspec_itemfilter);
-               fputs(fh, "\n");
-               fclose(fh);
-       }
-}
-
-void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
-{
-       sprint(_to, strcat(_con_title, _msg));
-
-       if(_to.superspec_flags & SSF_SILENT)
-               return;
-
-       if(_spamlevel > 1)
-               if (!(_to.superspec_flags & SSF_VERBOSE))
-                       return;
-
-       centerprint(_to, strcat(_center_title, _msg));
-}
-
-float superspec_filteritem(entity _for, entity _item)
-{
-       float i;
-
-       if(_for.superspec_itemfilter == "")
-               return true;
-
-       if(_for.superspec_itemfilter == "")
-               return true;
-
-       float l = tokenize_console(_for.superspec_itemfilter);
-       for(i = 0; i < l; ++i)
-       {
-               if(argv(i) == _item.classname)
-                       return true;
-       }
-
-       return false;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
-{
-       entity item = M_ARGV(0, entity);
-       entity toucher = M_ARGV(1, entity);
-
-       FOREACH_CLIENT(true, LAMBDA(
-               if(!IS_SPEC(it) && !IS_OBSERVER(it))
-                       continue;
-               if(it.superspec_flags & SSF_ITEMMSG)
-                       if(superspec_filteritem(it, item))
-                       {
-                               if(it.superspec_flags & SSF_VERBOSE)
-                                       superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1);
-                               else
-                                       superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1);
-                               if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher)
-                               {
-                                       superspec_Spectate(it, toucher);
-                                       return MUT_ITEMTOUCH_CONTINUE;
-                               }
-                       }
-
-               if((it.autospec_flags & ASF_SHIELD && item.invincible_finished) ||
-                       (it.autospec_flags & ASF_STRENGTH && item.strength_finished) ||
-                       (it.autospec_flags & ASF_MEGA_AR && item.itemdef == ITEM_ArmorMega) ||
-                       (it.autospec_flags & ASF_MEGA_HP && item.itemdef == ITEM_HealthMega) ||
-                       (it.autospec_flags & ASF_FLAG_GRAB && item.classname == "item_flag_team"))
-               {
-
-                       if((it.enemy != toucher) || IS_OBSERVER(it))
-                       {
-                               if(it.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(it))
-                               {
-                                       if(it.superspec_flags & SSF_VERBOSE)
-                                               superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2);
-                               }
-                               else
-                               {
-                                       if(it.autospec_flags & ASF_SHOWWHAT)
-                                               superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2);
-
-                                       superspec_Spectate(it, toucher);
-                               }
-                       }
-               }
-       ));
-
-       return MUT_ITEMTOUCH_CONTINUE;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand)
-{
-#define OPTIONINFO(flag,var,test,text,long,short) \
-    var = strcat(var, ((flag & test) ? "^2[ON]  ^7" : "^1[OFF] ^7")); \
-    var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n")
-
-       if(MUTATOR_RETURNVALUE) // command was already handled?
-               return;
-
-       entity player = M_ARGV(0, entity);
-       string cmd_name = M_ARGV(1, string);
-       int cmd_argc = M_ARGV(2, int);
-
-       if(IS_PLAYER(player))
-               return;
-
-       if(cmd_name == "superspec_itemfilter")
-       {
-               if(argv(1) == "help")
-               {
-                       string _aspeco;
-                       _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n";
-                       _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n");
-                       _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n");
-                       superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1);
-               }
-               else if(argv(1) == "clear")
-               {
-                       if(player.superspec_itemfilter != "")
-                               strunzone(player.superspec_itemfilter);
-
-                       player.superspec_itemfilter = "";
-               }
-               else if(argv(1) == "show" || argv(1) == "")
-               {
-                       if(player.superspec_itemfilter == "")
-                       {
-                               superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1);
-                               return true;
-                       }
-                       float i;
-                       float l = tokenize_console(player.superspec_itemfilter);
-                       string _msg = "";
-                       for(i = 0; i < l; ++i)
-                               _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n");
-                               //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i));
-
-                       _msg = strcat(_msg,"\n");
-
-                       superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1);
-               }
-               else
-               {
-                       if(player.superspec_itemfilter != "")
-                               strunzone(player.superspec_itemfilter);
-
-                       player.superspec_itemfilter = strzone(argv(1));
-               }
-
-               return true;
-       }
-
-       if(cmd_name == "superspec")
-       {
-               string _aspeco;
-
-               if(cmd_argc > 1)
-               {
-                       float i, _bits = 0, _start = 1;
-                       if(argv(1) == "help")
-                       {
-                               _aspeco = "use cmd superspec [option] [on|off] to set options\n\n";
-                               _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n");
-                               _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n");
-                               _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n");
-                               _aspeco = strcat(_aspeco, "^7    Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n");
-                               superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1);
-                               return true;
-                       }
-
-                       if(argv(1) == "clear")
-                       {
-                               player.superspec_flags = 0;
-                               _start = 2;
-                       }
-
-                       for(i = _start; i < cmd_argc; ++i)
-                       {
-                               if(argv(i) == "on" || argv(i) == "1")
-                               {
-                                       player.superspec_flags |= _bits;
-                                       _bits = 0;
-                               }
-                               else if(argv(i) == "off" || argv(i) == "0")
-                               {
-                                       if(_start == 1)
-                                               player.superspec_flags &= ~_bits;
-
-                                       _bits = 0;
-                               }
-                               else
-                               {
-                                       if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ;
-                                       if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE;
-                                       if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG;
-                               }
-                       }
-               }
-
-               _aspeco = "";
-               OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si");
-               OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve");
-               OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im");
-
-               superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1);
-
-               return true;
-       }
-
-/////////////////////
-
-       if(cmd_name == "autospec")
-       {
-               string _aspeco;
-               if(cmd_argc > 1)
-               {
-                       if(argv(1) == "help")
-                       {
-                               _aspeco = "use cmd autospec [option] [on|off] to set options\n\n";
-                               _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
-                               _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
-                               _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
-                               _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
-                               _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
-                               _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n");
-                               _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n");
-                               _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n");
-                               _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n");
-                               _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n");
-                               superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1);
-                               return true;
-                       }
-
-                       float i, _bits = 0, _start = 1;
-                       if(argv(1) == "clear")
-                       {
-                               player.autospec_flags = 0;
-                               _start = 2;
-                       }
-
-                       for(i = _start; i < cmd_argc; ++i)
-                       {
-                               if(argv(i) == "on" || argv(i) == "1")
-                               {
-                                       player.autospec_flags |= _bits;
-                                       _bits = 0;
-                               }
-                               else if(argv(i) == "off" || argv(i) == "0")
-                               {
-                                       if(_start == 1)
-                                               player.autospec_flags &= ~_bits;
-
-                                       _bits = 0;
-                               }
-                               else
-                               {
-                                       if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
-                                       if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
-                                       if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
-                                       if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
-                                       if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
-                                       if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY;
-                                       if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT;
-                                       if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM;
-                                       if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER;
-                                       if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL;
-                               }
-                       }
-               }
-
-               _aspeco = "";
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im");
-               OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk");
-
-               superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1);
-               return true;
-       }
-
-       if(cmd_name == "followpowerup")
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && (it.strength_finished > time || it.invincible_finished > time), LAMBDA(return superspec_Spectate(player, it)));
-
-               superspec_msg("", "", player, "No active powerup\n", 1);
-               return true;
-       }
-
-       if(cmd_name == "followstrength")
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it.strength_finished > time, LAMBDA(return superspec_Spectate(player, it)));
-
-               superspec_msg("", "", player, "No active Strength\n", 1);
-               return true;
-       }
-
-       if(cmd_name == "followshield")
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it.invincible_finished > time, LAMBDA(return superspec_Spectate(player, it)));
-
-               superspec_msg("", "", player, "No active Shield\n", 1);
-               return true;
-       }
-#undef OPTIONINFO
-}
-
-MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS");
-}
-
-MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators");
-}
-
-void superspec_hello(entity this)
-{
-       if(this.enemy.crypto_idfp == "")
-               Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID);
-
-       remove(this);
-}
-
-MUTATOR_HOOKFUNCTION(superspec, ClientConnect)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(!IS_REAL_CLIENT(player))
-               return;
-
-       string fn = "superspec-local.options";
-       float fh;
-
-       player.superspec_flags = SSF_VERBOSE;
-       player.superspec_itemfilter = "";
-
-       entity _hello = spawn();
-       _hello.enemy = player;
-       setthink(_hello, superspec_hello);
-       _hello.nextthink = time + 5;
-
-       if (!_ISLOCAL(player))
-       {
-               if(player.crypto_idfp == "")
-                       return;
-
-               fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp));
-       }
-
-       fh = fopen(fn, FILE_READ);
-       if(fh < 0)
-       {
-               LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n");
-       }
-       else
-       {
-               string _magic = fgets(fh);
-               if(_magic != _SSMAGIX)
-               {
-                       LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n");
-               }
-               else
-               {
-                       player.autospec_flags = stof(fgets(fh));
-                       player.superspec_flags = stof(fgets(fh));
-                       player.superspec_itemfilter = strzone(fgets(fh));
-               }
-               fclose(fh);
-       }
-}
-
-MUTATOR_HOOKFUNCTION(superspec, PlayerDies)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-
-       FOREACH_CLIENT(IS_SPEC(it), LAMBDA(
-               if(it.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && it.enemy == frag_target)
-               {
-                       if(it.autospec_flags & ASF_SHOWWHAT)
-                               superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2);
-
-                       superspec_Spectate(it, frag_attacker);
-               }
-       ));
-}
-
-MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect)
-{
-       entity player = M_ARGV(0, entity);
-
-       superspec_save_client_conf(player);
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc
new file mode 100644 (file)
index 0000000..c423042
--- /dev/null
@@ -0,0 +1,459 @@
+#include "sv_superspec.qh"
+
+REGISTER_MUTATOR(superspec, cvar("g_superspectate"));
+
+#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
+#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false)
+
+const float ASF_STRENGTH               = BIT(0);
+const float ASF_SHIELD                         = BIT(1);
+const float ASF_MEGA_AR                = BIT(2);
+const float ASF_MEGA_HP                = BIT(3);
+const float ASF_FLAG_GRAB              = BIT(4);
+const float ASF_OBSERVER_ONLY  = BIT(5);
+const float ASF_SHOWWHAT               = BIT(6);
+const float ASF_SSIM                   = BIT(7);
+const float ASF_FOLLOWKILLER   = BIT(8);
+const float ASF_ALL                    = 0xFFFFFF;
+.float autospec_flags;
+
+const float SSF_SILENT = 1;
+const float SSF_VERBOSE = 2;
+const float SSF_ITEMMSG = 4;
+.float superspec_flags;
+
+.string superspec_itemfilter; //"classname1 classname2 ..."
+
+bool superspec_Spectate(entity this, entity targ)
+{
+       if(Spectate(this, targ) == 1)
+           TRANSMUTE(Spectator, this);
+
+       return true;
+}
+
+void superspec_save_client_conf(entity this)
+{
+       string fn = "superspec-local.options";
+       float fh;
+
+       if (!_ISLOCAL(this))
+       {
+               if(this.crypto_idfp == "")
+                       return;
+
+               fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp));
+       }
+
+       fh = fopen(fn, FILE_WRITE);
+       if(fh < 0)
+       {
+               LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.");
+       }
+       else
+       {
+               fputs(fh, _SSMAGIX);
+               fputs(fh, "\n");
+               fputs(fh, ftos(this.autospec_flags));
+               fputs(fh, "\n");
+               fputs(fh, ftos(this.superspec_flags));
+               fputs(fh, "\n");
+               fputs(fh, this.superspec_itemfilter);
+               fputs(fh, "\n");
+               fclose(fh);
+       }
+}
+
+void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
+{
+       sprint(_to, strcat(_con_title, _msg));
+
+       if(_to.superspec_flags & SSF_SILENT)
+               return;
+
+       if(_spamlevel > 1)
+               if (!(_to.superspec_flags & SSF_VERBOSE))
+                       return;
+
+       centerprint(_to, strcat(_center_title, _msg));
+}
+
+float superspec_filteritem(entity _for, entity _item)
+{
+       float i;
+
+       if(_for.superspec_itemfilter == "")
+               return true;
+
+       if(_for.superspec_itemfilter == "")
+               return true;
+
+       float l = tokenize_console(_for.superspec_itemfilter);
+       for(i = 0; i < l; ++i)
+       {
+               if(argv(i) == _item.classname)
+                       return true;
+       }
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
+{
+       entity item = M_ARGV(0, entity);
+       entity toucher = M_ARGV(1, entity);
+
+       FOREACH_CLIENT(true, LAMBDA(
+               if(!IS_SPEC(it) && !IS_OBSERVER(it))
+                       continue;
+               if(it.superspec_flags & SSF_ITEMMSG)
+                       if(superspec_filteritem(it, item))
+                       {
+                               if(it.superspec_flags & SSF_VERBOSE)
+                                       superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1);
+                               else
+                                       superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1);
+                               if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher)
+                               {
+                                       superspec_Spectate(it, toucher);
+                                       return MUT_ITEMTOUCH_CONTINUE;
+                               }
+                       }
+
+               if((it.autospec_flags & ASF_SHIELD && item.invincible_finished) ||
+                       (it.autospec_flags & ASF_STRENGTH && item.strength_finished) ||
+                       (it.autospec_flags & ASF_MEGA_AR && item.itemdef == ITEM_ArmorMega) ||
+                       (it.autospec_flags & ASF_MEGA_HP && item.itemdef == ITEM_HealthMega) ||
+                       (it.autospec_flags & ASF_FLAG_GRAB && item.classname == "item_flag_team"))
+               {
+
+                       if((it.enemy != toucher) || IS_OBSERVER(it))
+                       {
+                               if(it.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(it))
+                               {
+                                       if(it.superspec_flags & SSF_VERBOSE)
+                                               superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2);
+                               }
+                               else
+                               {
+                                       if(it.autospec_flags & ASF_SHOWWHAT)
+                                               superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2);
+
+                                       superspec_Spectate(it, toucher);
+                               }
+                       }
+               }
+       ));
+
+       return MUT_ITEMTOUCH_CONTINUE;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand)
+{
+#define OPTIONINFO(flag,var,test,text,long,short) \
+    var = strcat(var, ((flag & test) ? "^2[ON]  ^7" : "^1[OFF] ^7")); \
+    var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n")
+
+       if(MUTATOR_RETURNVALUE) // command was already handled?
+               return;
+
+       entity player = M_ARGV(0, entity);
+       string cmd_name = M_ARGV(1, string);
+       int cmd_argc = M_ARGV(2, int);
+
+       if(IS_PLAYER(player))
+               return;
+
+       if(cmd_name == "superspec_itemfilter")
+       {
+               if(argv(1) == "help")
+               {
+                       string _aspeco;
+                       _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n";
+                       _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n");
+                       _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n");
+                       superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1);
+               }
+               else if(argv(1) == "clear")
+               {
+                       if(player.superspec_itemfilter != "")
+                               strunzone(player.superspec_itemfilter);
+
+                       player.superspec_itemfilter = "";
+               }
+               else if(argv(1) == "show" || argv(1) == "")
+               {
+                       if(player.superspec_itemfilter == "")
+                       {
+                               superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1);
+                               return true;
+                       }
+                       float i;
+                       float l = tokenize_console(player.superspec_itemfilter);
+                       string _msg = "";
+                       for(i = 0; i < l; ++i)
+                               _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n");
+                               //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i));
+
+                       _msg = strcat(_msg,"\n");
+
+                       superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1);
+               }
+               else
+               {
+                       if(player.superspec_itemfilter != "")
+                               strunzone(player.superspec_itemfilter);
+
+                       player.superspec_itemfilter = strzone(argv(1));
+               }
+
+               return true;
+       }
+
+       if(cmd_name == "superspec")
+       {
+               string _aspeco;
+
+               if(cmd_argc > 1)
+               {
+                       float i, _bits = 0, _start = 1;
+                       if(argv(1) == "help")
+                       {
+                               _aspeco = "use cmd superspec [option] [on|off] to set options\n\n";
+                               _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n");
+                               _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n");
+                               _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n");
+                               _aspeco = strcat(_aspeco, "^7    Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n");
+                               superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1);
+                               return true;
+                       }
+
+                       if(argv(1) == "clear")
+                       {
+                               player.superspec_flags = 0;
+                               _start = 2;
+                       }
+
+                       for(i = _start; i < cmd_argc; ++i)
+                       {
+                               if(argv(i) == "on" || argv(i) == "1")
+                               {
+                                       player.superspec_flags |= _bits;
+                                       _bits = 0;
+                               }
+                               else if(argv(i) == "off" || argv(i) == "0")
+                               {
+                                       if(_start == 1)
+                                               player.superspec_flags &= ~_bits;
+
+                                       _bits = 0;
+                               }
+                               else
+                               {
+                                       if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ;
+                                       if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE;
+                                       if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG;
+                               }
+                       }
+               }
+
+               _aspeco = "";
+               OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si");
+               OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve");
+               OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im");
+
+               superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1);
+
+               return true;
+       }
+
+/////////////////////
+
+       if(cmd_name == "autospec")
+       {
+               string _aspeco;
+               if(cmd_argc > 1)
+               {
+                       if(argv(1) == "help")
+                       {
+                               _aspeco = "use cmd autospec [option] [on|off] to set options\n\n";
+                               _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
+                               _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
+                               _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
+                               _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
+                               _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
+                               _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n");
+                               _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n");
+                               _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n");
+                               _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n");
+                               _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n");
+                               superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1);
+                               return true;
+                       }
+
+                       float i, _bits = 0, _start = 1;
+                       if(argv(1) == "clear")
+                       {
+                               player.autospec_flags = 0;
+                               _start = 2;
+                       }
+
+                       for(i = _start; i < cmd_argc; ++i)
+                       {
+                               if(argv(i) == "on" || argv(i) == "1")
+                               {
+                                       player.autospec_flags |= _bits;
+                                       _bits = 0;
+                               }
+                               else if(argv(i) == "off" || argv(i) == "0")
+                               {
+                                       if(_start == 1)
+                                               player.autospec_flags &= ~_bits;
+
+                                       _bits = 0;
+                               }
+                               else
+                               {
+                                       if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
+                                       if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
+                                       if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
+                                       if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
+                                       if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
+                                       if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY;
+                                       if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT;
+                                       if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM;
+                                       if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER;
+                                       if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL;
+                               }
+                       }
+               }
+
+               _aspeco = "";
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im");
+               OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk");
+
+               superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1);
+               return true;
+       }
+
+       if(cmd_name == "followpowerup")
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && (it.strength_finished > time || it.invincible_finished > time), LAMBDA(return superspec_Spectate(player, it)));
+
+               superspec_msg("", "", player, "No active powerup\n", 1);
+               return true;
+       }
+
+       if(cmd_name == "followstrength")
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && it.strength_finished > time, LAMBDA(return superspec_Spectate(player, it)));
+
+               superspec_msg("", "", player, "No active Strength\n", 1);
+               return true;
+       }
+
+       if(cmd_name == "followshield")
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && it.invincible_finished > time, LAMBDA(return superspec_Spectate(player, it)));
+
+               superspec_msg("", "", player, "No active Shield\n", 1);
+               return true;
+       }
+#undef OPTIONINFO
+}
+
+MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS");
+}
+
+MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators");
+}
+
+void superspec_hello(entity this)
+{
+       if(this.enemy.crypto_idfp == "")
+               Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID);
+
+       delete(this);
+}
+
+MUTATOR_HOOKFUNCTION(superspec, ClientConnect)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(!IS_REAL_CLIENT(player))
+               return;
+
+       string fn = "superspec-local.options";
+       float fh;
+
+       player.superspec_flags = SSF_VERBOSE;
+       player.superspec_itemfilter = "";
+
+       entity _hello = spawn();
+       _hello.enemy = player;
+       setthink(_hello, superspec_hello);
+       _hello.nextthink = time + 5;
+
+       if (!_ISLOCAL(player))
+       {
+               if(player.crypto_idfp == "")
+                       return;
+
+               fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp));
+       }
+
+       fh = fopen(fn, FILE_READ);
+       if(fh < 0)
+       {
+               LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.");
+       }
+       else
+       {
+               string _magic = fgets(fh);
+               if(_magic != _SSMAGIX)
+               {
+                       LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic");
+               }
+               else
+               {
+                       player.autospec_flags = stof(fgets(fh));
+                       player.superspec_flags = stof(fgets(fh));
+                       player.superspec_itemfilter = strzone(fgets(fh));
+               }
+               fclose(fh);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(superspec, PlayerDies)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+
+       FOREACH_CLIENT(IS_SPEC(it), LAMBDA(
+               if(it.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && it.enemy == frag_target)
+               {
+                       if(it.autospec_flags & ASF_SHOWWHAT)
+                               superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2);
+
+                       superspec_Spectate(it, frag_attacker);
+               }
+       ));
+}
+
+MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect)
+{
+       entity player = M_ARGV(0, entity);
+
+       superspec_save_client_conf(player);
+}
diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 42dad3926e539193afb390660b551c6b70e97237..f341e2afe1993bd1bc0d0d179eb770dea76d7652 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/touchexplode/touchexplode.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/touchexplode/sv_touchexplode.qc>
+#endif
index ec71f52d749c7d6cc799ae3c6a03e9f21bc0e1e9..18cdcc60f698a187a2e434a2c8e142fdf6a258dc 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/touchexplode/touchexplode.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/touchexplode/sv_touchexplode.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/touchexplode/module.inc b/qcsrc/common/mutators/mutator/touchexplode/module.inc
deleted file mode 100644 (file)
index d3b0ea5..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "touchexplode.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc
new file mode 100644 (file)
index 0000000..38ac305
--- /dev/null
@@ -0,0 +1,47 @@
+#include "sv_touchexplode.qh"
+
+float autocvar_g_touchexplode_radius;
+float autocvar_g_touchexplode_damage;
+float autocvar_g_touchexplode_edgedamage;
+float autocvar_g_touchexplode_force;
+
+REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode"));
+
+.float touchexplode_time;
+
+void PlayerTouchExplode(entity p1, entity p2)
+{
+       vector org = (p1.origin + p2.origin) * 0.5;
+       org.z += (p1.mins.z + p2.mins.z) * 0.5;
+
+       sound(p1, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
+       Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1);
+
+       entity e = spawn();
+       setorigin(e, org);
+       RadiusDamage(e, NULL, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, NULL, NULL, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, NULL);
+       delete(e);
+}
+
+MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(time > player.touchexplode_time)
+       if(!gameover)
+       if(!STAT(FROZEN, player))
+       if(IS_PLAYER(player))
+       if(!IS_DEAD(player))
+       if(!IS_INDEPENDENT_PLAYER(player))
+               FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA(
+                       if(time > it.touchexplode_time)
+                       if(!STAT(FROZEN, it))
+                       if(!IS_DEAD(it))
+                       if (!IS_INDEPENDENT_PLAYER(it))
+                       if(boxesoverlap(player.absmin, player.absmax, it.absmin, it.absmax))
+                       {
+                               PlayerTouchExplode(player, it);
+                               player.touchexplode_time = it.touchexplode_time = time + 0.2;
+                       }
+               ));
+}
diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc
deleted file mode 100644 (file)
index 67e1440..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifdef IMPLEMENTATION
-float autocvar_g_touchexplode_radius;
-float autocvar_g_touchexplode_damage;
-float autocvar_g_touchexplode_edgedamage;
-float autocvar_g_touchexplode_force;
-
-REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode"));
-
-.float touchexplode_time;
-
-void PlayerTouchExplode(entity p1, entity p2)
-{
-       vector org = (p1.origin + p2.origin) * 0.5;
-       org.z += (p1.mins.z + p2.mins.z) * 0.5;
-
-       sound(p1, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
-       Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1);
-
-       entity e = spawn();
-       setorigin(e, org);
-       RadiusDamage(e, NULL, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, NULL, NULL, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, NULL);
-       remove(e);
-}
-
-MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(time > player.touchexplode_time)
-       if(!gameover)
-       if(!STAT(FROZEN, player))
-       if(IS_PLAYER(player))
-       if(!IS_DEAD(player))
-       if(!IS_INDEPENDENT_PLAYER(player))
-               FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA(
-                       if(time > it.touchexplode_time)
-                       if(!STAT(FROZEN, it))
-                       if(!IS_DEAD(it))
-                       if (!IS_INDEPENDENT_PLAYER(it))
-                       if(boxesoverlap(player.absmin, player.absmax, it.absmin, it.absmax))
-                       {
-                               PlayerTouchExplode(player, it);
-                               player.touchexplode_time = it.touchexplode_time = time + 0.2;
-                       }
-               ));
-}
-#endif
index 856ed84c463aa54dbb0fc71943fcfc4b4d8af34f..57b844451a552879e996ce53673ab242122d2e15 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/vampire/vampire.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/vampire/sv_vampire.qc>
+#endif
index 551184c77bb422d812af35d50d3dcd2e634144d8..d7d8a4143c5cd9b7b7a73308086164734760dc6c 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/vampire/vampire.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/vampire/sv_vampire.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/vampire/module.inc b/qcsrc/common/mutators/mutator/vampire/module.inc
deleted file mode 100644 (file)
index 864ea28..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "vampire.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc
new file mode 100644 (file)
index 0000000..3a435c5
--- /dev/null
@@ -0,0 +1,28 @@
+#include "sv_vampire.qh"
+
+REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib"));
+
+MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float damage_take = M_ARGV(4, float);
+
+       if(time >= frag_target.spawnshieldtime)
+       if(frag_target != frag_attacker)
+       if(!IS_DEAD(frag_target))
+       {
+               frag_attacker.health += bound(0, damage_take, frag_target.health);
+               frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Vampire");
+}
+
+MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Vampire");
+}
diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/vampire/vampire.qc b/qcsrc/common/mutators/mutator/vampire/vampire.qc
deleted file mode 100644 (file)
index d245c80..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib"));
-
-MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
-{
-       entity frag_attacker = M_ARGV(1, entity);
-       entity frag_target = M_ARGV(2, entity);
-       float damage_take = M_ARGV(4, float);
-
-       if(time >= frag_target.spawnshieldtime)
-       if(frag_target != frag_attacker)
-       if(!IS_DEAD(frag_target))
-       {
-               frag_attacker.health += bound(0, damage_take, frag_target.health);
-               frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit);
-       }
-}
-
-MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Vampire");
-}
-
-MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Vampire");
-}
-#endif
index 868a4ef3fc2e6688214aec9c10c2fab117dc1868..72b2bacb9c1796aa5addc651711c4dec14dcab26 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/vampirehook/vampirehook.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/vampirehook/sv_vampirehook.qc>
+#endif
index 5d57816c9634743c906ba032f5f78e1d0fc0106e..eaa7b320a8f5ccb9eb494fd54a9d6434ca434929 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/vampirehook/vampirehook.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/vampirehook/sv_vampirehook.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/vampirehook/module.inc b/qcsrc/common/mutators/mutator/vampirehook/module.inc
deleted file mode 100644 (file)
index 17ecf60..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "vampirehook.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc
new file mode 100644 (file)
index 0000000..e2b0f57
--- /dev/null
@@ -0,0 +1,37 @@
+#include "sv_vampirehook.qh"
+
+REGISTER_MUTATOR(vh, cvar("g_vampirehook"));
+
+bool autocvar_g_vampirehook_teamheal;
+float autocvar_g_vampirehook_damage;
+float autocvar_g_vampirehook_damagerate;
+float autocvar_g_vampirehook_health_steal;
+
+.float last_dmg;
+
+MUTATOR_HOOKFUNCTION(vh, GrappleHookThink)
+{
+       entity thehook = M_ARGV(0, entity);
+
+       entity dmgent = ((SAME_TEAM(thehook.owner, thehook.aiment) && autocvar_g_vampirehook_teamheal) ? thehook.owner : thehook.aiment);
+
+       if(IS_PLAYER(thehook.aiment))
+       if(thehook.last_dmg < time)
+       if(!STAT(FROZEN, thehook.aiment))
+       if(time >= game_starttime)
+       if(DIFF_TEAM(thehook.owner, thehook.aiment) || autocvar_g_vampirehook_teamheal)
+       if(thehook.aiment.health > 0)
+       if(autocvar_g_vampirehook_damage)
+       {
+               thehook.last_dmg = time + autocvar_g_vampirehook_damagerate;
+               thehook.owner.damage_dealt += autocvar_g_vampirehook_damage;
+               Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, thehook.origin, '0 0 0');
+               if(SAME_TEAM(thehook.owner, thehook.aiment))
+                       thehook.aiment.health = min(thehook.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
+               else
+                       thehook.owner.health = min(thehook.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
+
+               if(dmgent == thehook.owner)
+                       dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?!
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc
deleted file mode 100644 (file)
index a54dc74..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(vh, cvar("g_vampirehook"));
-
-bool autocvar_g_vampirehook_teamheal;
-float autocvar_g_vampirehook_damage;
-float autocvar_g_vampirehook_damagerate;
-float autocvar_g_vampirehook_health_steal;
-
-.float last_dmg;
-
-MUTATOR_HOOKFUNCTION(vh, GrappleHookThink)
-{
-       entity thehook = M_ARGV(0, entity);
-
-       entity dmgent = ((SAME_TEAM(thehook.owner, thehook.aiment) && autocvar_g_vampirehook_teamheal) ? thehook.owner : thehook.aiment);
-
-       if(IS_PLAYER(thehook.aiment))
-       if(thehook.last_dmg < time)
-       if(!STAT(FROZEN, thehook.aiment))
-       if(time >= game_starttime)
-       if(DIFF_TEAM(thehook.owner, thehook.aiment) || autocvar_g_vampirehook_teamheal)
-       if(thehook.aiment.health > 0)
-       if(autocvar_g_vampirehook_damage)
-       {
-               thehook.last_dmg = time + autocvar_g_vampirehook_damagerate;
-               thehook.owner.damage_dealt += autocvar_g_vampirehook_damage;
-               Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, thehook.origin, '0 0 0');
-               if(SAME_TEAM(thehook.owner, thehook.aiment))
-                       thehook.aiment.health = min(thehook.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
-               else
-                       thehook.owner.health = min(thehook.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
-
-               if(dmgent == thehook.owner)
-                       dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?!
-       }
-}
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/walljump/_mod.inc b/qcsrc/common/mutators/mutator/walljump/_mod.inc
new file mode 100644 (file)
index 0000000..4f879c8
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef GAMEQC
+    #include <common/mutators/mutator/walljump/walljump.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/walljump/_mod.qh b/qcsrc/common/mutators/mutator/walljump/_mod.qh
new file mode 100644 (file)
index 0000000..59ab515
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef GAMEQC
+       #include <common/mutators/mutator/walljump/walljump.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/walljump/walljump.qc b/qcsrc/common/mutators/mutator/walljump/walljump.qc
new file mode 100644 (file)
index 0000000..96b81a5
--- /dev/null
@@ -0,0 +1,70 @@
+#include "walljump.qh"
+
+#ifdef CSQC
+REGISTER_MUTATOR(walljump, true);
+#elif defined(SVQC)
+REGISTER_MUTATOR(walljump, cvar("g_walljump"));
+#endif
+
+#define PHYS_WALLJUMP(s)                                               STAT(WALLJUMP, s)
+#define PHYS_WALLJUMP_VELOCITY_Z_FACTOR(s)             STAT(WALLJUMP_VELOCITY_Z_FACTOR, s)
+#define PHYS_WALLJUMP_VELOCITY_XY_FACTOR(s)    STAT(WALLJUMP_VELOCITY_XY_FACTOR, s)
+#define PHYS_WALLJUMP_DELAY(s)                                         STAT(WALLJUMP_DELAY, s)
+#define PHYS_WALLJUMP_FORCE(s)                                         STAT(WALLJUMP_FORCE, s)
+
+vector PlayerTouchWall(entity this)
+{
+#define TRACE(newvec) \
+       tracebox (start, this.mins, this.maxs, (newvec), true, this); \
+       if (trace_fraction < 1 && vdist(this.origin - trace_endpos, <, dist) && trace_plane_normal_z < max_normal) \
+       if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) \
+               return trace_plane_normal;
+
+       float dist = 10, max_normal = 0.2, scaler = 100;
+       vector start = this.origin;
+       TRACE(start + v_forward * scaler)
+       TRACE(start - v_forward * scaler)
+       TRACE(start + v_right * scaler)
+       TRACE(start - v_right * scaler)
+#undef TRACE
+       return '0 0 0';
+}
+
+MUTATOR_HOOKFUNCTION(walljump, PlayerJump)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(PHYS_WALLJUMP(player))
+       if(time - STAT(LASTWJ, player) > PHYS_WALLJUMP_DELAY(player)) // can't do this on client, as it's too stupid to obey counters
+       if(!IS_ONGROUND(player))
+       if(player.move_movetype != MOVETYPE_NONE && player.move_movetype != MOVETYPE_FOLLOW && player.move_movetype != MOVETYPE_FLY && player.move_movetype != MOVETYPE_NOCLIP)
+       if(!IS_JUMP_HELD(player))
+       if(!STAT(FROZEN, player))
+       if(!IS_DEAD(player))
+       {
+               vector plane_normal = PlayerTouchWall(player);
+               
+               if(plane_normal != '0 0 0')
+               {
+                       float wj_force = PHYS_WALLJUMP_FORCE(player);
+                       float wj_xy_factor = PHYS_WALLJUMP_VELOCITY_XY_FACTOR(player);
+                       float wj_z_factor = PHYS_WALLJUMP_VELOCITY_Z_FACTOR(player);
+                       player.velocity_x += plane_normal_x * wj_force;
+                       player.velocity_x /= wj_xy_factor;
+                       player.velocity_y += plane_normal_y * wj_force;
+                       player.velocity_y /= wj_xy_factor;
+                       player.velocity_z = PHYS_JUMPVELOCITY(player) * wj_z_factor;
+                       if(PHYS_INPUT_BUTTON_CROUCH(player)) player.velocity_z *= -1;
+
+#ifdef SVQC
+                       STAT(LASTWJ, player) = time;
+                       player.oldvelocity = player.velocity;
+                       Send_Effect(EFFECT_SMOKE_RING, trace_endpos, plane_normal, 5);
+                       PlayerSound(player, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                       animdecide_setaction(player, ANIMACTION_JUMP, true);
+#endif
+
+                       M_ARGV(2, bool) = true; // multijump
+               }
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/walljump/walljump.qh b/qcsrc/common/mutators/mutator/walljump/walljump.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c74715ea714f6c2b92652a369a9e59a6d8de0973..73f22b836ce6485c9a5e9164e96bf5238a14e95e 100644 (file)
@@ -14,7 +14,6 @@ REGISTER_WAYPOINT(RaceFinish, _("Finish"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(RaceStart, _("Start"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(RaceStartFinish, _("Start"), '1 0.5 0', 1);
 
-REGISTER_WAYPOINT(Assault, _("<placeholder>"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(AssaultDefend, _("Defend"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(AssaultDestroy, _("Destroy"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(AssaultPush, _("Push"), '1 0.5 0', 1);
@@ -27,6 +26,7 @@ REGISTER_WAYPOINT(FlagBaseRed, _("Red base"), '0.8 0.8 0', 1);
 REGISTER_WAYPOINT(FlagBaseBlue, _("Blue base"), '0.8 0.8 0', 1);
 REGISTER_WAYPOINT(FlagBaseYellow, _("Yellow base"), '0.8 0.8 0', 1);
 REGISTER_WAYPOINT(FlagBasePink, _("Pink base"), '0.8 0.8 0', 1);
+REGISTER_WAYPOINT(FlagReturn, _("Return flag here"), '0 0.8 0.8', 1);
 
 REGISTER_WAYPOINT(DomNeut, _("Control point"), '0 1 1', 1);
 REGISTER_WAYPOINT(DomRed, _("Control point"), '0 1 1', 1);
index 2b28784b7cdb284e24ed71fa300f3ebf0b24c701..77c4312001de399edeaaddd1d9a1abcc7de55020 100644 (file)
@@ -1,9 +1,8 @@
-#ifndef WAYPOINTS_ALL_H
-#define WAYPOINTS_ALL_H
+#pragma once
 
 #include "waypointsprites.qh"
 
-REGISTRY(Waypoints, BITS(6))
+REGISTRY(Waypoints, BITS(7))
 #define Waypoints_from(i) _Waypoints_from(i, WP_Null)
 REGISTER_REGISTRY(Waypoints)
 REGISTRY_CHECK(Waypoints)
@@ -12,11 +11,11 @@ REGISTRY_CHECK(Waypoints)
 #define REGISTER_WAYPOINT_(id, init) REGISTER(Waypoints, WP, id, m_id, init)
 
 CLASS(Waypoint, Object)
-    ATTRIB(Waypoint, m_id, int, 0)
-    ATTRIB(Waypoint, netname, string, string_null)
-    ATTRIB(Waypoint, m_name, string, string_null)
-    ATTRIB(Waypoint, m_color, vector, '1 1 1')
-    ATTRIB(Waypoint, m_blink, int, 1)
+    ATTRIB(Waypoint, m_id, int, 0);
+    ATTRIB(Waypoint, netname, string);
+    ATTRIB(Waypoint, m_name, string);
+    ATTRIB(Waypoint, m_color, vector, '1 1 1');
+    ATTRIB(Waypoint, m_blink, int, 1);
     CONSTRUCTOR(Waypoint, string _netname, string _name, vector _color, int _blink) {
         CONSTRUCT(Waypoint);
         this.netname = _netname;
@@ -58,5 +57,3 @@ REGISTER_RADARICON(Vehicle,         1);
 REGISTER_RADARICON(Weapon,          1);
 
 #include "all.inc"
-
-#endif
diff --git a/qcsrc/common/mutators/mutator/waypoints/module.inc b/qcsrc/common/mutators/mutator/waypoints/module.inc
deleted file mode 100644 (file)
index 50bb5b4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "waypointsprites.qc"
index 5fdb7ec428535661a7f8a40ab685aa034dfe2d88..bdedf295d8b7e31a967b0b9b1f4a51248618d589 100644 (file)
@@ -1,7 +1,5 @@
 #include "waypointsprites.qh"
 
-#ifdef IMPLEMENTATION
-
 REGISTER_MUTATOR(waypointsprites, true);
 
 REGISTER_NET_LINKED(waypointsprites)
@@ -18,12 +16,16 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
         sendflags |= 0x80;
 
     int f = 0;
-    if(this.currentammo)
+    if(this.currentammo == 1)
         f |= 1; // hideable
     if(this.exteriormodeltoclient == to)
         f |= 2; // my own
+    if(this.currentammo == 2)
+        f |= 2; // radar only
 
     MUTATOR_CALLHOOK(SendWaypoint, this, to, sendflags, f);
+    sendflags = M_ARGV(2, int);
+    f = M_ARGV(3, int);
 
     WriteByte(MSG_ENTITY, sendflags);
     WriteByte(MSG_ENTITY, this.wp_extra);
@@ -98,9 +100,9 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
 #endif
 
 #ifdef CSQC
-void Ent_WaypointSprite(entity this);
+void Ent_WaypointSprite(entity this, bool isnew);
 NET_HANDLE(waypointsprites, bool isnew) {
-    Ent_WaypointSprite(this);
+    Ent_WaypointSprite(this, isnew);
     return true;
 }
 
@@ -112,7 +114,7 @@ void Ent_RemoveWaypointSprite(entity this)
 }
 
 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
-void Ent_WaypointSprite(entity this)
+void Ent_WaypointSprite(entity this, bool isnew)
 {
     int sendflags = ReadByte();
     this.wp_extra = ReadByte();
@@ -121,6 +123,10 @@ void Ent_WaypointSprite(entity this)
         this.spawntime = time;
 
     this.draw2d = Draw_WaypointSprite;
+    if (isnew) {
+               IL_PUSH(g_drawables_2d, this);
+               IL_PUSH(g_radaricons, this);
+    }
 
     InterpolateOrigin_Undo(this);
     this.iflags |= IFLAG_ORIGIN;
@@ -225,6 +231,7 @@ float spritelookupblinkvalue(entity this, string s)
             return 2;
     }
     if (s == WP_Item.netname) return Items_from(this.wp_extra).m_waypointblink;
+    if(s == WP_FlagReturn.netname) return 2;
 
     return 1;
 }
@@ -275,10 +282,10 @@ void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, ve
 
     // rotate them, and make them absolute
     rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
-    v1 = rotate(v1, rot) + org;
-    v2 = rotate(v2, rot) + org;
-    v3 = rotate(v3, rot) + org;
-    v4 = rotate(v4, rot) + org;
+    v1 = Rotate(v1, rot) + org;
+    v2 = Rotate(v2, rot) + org;
+    v3 = Rotate(v3, rot) + org;
+    v4 = Rotate(v4, rot) + org;
 
     // draw them
     R_BeginPolygon(pic, f);
@@ -312,9 +319,9 @@ void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, fl
     up = '0 1 0';
 
     rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
-    o = rotate(o, rot) + org;
-    ri = rotate(ri, rot);
-    up = rotate(up, rot);
+    o = Rotate(o, rot) + org;
+    ri = Rotate(ri, rot);
+    up = Rotate(up, rot);
 
     owidth = width + 2 * border;
     o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
@@ -341,19 +348,19 @@ vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
 
     R_BeginPolygon("", DRAWFLAG_NORMAL);
     R_PolygonVertex(o,                                  '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(arrowY  - borderX, ang), '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(arrowY  + borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(arrowY  - borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(arrowY  + borderX, ang), '0 0 0', '0 0 0', a);
     R_EndPolygon();
 
     R_BeginPolygon("", DRAWFLAG_ADDITIVE);
-    R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
-    R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
-    R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
+    R_PolygonVertex(o + Rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
+    R_PolygonVertex(o + Rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
+    R_PolygonVertex(o + Rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
     R_EndPolygon();
 
-    return o + rotate(eY * (borderDiag+size+margin), ang);
+    return o + Rotate(eY * (borderDiag+size+margin), ang);
 }
 
 // returns location of sprite healthbar
@@ -452,7 +459,7 @@ vector fixrgbexcess(vector rgb)
 
 void Draw_WaypointSprite(entity this)
 {
-    if (this.lifetime)
+    if (this.lifetime > 0)
         this.alpha = pow(bound(0, (this.fadetime - time) / this.lifetime, 1), waypointsprite_timealphaexponent);
     else
         this.alpha = 1;
@@ -463,14 +470,12 @@ void Draw_WaypointSprite(entity this)
     if (autocvar_cl_hidewaypoints >= 2)
         return;
 
-    if (this.hideflags & 1)
-        if (autocvar_cl_hidewaypoints)
-            return; // fixed waypoint
+    if (this.hideflags & 1 && autocvar_cl_hidewaypoints)
+        return; // fixed waypoint
 
     InterpolateOrigin_Do(this);
 
     float t = entcs_GetTeam(player_localnum) + 1;
-
     string spriteimage = "";
 
     // choose the sprite
@@ -479,7 +484,7 @@ void Draw_WaypointSprite(entity this)
         case SPRITERULE_SPECTATOR:
             if (!(
                 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
-            || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
+            || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2))
                 ))
                 return;
             spriteimage = this.netname;
@@ -513,11 +518,8 @@ void Draw_WaypointSprite(entity this)
 
     ++waypointsprite_newcount;
 
-    float dist;
-    dist = vlen(this.origin - view_origin);
-
-    float a;
-    a = this.alpha * autocvar_hud_panel_fg_alpha;
+    float dist = vlen(this.origin - view_origin);
+    float a = this.alpha * autocvar_hud_panel_fg_alpha;
 
     if (this.maxdistance > waypointsprite_normdistance)
         a *= pow(bound(0, (this.maxdistance - dist) / (this.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
@@ -535,7 +537,7 @@ void Draw_WaypointSprite(entity this)
     {
         if (this.helpme && time < this.helpme)
             a *= SPRITE_HELPME_BLINK;
-        else if (!this.lifetime) // fading out waypoints don't blink
+        else if (this.lifetime > 0) // fading out waypoints don't blink
             a *= spritelookupblinkvalue(this, spriteimage);
     }
 
@@ -609,11 +611,9 @@ void Draw_WaypointSprite(entity this)
     (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
     (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
 
-    float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
-
     float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
 
-    t = waypointsprite_scale * vidscale;
+    t = waypointsprite_scale;
     a *= waypointsprite_alpha;
 
     {
@@ -861,9 +861,10 @@ void WaypointSprite_FadeOutIn(entity e, float t)
         // ensure:
         //   (e.teleport_time - time) / wp.fade_time stays
         //   e.teleport_time = time + fadetime
-        float current_fadetime;
-        current_fadetime = e.teleport_time - time;
+        float current_fadetime = e.teleport_time - time;
         e.teleport_time = time + t;
+        if (e.fade_time < 0)
+               e.fade_time = -e.fade_time;
         e.fade_time = e.fade_time * t / current_fadetime;
     }
 
@@ -881,7 +882,7 @@ void WaypointSprite_Kill(entity wp)
 {
     if (!wp) return;
     if (wp.owner) wp.owner.(wp.owned_by_field) = NULL;
-    remove(wp);
+    delete(wp);
 }
 
 void WaypointSprite_Disown(entity wp, float fadetime)
@@ -932,7 +933,7 @@ bool WaypointSprite_visible_for_player(entity this, entity player, entity view)
     {
         if (!autocvar_sv_itemstime)
             return false;
-        if (!warmup_stage && IS_PLAYER(view))
+        if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
             return false;
     }
     else if (this.team && this.rule == SPRITERULE_DEFAULT)
@@ -963,17 +964,17 @@ float WaypointSprite_isteammate(entity e, entity e2)
     return e2 == e;
 }
 
-float WaypointSprite_Customize(entity this)
+bool WaypointSprite_Customize(entity this, entity client)
 {
     // this is not in SendEntity because it shall run every frame, not just every update
 
     // make spectators see what the player would see
-    entity e = WaypointSprite_getviewentity(other);
+    entity e = WaypointSprite_getviewentity(client);
 
-    if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, other))
+    if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client))
         return false;
 
-    return this.waypointsprite_visible_for_player(this, other, e);
+    return this.waypointsprite_visible_for_player(this, client, e);
 }
 
 bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
@@ -997,8 +998,10 @@ entity WaypointSprite_Spawn(
 )
 {
     entity wp = new(sprite_waypoint);
+    wp.fade_time = _lifetime; // if negative tells client not to fade it out
+    if(_lifetime < 0)
+       _lifetime = -_lifetime;
     wp.teleport_time = time + _lifetime;
-    wp.fade_time = _lifetime;
     wp.exteriormodeltoclient = ref;
     if (ref)
     {
@@ -1014,7 +1017,7 @@ entity WaypointSprite_Spawn(
     if (own)
     {
         if (own.(ownfield))
-            remove(own.(ownfield));
+            delete(own.(ownfield));
         own.(ownfield) = wp;
         wp.owned_by_field = ownfield;
     }
@@ -1142,4 +1145,3 @@ void WaypointSprite_PlayerGone(entity this)
     WaypointSprite_DetachCarrier(this);
 }
 #endif
-#endif
index 7fb578f82438477dd41d19c4aa1edebb4a0978d6..62a7cac61123efc52393d6f814c48521612a4eee 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef WAYPOINTSPRITES_H
-#define WAYPOINTSPRITES_H
+#pragma once
 
 #include "all.qh"
 
@@ -74,8 +73,8 @@ float waypointsprite_distancefadescale;
 float waypointsprite_distancefadedistance;
 float waypointsprite_alpha;
 
-const float SPRITE_HEALTHBAR_WIDTH = 144;
-const float SPRITE_HEALTHBAR_HEIGHT = 9;
+const float SPRITE_HEALTHBAR_WIDTH = 104;
+const float SPRITE_HEALTHBAR_HEIGHT = 7;
 const float SPRITE_HEALTHBAR_MARGIN = 6;
 const float SPRITE_HEALTHBAR_BORDER = 2;
 const float SPRITE_HEALTHBAR_BORDERALPHA = 1;
@@ -108,7 +107,7 @@ vector fixrgbexcess(vector rgb);
 
 void Ent_RemoveWaypointSprite(entity this);
 
-void Ent_WaypointSprite(entity this);
+void Ent_WaypointSprite(entity this, bool isnew);
 
 void WaypointSprite_Load_Frames(string ext);
 
@@ -118,6 +117,8 @@ void Draw_WaypointSprite(entity this);
 #endif
 
 #ifdef SVQC
+.entity sprite;
+
 float autocvar_sv_waypointsprite_deadlifetime;
 float autocvar_sv_waypointsprite_deployed_lifetime;
 float autocvar_sv_waypointsprite_limitedrange;
@@ -168,7 +169,7 @@ entity WaypointSprite_getviewentity(entity e);
 
 float WaypointSprite_isteammate(entity e, entity e2);
 
-float WaypointSprite_Customize(entity this);
+bool WaypointSprite_Customize(entity this, entity client);
 
 bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
 
@@ -234,5 +235,3 @@ void WaypointSprite_PlayerDead(entity this);
 
 void WaypointSprite_PlayerGone(entity this);
 #endif
-
-#endif
index 742510b883501ca97946c1b851ed4ac6aa148b72..034b51d1177e3d3faab54e27b54c3ddffa75a7cc 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/weaponarena_random/weaponarena_random.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc>
+#endif
index 68d6a24c4255ba1385aed7cd5f7c6e7d968f04f1..05b87c73d33471db8648db0d42a448bb7964d127 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <common/mutators/mutator/weaponarena_random/weaponarena_random.qh>
+#ifdef SVQC
+    #include <common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc
deleted file mode 100644 (file)
index b7a5f66..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifdef SVQC
-#include "weaponarena_random.qc"
-#endif
diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc
new file mode 100644 (file)
index 0000000..7ac4504
--- /dev/null
@@ -0,0 +1,14 @@
+#include "sv_weaponarena_random.qh"
+
+// WEAPONTODO: rename the cvars
+REGISTER_MUTATOR(weaponarena_random, true);
+
+MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn)
+{
+    if (!g_weaponarena_random) return;
+    entity player = M_ARGV(0, entity);
+
+    if (g_weaponarena_random_with_blaster) player.weapons &= ~WEPSET(BLASTER);
+    W_RandomWeapons(player, g_weaponarena_random);
+    if (g_weaponarena_random_with_blaster) player.weapons |= WEPSET(BLASTER);
+}
diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc
deleted file mode 100644 (file)
index e4d400d..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifdef IMPLEMENTATION
-// WEAPONTODO: rename the cvars
-REGISTER_MUTATOR(weaponarena_random, true);
-
-MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn)
-{
-    if (!g_weaponarena_random) return;
-    entity player = M_ARGV(0, entity);
-
-    if (g_weaponarena_random_with_blaster) player.weapons &= ~WEPSET(BLASTER);
-    W_RandomWeapons(player, g_weaponarena_random);
-    if (g_weaponarena_random_with_blaster) player.weapons |= WEPSET(BLASTER);
-}
-
-#endif
diff --git a/qcsrc/common/net_linked.qh b/qcsrc/common/net_linked.qh
new file mode 100644 (file)
index 0000000..9cd2094
--- /dev/null
@@ -0,0 +1,55 @@
+#pragma once
+
+REGISTER_NET_TEMP(TE_CSQC_PICTURE)
+REGISTER_NET_TEMP(TE_CSQC_RACE)
+REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER)
+REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT)
+REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN)
+REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP)
+
+const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
+const int RACE_NET_CHECKPOINT_CLEAR = 1;
+const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder
+const int RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent
+const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent
+const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder
+const int RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason
+const int RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason
+const int RACE_NET_SERVER_RECORD = 8; // server record, sent to client
+const int RACE_NET_SPEED_AWARD = 9; // speed award, sent to client
+const int RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client
+const int RACE_NET_SERVER_RANKINGS = 11;
+const int RACE_NET_SERVER_STATUS = 12;
+
+REGISTER_NET_LINKED(_ENT_CLIENT_INIT)
+#ifdef CSQC
+NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { make_pure(this); return true; }
+#endif
+/** Sent as a temp entity from a persistent linked entity */
+REGISTER_NET_TEMP(ENT_CLIENT_INIT)
+
+REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO)
+REGISTER_NET_LINKED(ENT_CLIENT_SCORES)
+REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES)
+REGISTER_NET_LINKED(ENT_CLIENT_NAGGER) // flags [votecalledvote]
+REGISTER_NET_LINKED(ENT_CLIENT_RADARLINK) // flags [startorigin] [endorigin] [startcolor+16*endcolor]
+REGISTER_NET_LINKED(ENT_CLIENT_PROJECTILE)
+REGISTER_NET_LINKED(ENT_CLIENT_MAPVOTE)
+REGISTER_NET_LINKED(ENT_CLIENT_CLIENTDATA)
+REGISTER_NET_LINKED(ENT_CLIENT_RANDOMSEED)
+REGISTER_NET_LINKED(ENT_CLIENT_ACCURACY)
+REGISTER_NET_LINKED(ENT_CLIENT_ELIMINATEDPLAYERS)
+
+REGISTER_NET_LINKED(ENT_CLIENT_MODEL)
+
+REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE)
+REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_CAMERA)
+REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_TELEPORTED)
+
+REGISTER_NET_LINKED(ENT_CLIENT_ARC_BEAM)
+REGISTER_NET_LINKED(ENT_CLIENT_HOOK)
+REGISTER_NET_LINKED(ENT_CLIENT_TUBANOTE)
+
+REGISTER_NET_LINKED(ENT_CLIENT_SPAWNPOINT)
+REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT)
+REGISTER_NET_LINKED(ENT_CLIENT_WALL)
index 1e1726ae452fe716041d291f30581eb3a1d3cab9..20d1351ebcae9475043db8b86de15a2df2df370d 100644 (file)
@@ -41,24 +41,30 @@ NET_HANDLE(TE_CSQC_SVNOTICE, bool isNew)
        return true;
 }
 entity cl_notices;
-STATIC_INIT(cl_notice)
-{
-    cl_notices = LL_NEW();
-}
 void cl_notice_read()
 {
     entity _notice = new_pure(sv_notice);
     _notice.netname = strzone(ReadString());
     _notice.alpha = ReadLong() + time;
     _notice.skin = ReadByte();
+    if(!cl_notices)
+       cl_notices = LL_NEW();
     LL_PUSH(cl_notices, _notice);
 }
 
 void cl_notice_run()
 {
+       if (!cl_notices)
+               return;
+
     bool flag = false;
     LL_EACH(cl_notices, it.alpha > time, { flag = true; break; });
-    if (!flag) return;
+       if (!flag)
+       {
+               LL_DELETE(cl_notices);
+               return;
+       }
+
     const int M1 = 30;
     const int M2 = 10;
 
@@ -76,10 +82,18 @@ void cl_notice_run()
     vector v3 = v1 + '10 10 0';
     #define OUT(s, z) MACRO_BEGIN { drawcolorcodedstring(v3, s, '1 1 0' * z, 1, DRAWFLAG_NORMAL); v3.y += z + 4; } MACRO_END
 
+    float cur_time = 0;
+    float time_width = 48;
     OUT(_("^1Server notices:"), 32);
     LL_EACH(cl_notices, it.alpha > time, {
-        string s = sprintf(_("^7%s (^3%d sec left)"), it.netname , rint(it.alpha - time));
-        OUT(s, 16);
+        if(it.alpha - cur_time > 0.1)
+        {
+            cur_time = it.alpha;
+            string s = sprintf("^3%d", ceil(cur_time - time));
+            drawcolorcodedstring(v3 + eX * 0.5 * (time_width - stringwidth(s, true, '1 1 0' * 16)), s, '1 1 0' * 16, 1, DRAWFLAG_NORMAL);
+            v3.x = v1.x + 10 + time_width;
+        }
+        OUT(it.netname, 16);
     });
     #undef OUT
 }
index b36f631fe2a17676c452e272f8176290d09d29eb..1134205185474bda2be0142568448d3e711ac494 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef NET_NOTICE_H
-#define NET_NOTICE_H
+#pragma once
 
 #ifdef SVQC
 string autocvar_sv_join_notices;
@@ -13,5 +12,3 @@ void sv_notice_join(entity _to);
 #ifdef CSQC
 void cl_notice_read();
 #endif
-
-#endif
index 42cf6a21493376c6aab2ff9a4013bc0bfc8d902f..92fd5df1ac37330b1479044e7ef1cb61280d3930 100644 (file)
 
     MSG_INFO_NOTIF(MONSTERS_DISABLED,                       1,  0, 0, "", "",           "",     _("^BGMonsters are currently disabled"), "")
 
+    MULTITEAM_INFO(NEXBALL_RETURN_HELD, 4,                  1,  0, 0, "", "",           "",     _("^BGThe ^TC^TT^BG team held the ball for too long"), "", NAME)
+
     MSG_INFO_NOTIF(ONSLAUGHT_CAPTURE,                       1,  2, 0, "s1 s2", "",      "",     _("^BG%s^BG captured %s^BG control point"), "")
     MULTITEAM_INFO(ONSLAUGHT_CPDESTROYED, 4,                1,  2, 0, "s1 s2", "",      "",     _("^TC^TT^BG team %s^BG control point has been destroyed by %s"), "", NAME)
     MULTITEAM_INFO(ONSLAUGHT_GENDESTROYED, 4,               1,  0, 0, "", "",           "",     _("^TC^TT^BG generator has been destroyed"), "", GENERATOR)
     MSG_INFO_NOTIF(WEAPON_SEEKER_MURDER_SPRAY,              1,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponseeker",             _("^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s"), "")
     MSG_INFO_NOTIF(WEAPON_SEEKER_MURDER_TAG,                1,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponseeker",             _("^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s"), "")
     MSG_INFO_NOTIF(WEAPON_SEEKER_SUICIDE,                   1,  2, 1, "s1 s2loc spree_lost", "s1",                      "weaponseeker",             _("^BG%s^K1 played with tiny Seeker rockets%s%s"), "")
-    MSG_INFO_NOTIF(WEAPON_SHOCKWAVE_MURDER,                 1,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponshotgun",            _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "")
+    MSG_INFO_NOTIF(WEAPON_SHOCKWAVE_MURDER,                 1,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponshockwave",          _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "")
     MSG_INFO_NOTIF(WEAPON_SHOCKWAVE_MURDER_SLAP,            1,  3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1",       "notify_melee_shotgun",     _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s"), "")
     MSG_INFO_NOTIF(WEAPON_SHOTGUN_MURDER,                   1,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",       "weaponshotgun",            _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s"), "")
     MSG_INFO_NOTIF(WEAPON_SHOTGUN_MURDER_SLAP,              1,  3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1",       "notify_melee_shotgun",     _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s"), "")
 
     MSG_CENTER_NOTIF(ASSAULT_ATTACKING,                 1,      0, 0, "",               CPID_ASSAULT_ROLE,      "0 0",  _("^BGYou are attacking!"), "")
     MSG_CENTER_NOTIF(ASSAULT_DEFENDING,                 1,      0, 0, "",               CPID_ASSAULT_ROLE,      "0 0",  _("^BGYou are defending!"), "")
+    MSG_CENTER_NOTIF(ASSAULT_OBJ_DESTROYED,             1,      0, 1, "f1time",         CPID_ASSAULT_ROLE,      "0 0",  _("^BGObjective destroyed in ^F4%s^BG!"), "")
 
     MSG_CENTER_NOTIF(COUNTDOWN_BEGIN,                   1,      0, 0, "",               CPID_ROUND,             "2 0",  _("^F4Begin!"), "")
     MSG_CENTER_NOTIF(COUNTDOWN_GAMESTART,               1,      0, 1, "",               CPID_ROUND,             "1 f1", _("^F4Game starts in ^COUNT"), "")
     MSG_CENTER_NOTIF(CTF_PASS_OTHER_NEUTRAL,            1,      2, 0, "s1 s2",          CPID_CTF_PASS,          "0 0",  _("^BG%s^BG passed the flag to %s"), "")
     MULTITEAM_CENTER(CTF_PASS_RECEIVED, 4,              1,      1, 0, "s1",             CPID_CTF_PASS,          "0 0",  _("^BGYou received the ^TC^TT^BG flag from %s"), "", FLAG)
     MSG_CENTER_NOTIF(CTF_PASS_RECEIVED_NEUTRAL,         1,      1, 0, "s1",             CPID_CTF_PASS,          "0 0",  _("^BGYou received the flag from %s"), "")
-    MSG_CENTER_NOTIF(CTF_PASS_REQUESTED,                1,      1, 0, "s1 pass_key",    CPID_CTF_PASS,          "0 0",  _("^BG%s^BG requests you to pass the flag%s"), "")
+    MSG_CENTER_NOTIF(CTF_PASS_REQUESTED,                1,      1, 0, "pass_key s1",    CPID_CTF_PASS,          "0 0",  _("^BGPress ^F2%s^BG to receive the flag from %s^BG"), "")
     MSG_CENTER_NOTIF(CTF_PASS_REQUESTING,               1,      1, 0, "s1",             CPID_CTF_PASS,          "0 0",  _("^BGRequesting %s^BG to pass you the flag"), "")
     MULTITEAM_CENTER(CTF_PASS_SENT, 4,                  1,      1, 0, "s1",             CPID_CTF_PASS,          "0 0",  _("^BGYou passed the ^TC^TT^BG flag to %s"), "", FLAG)
     MSG_CENTER_NOTIF(CTF_PASS_SENT_NEUTRAL,             1,      1, 0, "s1",             CPID_CTF_PASS,          "0 0",  _("^BGYou passed the flag to %s"), "")
     MULTITEAM_CENTER(CTF_PICKUP_TEAM_VERBOSE, 4,        1,      2, 0, "s1 s2 s1",       CPID_CTF_LOWPRIO,       "0 0",  _("^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!"), "", FLAG)
     MSG_CENTER_NOTIF(CTF_PICKUP_TEAM_NEUTRAL,           1,      1, 0, "s1",             CPID_CTF_LOWPRIO,       "0 0",  _("^BGYour %steam mate^BG got the flag! Protect them!"), "")
     MSG_CENTER_NOTIF(CTF_PICKUP_TEAM_VERBOSE_NEUTRAL,   1,      2, 0, "s1 s2 s1",       CPID_CTF_LOWPRIO,       "0 0",  _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "")
+    MSG_CENTER_NOTIF(CTF_PICKUP_VISIBLE,                1,      0, 0, "",               CPID_STALEMATE,         "0 0",  _("^BGEnemies can now see you on radar!"), "")
     MULTITEAM_CENTER(CTF_RETURN, 4,                     1,      0, 0, "",               CPID_CTF_LOWPRIO,       "0 0",  _("^BGYou returned the ^TC^TT^BG flag!"), "", FLAG)
     MSG_CENTER_NOTIF(CTF_STALEMATE_CARRIER,             1,      0, 0, "",               CPID_STALEMATE,         "0 0",  _("^BGStalemate! Enemies can now see you on radar!"), "")
     MSG_CENTER_NOTIF(CTF_STALEMATE_OTHER,               1,      0, 0, "",               CPID_STALEMATE,         "0 0",  _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "")
 
-    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG,                 1,      1, 1, "spree_cen s1",               CPID_Null,  "0 0",  _("^K3%sYou fragged ^BG%s"), _("^K3%sYou scored against ^BG%s"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED,              1,      1, 1, "spree_cen s1",               CPID_Null,  "0 0",  _("^K1%sYou were fragged by ^BG%s"), _("^K1%sYou were scored against by ^BG%s"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_VERBOSE,      1,      1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  _("^K1%sYou were fragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^BG%s"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_VERBOSE,         1,      1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  _("^K3%sYou fragged ^BG%s^BG%s"), _("^K3%sYou scored against ^BG%s^BG%s"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG,             1,      1, 1, "spree_cen s1",               CPID_Null,  "0 0",  _("^K1%sYou typefragged ^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED,          1,      1, 1, "spree_cen s1",               CPID_Null,  "0 0",  _("^K1%sYou were typefragged by ^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing!"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED_VERBOSE,  1,      1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  _("^K1%sYou were typefragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing^BG%s"))
-    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG_VERBOSE,     1,      1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  _("^K1%sYou typefragged ^BG%s^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing^BG%s"))
-
-    MSG_CENTER_NOTIF(NADE_THROW,                        1,      0, 0, "",               CPID_NADES,             "0 0",  _("^BGPress ^F2DROPWEAPON^BG again to toss the nade!"), "")
+    #define VERBOSE_MURDER(type) strcat(MURDER_##type, "^BG%s")
+
+    #define MURDER_FRAG             _("^K3%sYou fragged ^BG%s")
+    #define MURDER_FRAG2            _("^K3%sYou scored against ^BG%s")
+    #define MURDER_FRAGGED          _("^K1%sYou were fragged by ^BG%s")
+    #define MURDER_FRAGGED2         _("^K1%sYou were scored against by ^BG%s")
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG,                   1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAG,                    MURDER_FRAG2                   )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED,                1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAGGED,                 MURDER_FRAGGED2                )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_VERBOSE,        1,    1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  VERBOSE_MURDER(FRAGGED),        VERBOSE_MURDER(FRAGGED2)       )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_VERBOSE,           1,    1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  VERBOSE_MURDER(FRAG),           VERBOSE_MURDER(FRAG2)          )
+
+    #define MURDER_FRAG_FIRE        _("^K3%sYou burned ^BG%s")
+    #define MURDER_FRAG_FIRE2       _("^K3%sYou scored against ^BG%s")
+    #define MURDER_FRAGGED_FIRE     _("^K1%sYou were burned by ^BG%s")
+    #define MURDER_FRAGGED_FIRE2    _("^K1%sYou were scored against by ^BG%s")
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FIRE,              1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAG_FIRE,               MURDER_FRAG_FIRE2              )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FIRE,           1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAGGED_FIRE,            MURDER_FRAGGED_FIRE2           )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FIRE_VERBOSE,   1,    1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  VERBOSE_MURDER(FRAGGED_FIRE),   VERBOSE_MURDER(FRAGGED_FIRE2)  )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FIRE_VERBOSE,      1,    1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  VERBOSE_MURDER(FRAG_FIRE),      VERBOSE_MURDER(FRAG_FIRE2)     )
+
+    #define MURDER_FRAG_FREEZE      _("^K3%sYou froze ^BG%s")
+    #define MURDER_FRAG_FREEZE2     _("^K3%sYou scored against ^BG%s")
+    #define MURDER_FRAGGED_FREEZE   _("^K1%sYou were frozen by ^BG%s")
+    #define MURDER_FRAGGED_FREEZE2  _("^K1%sYou were scored against by ^BG%s")
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FREEZE,            1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAG_FREEZE,             MURDER_FRAG_FREEZE2            )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FREEZE,         1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_FRAGGED_FREEZE,          MURDER_FRAGGED_FREEZE2         )
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FREEZE_VERBOSE, 1,    1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  VERBOSE_MURDER(FRAGGED_FREEZE), VERBOSE_MURDER(FRAGGED_FREEZE2))
+    MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FREEZE_VERBOSE,    1,    1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  VERBOSE_MURDER(FRAG_FREEZE),    VERBOSE_MURDER(FRAG_FREEZE2)   )
+
+    #define MURDER_TYPEFRAG         _("^K1%sYou typefragged ^BG%s")
+    #define MURDER_TYPEFRAG2        _("^K1%sYou scored against ^BG%s^K1 while they were typing")
+    #define MURDER_TYPEFRAGGED      _("^K1%sYou were typefragged by ^BG%s")
+    #define MURDER_TYPEFRAGGED2     _("^K1%sYou were scored against by ^BG%s^K1 while typing")
+    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG,               1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_TYPEFRAG,                MURDER_TYPEFRAG2               )
+    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED,            1,    1, 1, "spree_cen s1",               CPID_Null,  "0 0",  MURDER_TYPEFRAGGED,             MURDER_TYPEFRAGGED2            )
+    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED_VERBOSE,    1,    1, 4, "spree_cen s1 frag_stats",    CPID_Null,  "0 0",  VERBOSE_MURDER(TYPEFRAGGED),    VERBOSE_MURDER(TYPEFRAGGED2)   )
+    MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG_VERBOSE,       1,    1, 2, "spree_cen s1 frag_ping",     CPID_Null,  "0 0",  VERBOSE_MURDER(TYPEFRAG),       VERBOSE_MURDER(TYPEFRAG2)      )
+
+    MSG_CENTER_NOTIF(NADE_THROW,                        1,      0, 0, "nade_key",       CPID_NADES,             "0 0",  _("^BGPress ^F2%s^BG again to toss the nade!"), "")
     MSG_CENTER_NOTIF(NADE_BONUS,                        1,      0, 0, "",               CPID_NADES,             "0 0",  _("^F2You got a ^K1BONUS GRENADE^F2!"), "")
 
     MSG_CENTER_NOTIF(DEATH_SELF_AUTOTEAMCHANGE,         1,      0, 1, "death_team",     CPID_Null,              "0 0",  _("^BGYou have been moved into a different team\nYou are now on: %s"), "")
 
     MSG_CENTER_NOTIF(EXTRALIVES,                        1,      0, 0, "",               CPID_Null,              "0 0",  _("^F2You picked up some extra lives"), "")
 
-    MSG_CENTER_NOTIF(FREEZETAG_FREEZE,                  1,      1, 0, "s1",             CPID_Null,              "0 0",  _("^K3You froze ^BG%s"), "")
-    MSG_CENTER_NOTIF(FREEZETAG_FROZEN,                  1,      1, 0, "s1",             CPID_Null,              "0 0",  _("^K1You were frozen by ^BG%s"), "")
     MSG_CENTER_NOTIF(FREEZETAG_REVIVE,                  1,      1, 0, "s1",             CPID_Null,              "0 0",  _("^K3You revived ^BG%s"), "")
     MSG_CENTER_NOTIF(FREEZETAG_REVIVE_SELF,             1,      0, 0, "",               CPID_Null,              "0 0",  _("^K3You revived yourself"), "")
     MSG_CENTER_NOTIF(FREEZETAG_REVIVED,                 1,      1, 0, "s1",             CPID_Null,              "0 0",  _("^K3You were revived by ^BG%s"), "")
     MSG_CENTER_NOTIF(NIX_COUNTDOWN,                     1,      0, 2, "item_wepname",   CPID_NIX,               "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "")
     MSG_CENTER_NOTIF(NIX_NEWWEAPON,                     1,      0, 1, "item_wepname",   CPID_NIX,               "0 0",  _("^F2Active weapon: ^F1%s"), "")
 
-    MSG_CENTER_NOTIF(NADE,                              1,      0, 0, "",               CPID_Null,              "0 0",  _("^BGPress ^F2DROPWEAPON^BG again to toss the grenade!"), "")
-
     MSG_CENTER_NOTIF(ONS_CAPTURE,                       1,      1, 0, "s1",             CPID_ONSLAUGHT,         "0 0",  _("^BGYou captured %s^BG control point"), "")
     MULTITEAM_CENTER(ONS_CAPTURE, 4,                    1,      1, 0, "s1",             CPID_ONSLAUGHT,         "0 0",  _("^TC^TT^BG team captured %s^BG control point"), "", NAME)
     MSG_CENTER_NOTIF(ONS_CONTROLPOINT_SHIELDED,         1,      0, 0, "",               CPID_ONS_CAPSHIELD,     "0 0",  _("^BGThis control point currently cannot be captured"), "")
     MSG_CENTER_NOTIF(ONS_GENERATOR_SHIELDED,            1,      0, 0, "",               CPID_ONS_CAPSHIELD,     "0 0",  _("^BGThe enemy generator cannot be destroyed yet\n^F2Capture some control points to unshield it"), "")
     MULTITEAM_CENTER(ONS_NOTSHIELDED, 4,                1,      0, 0, "",               CPID_ONSLAUGHT,         "0 0",  _("^BGThe ^TCenemy^BG generator is no longer shielded!"), "", NAME)
     MSG_CENTER_NOTIF(ONS_NOTSHIELDED_TEAM,              1,      0, 0, "",               CPID_ONSLAUGHT,         "0 0",  _("^K1Your generator is NOT shielded!\n^BGRe-capture control points to shield it!"), "")
-    MSG_CENTER_NOTIF(ONS_TELEPORT,                      1,      0, 0, "pass_key",       CPID_ONSLAUGHT,         "0 0",  _("^BGPress ^F2DROPFLAG%s^BG to teleport"), "")
+    MSG_CENTER_NOTIF(ONS_TELEPORT,                      1,      0, 0, "pass_key",       CPID_ONSLAUGHT,         "0 0",  _("^BGPress ^F2%s^BG to teleport"), "")
     MSG_CENTER_NOTIF(ONS_TELEPORT_ANTISPAM,             1,      0, 1, "f1secs",         CPID_ONSLAUGHT,         "0 0",  _("^BGTeleporting disabled for %s"), "")
 
     MSG_CENTER_NOTIF(OVERTIME_FRAG,                     1,      0, 0, "",               CPID_OVERTIME,          "0 0",  _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!"))
     MSG_CENTER_NOTIF(TEAMCHANGE_SUICIDE,                1,      0, 1, "",               CPID_TEAMCHANGE,        "1 f1", _("^K1Suicide in ^COUNT"), "")
 
     MSG_CENTER_NOTIF(TIMEOUT_BEGINNING,                 1,      0, 1, "",               CPID_TIMEOUT,           "1 f1", _("^F4Timeout begins in ^COUNT"), "")
-    MSG_CENTER_NOTIF(TIMEOUT_ENDING,                    1,      0, 1, "",               CPID_TIMEOUT,           "1 f1", _("^F4Timeout ends in ^COUNT"), "")
+    MSG_CENTER_NOTIF(TIMEOUT_ENDING,                    1,      0, 1, "",               CPID_TIMEIN,            "1 f1", _("^F4Timeout ends in ^COUNT"), "")
 
     MSG_CENTER_NOTIF(JOIN_PREVENT_MINIGAME,             1,      0, 0, "",               CPID_Null,              "0 0",  _("^K1Cannot join given minigame session!"), "" )
 
-    MSG_CENTER_NOTIF(VEHICLE_ENTER,                     1,      0, 0, "pass_key",       CPID_VEHICLES,          "0 0",  _("^BGPress ^F2DROPFLAG%s^BG to enter/exit the vehicle"), "")
-    MSG_CENTER_NOTIF(VEHICLE_ENTER_GUNNER,              1,      0, 0, "pass_key",       CPID_VEHICLES,          "0 0",  _("^BGPress ^F2DROPFLAG%s^BG to enter the vehicle gunner"), "")
-    MSG_CENTER_NOTIF(VEHICLE_ENTER_STEAL,               1,      0, 0, "pass_key",       CPID_VEHICLES,          "0 0",  _("^BGPress ^F2DROPFLAG%s^BG to steal this vehicle"), "")
+    MSG_CENTER_NOTIF(VEHICLE_ENTER,                     1,      0, 0, "pass_key",       CPID_VEHICLES,          "0 0",  _("^BGPress ^F2%s^BG to enter/exit the vehicle"), "")
+    MSG_CENTER_NOTIF(VEHICLE_ENTER_GUNNER,              1,      0, 0, "pass_key",       CPID_VEHICLES,          "0 0",  _("^BGPress ^F2%s^BG to enter the vehicle gunner"), "")
+    MSG_CENTER_NOTIF(VEHICLE_ENTER_STEAL,               1,      0, 0, "pass_key",       CPID_VEHICLES,          "0 0",  _("^BGPress ^F2%s^BG to steal this vehicle"), "")
     MSG_CENTER_NOTIF(VEHICLE_STEAL,                     1,      0, 0, "",               CPID_VEHICLES_OTHER,    "0 0",  _("^F2The enemy is stealing one of your vehicles!\n^F4Stop them!"), "")
     MSG_CENTER_NOTIF(VEHICLE_STEAL_SELF,                1,      0, 0, "",               CPID_VEHICLES_OTHER,    "4 0",  _("^F2You have stolen the enemy's vehicle, you are now visible on their radar!"), "")
 
     MSG_CHOICE_NOTIF(CTF_PICKUP_ENEMY_TEAM,     1, 2,   MSG_CENTER, CENTER_CTF_PICKUP_ENEMY_TEAM,       CENTER_CTF_PICKUP_ENEMY_TEAM_VERBOSE)
     MSG_CHOICE_NOTIF(FRAG,                      1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_FRAG,           CENTER_DEATH_MURDER_FRAG_VERBOSE)
     MSG_CHOICE_NOTIF(FRAGGED,                   1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED,        CENTER_DEATH_MURDER_FRAGGED_VERBOSE)
+    MSG_CHOICE_NOTIF(FRAG_FIRE,                 1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_FRAG_FIRE,      CENTER_DEATH_MURDER_FRAG_FIRE_VERBOSE)
+    MSG_CHOICE_NOTIF(FRAGGED_FIRE,              1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED_FIRE,   CENTER_DEATH_MURDER_FRAGGED_FIRE_VERBOSE)
+    MSG_CHOICE_NOTIF(FRAG_FREEZE,               1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_FRAG_FREEZE,    CENTER_DEATH_MURDER_FRAG_FREEZE_VERBOSE)
+    MSG_CHOICE_NOTIF(FRAGGED_FREEZE,            1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED_FREEZE, CENTER_DEATH_MURDER_FRAGGED_FREEZE_VERBOSE)
     MSG_CHOICE_NOTIF(TYPEFRAG,                  1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_TYPEFRAG,       CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE)
     MSG_CHOICE_NOTIF(TYPEFRAGGED,               1, 1,   MSG_CENTER, CENTER_DEATH_MURDER_TYPEFRAGGED,    CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE)
index 853280d6e023410758388258c2543e15beff954d..5cf3e1c6b1493972428805f13f07535ecb3a163d 100644 (file)
@@ -1,14 +1,15 @@
+#include "all.qh"
 #if defined(CSQC)
        #include <client/announcer.qh>
 #elif defined(MENUQC)
 #elif defined(SVQC)
        #include <common/constants.qh>
+       #include <common/net_linked.qh>
        #include <common/teams.qh>
        #include <server/autocvars.qh>
        #include <server/constants.qh>
        #include <server/defs.qh>
-       #include "all.qh"
-       #include <server/mutators/all.qh>
+       #include <server/mutators/_mod.qh>
 #endif
 
 // ================================================
@@ -153,7 +154,7 @@ void Destroy_Notification_Entity(entity notif)
        if (notif.nent_icon != "") strunzone(notif.nent_icon);
        if (notif.nent_durcnt != "") strunzone(notif.nent_durcnt);
        if (notif.nent_string != "") strunzone(notif.nent_string);
-       remove(notif);
+       delete(notif);
 }
 
 void Destroy_All_Notifications()
@@ -572,7 +573,7 @@ void Create_Notification_Entity_InfoCenter(entity notif,
                                }
                                else if(icon != "")
                                {
-                                       LOG_WARNINGF(
+                                       LOG_WARNF(
                                                (
                                                        "^1NOTIFICATION HAS ICON BUT NO HUDARGS: "
                                                        "^7net_type = %s, net_name = %s.\n"
@@ -589,7 +590,7 @@ void Create_Notification_Entity_InfoCenter(entity notif,
 
                                        if (cpid == CPID_Null && durcnt != "0 0")
                                        {
-                                               LOG_WARNINGF(
+                                               LOG_WARNF(
                                                        (
                                                                "Notification has durcnt but no cpid: "
                                                                "net_type = %s, net_name = %s."
@@ -1210,7 +1211,7 @@ void Local_Notification(MSG net_type, Notification net_name, ...count)
                        Get_Notif_TypeName(net_type)
                ));
                #endif
-               LOG_WARNINGF("Incorrect usage of Local_Notification: %s\n", "Null notification");
+               LOG_WARNF("Incorrect usage of Local_Notification: %s", "Null notification");
                return;
        }
 
@@ -1491,7 +1492,7 @@ void Net_Notification_Remove(entity this)
        ));
        #endif
        for (int i = 0; i < this.nent_stringcount; ++i) { if (this.nent_strings[i]) strunzone(this.nent_strings[i]); }
-       remove(this);
+       delete(this);
 }
 
 bool Net_Write_Notification(entity this, entity client, int sf)
@@ -1523,7 +1524,7 @@ void Kill_Notification(
        #endif
 
        string checkargs = Notification_CheckArgs(broadcast, client);
-       if (checkargs != "") { LOG_WARNINGF("Incorrect usage of Kill_Notification: %s", checkargs); return; }
+       if (checkargs != "") { LOG_WARNF("Incorrect usage of Kill_Notification: %s", checkargs); return; }
 
        entity net_notif = new_pure(net_kill_notification);
        net_notif.nent_broadcast = broadcast;
@@ -1532,8 +1533,8 @@ void Kill_Notification(
        net_notif.nent_net_name = ORDINAL(net_cpid);
        Net_LinkEntity(net_notif, false, autocvar_notification_lifetime_runtime, Net_Write_Notification);
 
-       FOREACH_ENTITY_CLASS(
-               "net_notification",
+       IL_EACH(
+               g_notifications,
                (it.owner.nent_type == net_type || net_type == MSG_Null) && (it.owner.nent_cpid == net_cpid || net_cpid == CPID_Null),
                {
                        it.nent_net_name = -1;
@@ -1561,7 +1562,7 @@ void Send_Notification(
 
        if (!notif)
        {
-               LOG_WARNING("Send_Notification: Could not find notification entity!");
+               LOG_WARN("Send_Notification: Could not find notification entity!");
                return;
        }
 
@@ -1570,7 +1571,7 @@ void Send_Notification(
     if (!net_name) { checkargs = sprintf("No notification provided! %s", checkargs); }
        if (checkargs != "")
        {
-               LOG_WARNINGF("Incorrect usage of Send_Notification: %s", checkargs);
+               LOG_WARNF("Incorrect usage of Send_Notification: %s", checkargs);
                return;
        }
 
@@ -1594,7 +1595,7 @@ void Send_Notification(
 
        if ((notif.nent_stringcount + notif.nent_floatcount) != count)
        {
-               LOG_WARNINGF(
+               LOG_WARNF(
                        "Argument mismatch for Send_Notification(%s, ...)! "
                        "stringcount(%d) + floatcount(%d) != count(%d)\n"
                        "Check the definition and function call for accuracy...?\n",
@@ -1682,6 +1683,7 @@ void Send_Notification(
        else
        {
                entity net_notif = new_pure(net_notification);
+               IL_PUSH(g_notifications, net_notif);
                net_notif.owner = notif;
                net_notif.nent_broadcast = broadcast;
                net_notif.nent_client = client;
index 7dcfec3534549fa9462be965e25abe06795d6f04..5e5fbf1d603a560eb07fa18f8c1312c4a2f89a1a 100644 (file)
@@ -1,7 +1,6 @@
-#ifndef NOTIFICATIONS_H
-#define NOTIFICATIONS_H
+#pragma once
 
-#include <common/command/all.qh>
+#include <common/command/_mod.qh>
 
 #include <common/constants.qh>
 #include <common/teams.qh>
@@ -33,7 +32,7 @@ string Get_Notif_TypeName(MSG net_type)
                case MSG_MULTI: return "MSG_MULTI";
                case MSG_CHOICE: return "MSG_CHOICE";
        }
-       LOG_WARNINGF("Get_Notif_TypeName(%d): Improper net type!\n", ORDINAL(net_type));
+       LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!", ORDINAL(net_type));
        return "";
 }
 
@@ -66,6 +65,7 @@ ENUMCLASS(CPID)
        CASE(CPID, RACE_FINISHLAP)
        CASE(CPID, TEAMCHANGE)
        CASE(CPID, TIMEOUT)
+       CASE(CPID, TIMEIN)
        CASE(CPID, VEHICLES)
        CASE(CPID, VEHICLES_OTHER)
        /** always last */
@@ -76,8 +76,7 @@ USING(Notification, entity);
 
 // used for notification system multi-team identifiers
 #define APP_TEAM_NUM(num, prefix) ((num == NUM_TEAM_1) ? prefix##_RED : ((num == NUM_TEAM_2) ? prefix##_BLUE : ((num == NUM_TEAM_3) ? prefix##_YELLOW : prefix##_PINK)))
-/** @deprecated use APP_TEAM_NUM */
-#define APP_TEAM_ENT(ent, prefix) APP_TEAM_NUM(ent.team, prefix)
+#define APP_NUM(num, prefix) ((num) ? APP_TEAM_NUM(num, prefix) : prefix##_NEUTRAL)
 
 #define EIGHT_VARS_TO_VARARGS_VARLIST \
        VARITEM(1, 0, s1) \
@@ -160,7 +159,7 @@ GENERIC_COMMAND(dumpnotifs, "Dump all notifications into notifications_dump.txt"
        {
                case CMD_REQUEST_COMMAND:
                {
-                       #ifndef MENUQC
+                       #ifdef GAMEQC
                        string filename = argv(1);
                        bool alsoprint = false;
                        if (filename == "")
@@ -201,8 +200,8 @@ GENERIC_COMMAND(dumpnotifs, "Dump all notifications into notifications_dump.txt"
        }
 }
 
-float autocvar_notification_debug = false;
 #ifdef NOTIFICATIONS_DEBUG
+bool autocvar_notification_debug = false;
 void Debug_Notification(string input)
 {
        switch (autocvar_notification_debug)
@@ -226,6 +225,11 @@ string prev_soundfile;
 float prev_soundtime;
 #endif
 
+#ifdef SVQC
+IntrusiveList g_notifications;
+STATIC_INIT(g_notifications) { g_notifications = IL_NEW(); }
+#endif
+
 #ifdef SVQC
 ENUMCLASS(NOTIF)
        /** send to one client and their spectators */
@@ -253,7 +257,7 @@ string Get_Notif_BroadcastName(NOTIF broadcast)
                case NOTIF_TEAM: return "NOTIF_TEAM";
                case NOTIF_TEAM_EXCEPT: return "NOTIF_TEAM_EXCEPT";
        }
-       LOG_WARNINGF("Get_Notif_BroadcastName(%d): Improper broadcast!\n", broadcast);
+       LOG_WARNF("Get_Notif_BroadcastName(%d): Improper broadcast!", broadcast);
        return "";
 }
 
@@ -293,8 +297,10 @@ float autocvar_notification_show_sprees_info = 3; // 0 = off, 1 = target only, 2
 float autocvar_notification_show_sprees_info_newline = true;
 float autocvar_notification_show_sprees_info_specialonly = true;
 float autocvar_notification_errors_are_fatal = true;
+#ifdef SVQC
 float autocvar_notification_lifetime_runtime = 0.5;
 float autocvar_notification_lifetime_mapload = 10;
+#endif
 
 #ifdef SVQC
 .float FRAG_VERBOSE;
@@ -349,6 +355,7 @@ float autocvar_notification_show_sprees_center_specialonly = true;
        race_diff: show time difference between f2 and f3
        missing_teams: show which teams still need players
        pass_key: find the keybind for "passing" or "dropping" in CTF game mode
+       nade_key: find the keybind for nade throwing
        frag_ping: show the ping of a player
        frag_stats: show health/armor/ping of a player
        frag_pos: show score status and position in the match of a player
@@ -408,7 +415,8 @@ string BUFF_NAME(int i);
        ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
        ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
        ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1)) \
-       ARG_CASE(ARG_CS,        "pass_key",      ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
+       ARG_CASE(ARG_CS,        "pass_key",      getcommandkey(_("drop flag"), "+use")) \
+       ARG_CASE(ARG_CS,        "nade_key",      getcommandkey(_("throw nade"), "dropweapon")) \
        ARG_CASE(ARG_CS,        "frag_ping",     notif_arg_frag_ping(true, f2)) \
        ARG_CASE(ARG_CS,        "frag_stats",    notif_arg_frag_stats(f2, f3, f4)) \
        /*ARG_CASE(ARG_CS,      "frag_pos",      ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))*/ \
@@ -685,7 +693,7 @@ Notification Get_Notif_Ent(MSG net_type, int net_name)
 {
        Notification it = _Notifications_from(net_name, NULL);
        if (it.nent_type != net_type) {
-               LOG_WARNINGF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!\n",
+               LOG_WARNF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!",
                        Get_Notif_TypeName(net_type), net_type,
                        it.registered_id, net_name,
                        Get_Notif_TypeName(it.nent_type)
@@ -800,5 +808,3 @@ REGISTRY_END(Notifications)
 }
 
 #include "all.inc"
-
-#endif
index 3a61cd4e9a433c03742c10a5233e4a044f32c181..100aecae29a006bcecf6caf42162815d30f76086 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
 #include <common/physics/movelib.qc>
 #include <common/physics/player.qc>
+
+#include <common/physics/movetypes/_mod.inc>
index 39dacad8e2817c9aad232c4b59a8348df012fb7e..377a7b340d8527720af548e8926af06d634a8804 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
 #include <common/physics/movelib.qh>
 #include <common/physics/player.qh>
+
+#include <common/physics/movetypes/_mod.qh>
index 250c3ba2b28e121d7e4568a4a87baf134d5ac45b..e0659a40c197ffcbeeffb775a974682974466212 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MOVELIB_H
-#define MOVELIB_H
+#pragma once
 
 #ifdef SVQC
 .vector moveto;
@@ -49,5 +48,3 @@ Yed need to set v_up and v_forward (generally by calling makevectors) before cal
 #endif
 
 void movelib_groundalign4point(entity this, float spring_length, float spring_up, float blendrate, float _max);
-
-#endif
index 4effcbd04a0a983e4d4bac87e760ada7f99d4928..5cb1d0bc4e99ac3840142341505923e501a3d5dc 100644 (file)
@@ -1,7 +1,6 @@
 // generated file; do not modify
 #include <common/physics/movetypes/follow.qc>
 #include <common/physics/movetypes/movetypes.qc>
-#include <common/physics/movetypes/push.qc>
 #include <common/physics/movetypes/step.qc>
 #include <common/physics/movetypes/toss.qc>
 #include <common/physics/movetypes/walk.qc>
index 32ae3813c8ea37976b4f1bb2abb3befd08c135d3..1b1241a0aa8e2451dc8db4f243179ff5e67e0f4f 100644 (file)
@@ -1,7 +1,6 @@
 // generated file; do not modify
 #include <common/physics/movetypes/follow.qh>
 #include <common/physics/movetypes/movetypes.qh>
-#include <common/physics/movetypes/push.qh>
 #include <common/physics/movetypes/step.qh>
 #include <common/physics/movetypes/toss.qh>
 #include <common/physics/movetypes/walk.qh>
index 322b3c4de13de003a1218820aa57ea0dea1a3941..70157d1862746141158ee5c31ee903ded84e7edb 100644 (file)
@@ -1,4 +1,3 @@
-#include "push.qc"
 #include "toss.qc"
 #include "walk.qc"
 #include "step.qc"
index 2d3e24f44c93ec5e8d4f34b0800d806336f2039d..3009069052f7de48c7a4e7a0fa25f69f993cac21 100644 (file)
@@ -1,31 +1,30 @@
+#include "follow.qh"
 void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow
 {
-       entity e = this.move_aiment; // TODO: networking?
+       entity e = this.aiment;
 
-       // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
-       if(this.move_angles == this.move_punchangle)
+       if(e.angles == this.punchangle)
        {
-               this.move_origin = e.move_origin + this.view_ofs;
+               this.origin = e.origin + this.view_ofs;
        }
        else
        {
                vector ang, v;
-               ang_x = -this.move_punchangle_x;
-               ang_y = this.move_punchangle_y;
-               ang_z = this.move_punchangle_z;
+               ang_x = -this.punchangle_x;
+               ang_y = this.punchangle_y;
+               ang_z = this.punchangle_z;
                makevectors(ang);
                v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x;
                v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y;
                v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.view_ofs_z * v_up_z;
-               ang_x = -e.move_angles_x;
-               ang_y = e.move_angles_y;
-               ang_z = e.move_angles_z;
+               ang = e.angles;
+               ang_x = -e.angles_x;
                makevectors(ang);
-               this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x;
-               this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y;
-               this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z;
+               this.origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.origin_x;
+               this.origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.origin_y;
+               this.origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.origin_z;
        }
 
-       this.move_angles = e.move_angles + this.v_angle;
+       this.angles = e.angles + this.v_angle;
        _Movetype_LinkEdict(this, false);
 }
diff --git a/qcsrc/common/physics/movetypes/follow.qh b/qcsrc/common/physics/movetypes/follow.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index eee217200f7da8baf8015dfae9c87d06516079fb..f0df80cd5d52c044dd46563eb248ea976f3692ed 100644 (file)
@@ -1,10 +1,10 @@
+#include "movetypes.qh"
 #include "../player.qh"
 
 #if defined(CSQC)
        #include <client/defs.qh>
        #include <common/stats.qh>
        #include <common/util.qh>
-       #include "movetypes.qh"
        #include <lib/csqcmodel/common.qh>
        #include <common/t_items.qh>
 #elif defined(MENUQC)
        #include <server/autocvars.qh>
 #endif
 
+#ifdef SVQC
+void set_movetype(entity this, int mt)
+{
+       this.move_movetype = mt;
+       if (mt == MOVETYPE_PHYSICS || mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH) {
+               this.move_qcphysics = false;
+       }
+       this.movetype = (this.move_qcphysics) ? MOVETYPE_NONE : mt;
+}
+#elif defined(CSQC)
+void set_movetype(entity this, int mt)
+{
+       this.move_movetype = mt;
+}
+#endif
+
 void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
 {
        /*float d, i;
@@ -21,11 +37,11 @@ void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
 
        if(d < 0)
        {
-           i = (stepnormal * this.move_velocity);
+           i = (stepnormal * this.velocity);
            into = i * stepnormal;
-           side = this.move_velocity - into;
-           this.move_velocity_x = side.x * (1 * d);
-           this.move_velocity_y = side.y * (1 * d);
+           side = this.velocity - into;
+           this.velocity_x = side.x * (1 * d);
+           this.velocity_y = side.y * (1 * d);
        }*/
 }
 
@@ -46,23 +62,23 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                this.move_didgravity = 1;
                grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this);
 
-               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
+               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this))
                {
                        if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                               this.move_velocity_z -= grav * 0.5;
+                               this.velocity_z -= grav * 0.5;
                        else
-                               this.move_velocity_z -= grav;
+                               this.velocity_z -= grav;
                }
        }
 
-       original_velocity = primal_velocity = restore_velocity = this.move_velocity;
+       original_velocity = primal_velocity = restore_velocity = this.velocity;
 
        for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
        {
-               if(this.move_velocity == '0 0 0')
+               if(this.velocity == '0 0 0')
                        break;
 
-               push = this.move_velocity * time_left;
+               push = this.velocity * time_left;
                _Movetype_PushEntity(this, push, true);
                if(trace_startsolid)
                {
@@ -76,7 +92,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                // abort move if we're stuck in the world (and didn't make it out)
                if(trace_startsolid && trace_allsolid)
                {
-                       this.move_velocity = restore_velocity;
+                       this.velocity = restore_velocity;
                        return 3;
                }
 
@@ -99,14 +115,14 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                                        trace_ent = NULL;
                                }
 
-                               this.move_flags |= FL_ONGROUND;
-                               this.move_groundentity = trace_ent;
+                               SET_ONGROUND(this);
+                               this.groundentity = trace_ent;
                        }
                }
                else if(stepheight)
                {
                        // step - handle it immediately
-                       vector org = this.move_origin;
+                       vector org = this.origin;
                        vector steppush = '0 0 1' * stepheight;
 
                        _Movetype_PushEntity(this, steppush, true);
@@ -122,7 +138,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                                break;
                        }
                        float trace2_fraction = trace_fraction;
-                       steppush = '0 0 1' * (org_z - this.move_origin_z);
+                       steppush = '0 0 1' * (org_z - this.origin_z);
                        _Movetype_PushEntity(this, steppush, true);
                        if(trace_startsolid)
                        {
@@ -131,15 +147,15 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                        }
 
                        // accept the new position if it made some progress...
-                       if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125)
+                       if(fabs(this.origin_x - org_x) >= 0.03125 || fabs(this.origin_y - org_y) >= 0.03125)
                        {
-                               trace_endpos = this.move_origin;
+                               trace_endpos = this.origin;
                                time_left *= 1 - trace2_fraction;
                                numplanes = 0;
                                continue;
                        }
                        else
-                               this.move_origin = org;
+                               this.origin = org;
                }
                else
                {
@@ -153,7 +169,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                if(my_trace_fraction >= 0.001)
                {
                        // actually covered some distance
-                       original_velocity = this.move_velocity;
+                       original_velocity = this.velocity;
                        numplanes = 0;
                }
 
@@ -163,7 +179,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                if(numplanes >= MAX_CLIP_PLANES)
                {
                        // this shouldn't really happen
-                       this.move_velocity = '0 0 0';
+                       this.velocity = '0 0 0';
                        blocked = 3;
                        break;
                }
@@ -192,14 +208,14 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                if(i != numplanes)
                {
                        // go along this plane
-                       this.move_velocity = new_velocity;
+                       this.velocity = new_velocity;
                }
                else
                {
                        // go along the crease
                        if(numplanes != 2)
                        {
-                               this.move_velocity = '0 0 0';
+                               this.velocity = '0 0 0';
                                blocked = 7;
                                break;
                        }
@@ -211,29 +227,29 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                        dir.x *= ilength;
                        dir.y *= ilength;
                        dir.z *= ilength;
-                       float d = (dir * this.move_velocity);
-                       this.move_velocity = dir * d;
+                       float d = (dir * this.velocity);
+                       this.velocity = dir * d;
                }
 
                // if current velocity is against the original velocity,
                // stop dead to avoid tiny occilations in sloping corners
-               if((this.move_velocity * primal_velocity) <= 0)
+               if((this.velocity * primal_velocity) <= 0)
                {
-                       this.move_velocity = '0 0 0';
+                       this.velocity = '0 0 0';
                        break;
                }
        }
 
        // LordHavoc: this came from QW and allows you to get out of water more easily
-       if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8))
-               this.move_velocity = primal_velocity;
+       if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.flags & FL_WATERJUMP) && !(blocked & 8))
+               this.velocity = primal_velocity;
 
        if(applygravity)
        {
-               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
+               if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this))
                {
                        if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                               this.move_velocity_z -= grav * 0.5f;
+                               this.velocity_z -= grav * 0.5f;
                }
        }
 
@@ -242,98 +258,84 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
 
 void _Movetype_CheckVelocity(entity this)  // SV_CheckVelocity
 {
-       // if(vlen(this.move_velocity) < 0.0001)
-       // this.move_velocity = '0 0 0';
+       // if(vlen(this.velocity) < 0.0001)
+       // this.velocity = '0 0 0';
 }
 
 bool _Movetype_CheckWater(entity this)  // SV_CheckWater
 {
-       vector point = this.move_origin;
+       vector point = this.origin;
        point.z += this.mins.z + 1;
 
        int nativecontents = pointcontents(point);
-       if(this.move_watertype && this.move_watertype != nativecontents)
+       if(this.watertype && this.watertype != nativecontents)
        {
-               // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents);
+               // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.watertype, nativecontents);
                if(this.contentstransition)
-                       this.contentstransition(this.move_watertype, nativecontents);
+                       this.contentstransition(this.watertype, nativecontents);
        }
 
-       this.move_waterlevel = WATERLEVEL_NONE;
-       this.move_watertype = CONTENT_EMPTY;
+       this.waterlevel = WATERLEVEL_NONE;
+       this.watertype = CONTENT_EMPTY;
 
        int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
        if(supercontents & DPCONTENTS_LIQUIDSMASK)
        {
-               this.move_watertype = nativecontents;
-               this.move_waterlevel = WATERLEVEL_WETFEET;
-               point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5;
+               this.watertype = nativecontents;
+               this.waterlevel = WATERLEVEL_WETFEET;
+               point.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
                if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
                {
-                       this.move_waterlevel = WATERLEVEL_SWIMMING;
-                       point.z = this.move_origin.z + this.view_ofs.z;
+                       this.waterlevel = WATERLEVEL_SWIMMING;
+                       point.z = this.origin.z + this.view_ofs.z;
                        if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
-                               this.move_waterlevel = WATERLEVEL_SUBMERGED;
+                               this.waterlevel = WATERLEVEL_SUBMERGED;
                }
        }
 
-       return this.move_waterlevel > 1;
+       return this.waterlevel > 1;
 }
 
 void _Movetype_CheckWaterTransition(entity ent)  // SV_CheckWaterTransition
 {
-       int contents = pointcontents(ent.move_origin);
+       int contents = pointcontents(ent.origin);
 
-       if(!ent.move_watertype)
+       if(!ent.watertype)
        {
                // just spawned here
-               if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
+               if(!GAMEPLAYFIX_WATERTRANSITION(ent))
                {
-                       ent.move_watertype = contents;
-                       ent.move_waterlevel = 1;
+                       ent.watertype = contents;
+                       ent.waterlevel = 1;
                        return;
                }
        }
-       else if(ent.move_watertype != contents)
+       else if(ent.watertype != contents)
        {
-               // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents);
+               // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.origin), pointcontents(ent.origin), ent.watertype, contents);
                if(ent.contentstransition)
-                       ent.contentstransition(ent.move_watertype, contents);
+                       ent.contentstransition(ent.watertype, contents);
        }
 
        if(contents <= CONTENT_WATER)
        {
-               ent.move_watertype = contents;
-               ent.move_waterlevel = 1;
+               ent.watertype = contents;
+               ent.waterlevel = 1;
        }
        else
        {
-               ent.move_watertype = CONTENT_EMPTY;
-               ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
+               ent.watertype = CONTENT_EMPTY;
+               ent.waterlevel = (GAMEPLAYFIX_WATERTRANSITION(ent) ? 0 : contents);
        }
 }
 
 void _Movetype_Impact(entity this, entity oth)  // SV_Impact
 {
-       entity oldother = other;
-
        if(gettouch(this))
-       {
-               other = oth;
-
-               gettouch(this)(this);
-
-               other = oldother;
-       }
+               gettouch(this)(this, oth);
 
        if(gettouch(oth))
-       {
-               other = this;
-
-               gettouch(oth)(oth);
-
-               other = oldother;
-       }
+               gettouch(oth)(oth, this);
 }
 
 void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
@@ -341,30 +343,24 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGr
        if(this.solid == SOLID_NOT)
                return;
 
-       entity oldother = other;
-
     FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
                if (it.solid == SOLID_TRIGGER && it != this)
                if (it.move_nomonsters != MOVE_NOMONSTERS && it.move_nomonsters != MOVE_WORLDONLY)
                if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax))
                {
-                       other = this;
-
                        trace_allsolid = false;
                        trace_startsolid = false;
                        trace_fraction = 1;
                        trace_inwater = false;
                        trace_inopen = true;
-                       trace_endpos = it.move_origin;
+                       trace_endpos = it.origin;
                        trace_plane_normal = '0 0 1';
                        trace_plane_dist = 0;
                        trace_ent = this;
 
-                       gettouch(it)(it);
+                       gettouch(it)(it, this);
                }
     });
-
-       other = oldother;
 }
 
 void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
@@ -381,10 +377,10 @@ void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
                mi = this.mins;
                ma = this.maxs;
        }
-       mi += this.move_origin;
-       ma += this.move_origin;
+       mi += this.origin;
+       ma += this.origin;
 
-       if(this.move_flags & FL_ITEM)
+       if(this.flags & FL_ITEM)
        {
                mi.x -= 15;
                mi.y -= 15;
@@ -414,18 +410,18 @@ entity _Movetype_TestEntityPosition_ent;
 bool _Movetype_TestEntityPosition(vector ofs)  // SV_TestEntityPosition
 {
     entity this = _Movetype_TestEntityPosition_ent;
-//     vector org = this.move_origin + ofs;
+//     vector org = this.origin + ofs;
 
        int cont = this.dphitcontentsmask;
        this.dphitcontentsmask = DPCONTENTS_SOLID;
-       tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
+       tracebox(this.origin, this.mins, this.maxs, this.origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
        this.dphitcontentsmask = cont;
 
        if(trace_startsolid)
                return true;
 
-       if(vdist(trace_endpos - this.move_origin, >, 0.0001))
-               this.move_origin = trace_endpos;
+       if(vdist(trace_endpos - this.origin, >, 0.0001))
+               this.origin = trace_endpos;
        return false;
 }
 
@@ -450,13 +446,13 @@ bool _Movetype_UnstickEntity(entity this)  // SV_UnstickEntity
         X(17)
         #undef X
         {
-            LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
-                etof(this), this.classname, vtos(this.move_origin));
+            LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)",
+                etof(this), this.classname, vtos(this.origin));
             return false;
         }
        }
-       LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
-               etof(this), this.classname, vtos(this.move_origin));
+       LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)",
+               etof(this), this.classname, vtos(this.origin));
        _Movetype_LinkEdict(this, true);
        return true;
 }
@@ -474,7 +470,7 @@ vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVeloc
 
 void _Movetype_PushEntityTrace(entity this, vector push)
 {
-       vector end = this.move_origin + push;
+       vector end = this.origin + push;
        int type;
        if(this.move_nomonsters)
                type = max(0, this.move_nomonsters);
@@ -487,7 +483,7 @@ void _Movetype_PushEntityTrace(entity this, vector push)
        else
                type = MOVE_NORMAL;
 
-       tracebox(this.move_origin, this.mins, this.maxs, end, type, this);
+       tracebox(this.origin, this.mins, this.maxs, end, type, this);
 }
 
 float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid)  // SV_PushEntity
@@ -497,10 +493,10 @@ float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid)  //
        if(trace_startsolid && failonstartsolid)
                return trace_fraction;
 
-       this.move_origin = trace_endpos;
+       this.origin = trace_endpos;
 
        if(trace_fraction < 1)
-               if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent)))
+               if(this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || (this.groundentity != trace_ent)))
                        _Movetype_Impact(this, trace_ent);
 
        return trace_fraction;
@@ -514,9 +510,9 @@ void makevectors_matrix(vector myangles)  // AngleVectorsFLU
 {
        v_forward = v_right = v_up = '0 0 0';
 
-       float y = myangles.y * (M_PI * 2 / 360);
-       float sy = sin(y);
-       float cy = cos(y);
+       float yy = myangles.y * (M_PI * 2 / 360);
+       float sy = sin(yy);
+       float cy = cos(yy);
        float p = myangles.x * (M_PI * 2 / 360);
        float sp = sin(p);
        float cp = cos(p);
@@ -571,7 +567,7 @@ void _Movetype_Physics_Frame(entity this, float movedt)
        {
                case MOVETYPE_PUSH:
                case MOVETYPE_FAKEPUSH:
-                       _Movetype_Physics_Pusher(this, movedt);
+                       LOG_DEBUGF("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
                        break;
                case MOVETYPE_NONE:
                        break;
@@ -580,8 +576,8 @@ void _Movetype_Physics_Frame(entity this, float movedt)
                        break;
                case MOVETYPE_NOCLIP:
                        _Movetype_CheckWater(this);
-                       this.move_origin = this.move_origin + TICRATE * this.move_velocity;
-                       this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
+                       this.origin = this.origin + movedt * this.velocity;
+                       this.angles = this.angles + movedt * this.avelocity;
                        _Movetype_LinkEdict(this, false);
                        break;
                case MOVETYPE_STEP:
@@ -611,7 +607,7 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
        {
                case MOVETYPE_PUSH:
                case MOVETYPE_FAKEPUSH:
-                       _Movetype_Physics_Pusher(this, movedt);
+                       LOG_DEBUGF("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
                        break;
                case MOVETYPE_NONE:
                        break;
@@ -620,8 +616,8 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
                        break;
                case MOVETYPE_NOCLIP:
                        _Movetype_CheckWater(this);
-                       this.move_origin = this.move_origin + TICRATE * this.move_velocity;
-                       this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
+                       this.origin = this.origin + movedt * this.velocity;
+                       this.angles = this.angles + movedt * this.avelocity;
                        _Movetype_LinkEdict(this, false);
                        break;
                case MOVETYPE_STEP:
@@ -643,6 +639,20 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
        }
 }
 
+void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)  // to be run every move frame
+{
+       this.move_time = time;
+
+       if(isclient)
+               _Movetype_Physics_ClientFrame(this, movedt);
+       else
+               _Movetype_Physics_Frame(this, movedt);
+       if(wasfreed(this))
+               return;
+
+       setorigin(this, this.origin);
+}
+
 void Movetype_Physics_NoMatchServer(entity this)  // optimized
 {
        float movedt = time - this.move_time;
@@ -652,10 +662,7 @@ void Movetype_Physics_NoMatchServer(entity this)  // optimized
        if(wasfreed(this))
                return;
 
-       this.avelocity = this.move_avelocity;
-       this.velocity = this.move_velocity;
-       this.angles = this.move_angles;
-       setorigin(this, this.move_origin);
+       setorigin(this, this.origin);
 }
 
 void Movetype_Physics_MatchServer(entity this, bool sloppy)
@@ -663,11 +670,49 @@ void Movetype_Physics_MatchServer(entity this, bool sloppy)
        Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
 }
 
+.vector tic_origin;
+.vector tic_velocity;
+.int tic_flags;
+.vector tic_avelocity;
+.vector tic_angles;
+
+.vector tic_saved_origin;
+.vector tic_saved_velocity;
+.int tic_saved_flags;
+.vector tic_saved_avelocity;
+.vector tic_saved_angles;
 void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Physics_Entity
 {
+#define X(s) \
+       if(this.(s) != this.tic_saved_##s) \
+               this.tic_##s = this.(s)
+
+       X(origin);
+       X(velocity);
+       X(flags);
+       X(avelocity);
+       X(angles);
+#undef X
+
        if(tr <= 0)
        {
+               this.flags = this.tic_flags;
+               this.velocity = this.tic_velocity;
+               this.origin = this.tic_origin;
+               this.avelocity = this.tic_avelocity;
+               this.angles = this.tic_angles;
                Movetype_Physics_NoMatchServer(this);
+               this.tic_origin = this.origin;
+               this.tic_velocity = this.velocity;
+               this.tic_avelocity = this.avelocity;
+               this.tic_angles = this.angles;
+               this.tic_flags = this.flags;
+
+               this.tic_saved_flags = this.flags;
+               this.tic_saved_velocity = this.velocity;
+               this.tic_saved_origin = this.origin;
+               this.tic_saved_avelocity = this.avelocity;
+               this.tic_saved_angles = this.angles;
                return;
        }
 
@@ -678,21 +723,31 @@ void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Ph
        this.move_time += n * tr;
 
        if(!this.move_didgravity)
-               this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND));
+               this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.tic_flags & FL_ONGROUND));
 
        for (int i = 0; i < n; ++i)
        {
+               this.flags = this.tic_flags;
+               this.velocity = this.tic_velocity;
+               setorigin(this, this.tic_origin);
+               this.avelocity = this.tic_avelocity;
+               this.angles = this.tic_angles;
                _Movetype_Physics_Frame(this, tr);
+               this.tic_origin = this.origin;
+               this.tic_velocity = this.velocity;
+               this.tic_avelocity = this.avelocity;
+               this.tic_angles = this.angles;
+               this.tic_flags = this.flags;
                if(wasfreed(this))
                        return;
        }
 
-       this.avelocity = this.move_avelocity;
+       this.avelocity = this.tic_avelocity;
 
-       if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND))
+       if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.tic_flags & FL_ONGROUND))
        {
                // now continue the move from move_time to time
-               this.velocity = this.move_velocity;
+               this.velocity = this.tic_velocity;
 
                if(this.move_didgravity > 0)
                {
@@ -702,15 +757,18 @@ void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Ph
                            * PHYS_GRAVITY(this);
                }
 
-               this.angles = this.move_angles + dt * this.avelocity;
+               this.angles = this.tic_angles + dt * this.avelocity;
 
                if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
                {
-                       setorigin(this, this.move_origin + dt * this.velocity);
+                       setorigin(this, this.tic_origin + dt * this.velocity);
                }
                else
                {
+                       vector oldorg = this.origin;
+                       this.origin = this.tic_origin;
                        _Movetype_PushEntityTrace(this, dt * this.velocity);
+                       this.origin = oldorg;
                        if(!trace_startsolid)
                                setorigin(this, trace_endpos);
                }
@@ -720,8 +778,14 @@ void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Ph
        }
        else
        {
-               this.velocity = this.move_velocity;
-               this.angles = this.move_angles;
-               setorigin(this, this.move_origin);
+               this.velocity = this.tic_velocity;
+               this.angles = this.tic_angles;
+               setorigin(this, this.tic_origin);
        }
+
+       this.tic_saved_flags = this.flags;
+       this.tic_saved_velocity = this.velocity;
+       this.tic_saved_origin = this.origin;
+       this.tic_saved_avelocity = this.avelocity;
+       this.tic_saved_angles = this.angles;
 }
index 61b7e94a954f43d740bf73ec275f80db394be125..46e80fbc3e47aba9df7b2c659fa0ffa1b12eb6a5 100644 (file)
@@ -1,36 +1,37 @@
-#ifndef MOVETYPES_H
-#define MOVETYPES_H
+#pragma once
 
 #define IS_ONGROUND(s)                      boolean((s).flags & FL_ONGROUND)
 #define SET_ONGROUND(s)                     ((s).flags |= FL_ONGROUND)
 #define UNSET_ONGROUND(s)                   ((s).flags &= ~FL_ONGROUND)
+#define IS_ONSLICK(s)                                          boolean((s).flags & FL_ONSLICK)
+#define SET_ONSLICK(s)                                         ((s).flags |= FL_ONSLICK)
+#define UNSET_ONSLICK(s)                                       ((s).flags &= ~FL_ONSLICK)
 
-.float move_ltime;
-.void(entity this) move_think;
-.float move_nextthink;
-.void(entity this) move_blocked;
+#ifdef CSQC
+.float bouncestop;
+.float bouncefactor;
+#endif
+
+void set_movetype(entity this, int mt);
 
 .float move_movetype;
 .float move_time;
-.vector move_origin;
-.vector move_angles;
-.vector move_velocity;
-.vector move_avelocity;
-.int move_flags;
-.int move_watertype;
-.int move_waterlevel;
+//.vector move_origin;
+//.vector move_angles;
+//.vector move_velocity;
+//.vector move_avelocity;
+//.int move_flags;
+//.int move_watertype;
+//.int move_waterlevel;
 .void(float, float)contentstransition;
-.float move_bounce_factor;
-.float move_bounce_stopspeed;
+//.float move_bounce_factor;
+//.float move_bounce_stopspeed;
 .float move_nomonsters;  // -1 for MOVE_NORMAL, otherwise a MOVE_ constant
 
-.entity move_aiment;
-.vector move_punchangle;
+.entity aiment;
+.vector punchangle;
 
-// should match sv_gameplayfix_fixedcheckwatertransition
-float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1;
-
-.entity move_groundentity;  // FIXME add move_groundnetworkentity?
+.entity groundentity;  // FIXME add move_groundnetworkentity?
 .float move_suspendedinair;
 .float move_didgravity;
 
@@ -46,6 +47,7 @@ void _Movetype_PushEntityTrace(entity this, vector push);
 float _Movetype_PushEntity(entity this, vector push, float failonstartsolid);
 void makevectors_matrix(vector myangles);
 
+void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient);
 void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy);
 void Movetype_Physics_MatchServer(entity this, bool sloppy);
 void Movetype_Physics_NoMatchServer(entity this);
@@ -75,8 +77,13 @@ const int MOVETYPE_FLY_WORLDONLY    = 33;
 
 const int FL_ITEM                   = 256;
 const int FL_ONGROUND                          = 512;
+#elif defined(SVQC)
+const int MOVETYPE_ANGLENOCLIP      = 1;
+const int MOVETYPE_ANGLECLIP        = 2;
 #endif
 
+const int FL_ONSLICK = BIT(20);
+
 const int MOVETYPE_FAKEPUSH         = 13;
 
 const int MOVEFLAG_VALID = BIT(23);
@@ -87,5 +94,3 @@ const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2);
 #ifdef CSQC
 #define moveflags STAT(MOVEFLAGS)
 #endif
-
-#endif
diff --git a/qcsrc/common/physics/movetypes/push.qc b/qcsrc/common/physics/movetypes/push.qc
deleted file mode 100644 (file)
index 0d03bff..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-void _Movetype_PushMove(entity this, float dt)  // SV_PushMove
-{
-       if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0')
-       {
-               this.move_ltime += dt;
-               return;
-       }
-
-       switch (this.solid)
-       {
-               // LordHavoc: valid pusher types
-               case SOLID_BSP:
-               case SOLID_BBOX:
-               case SOLID_SLIDEBOX:
-               case SOLID_CORPSE:  // LordHavoc: this would be weird...
-                       break;
-               // LordHavoc: no collisions
-               case SOLID_NOT:
-               case SOLID_TRIGGER:
-                       this.move_origin = this.move_origin + dt * this.move_velocity;
-                       this.move_angles = this.move_angles + dt * this.move_avelocity;
-                       this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
-                       this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
-                       this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
-                       this.move_ltime += dt;
-                       _Movetype_LinkEdict(this, true);
-                       return;
-               default:
-                       LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid);
-                       return;
-       }
-
-       bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0;
-
-       vector move1 = this.move_velocity * dt;
-       vector moveangle = this.move_avelocity * dt;
-
-       makevectors_matrix(-moveangle);
-
-//     vector pushorig = this.move_origin;
-//     vector pushang = this.move_angles;
-//     float pushltime = this.move_ltime;
-
-// move the pusher to its final position
-
-       this.move_origin = this.move_origin + dt * this.move_velocity;
-       this.move_angles = this.move_angles + dt * this.move_avelocity;
-
-       this.move_ltime += dt;
-       _Movetype_LinkEdict(this, true);
-
-       int savesolid = this.solid;
-
-       if (this.move_movetype != MOVETYPE_FAKEPUSH)
-       {
-           FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
-                       switch (it.move_movetype)
-                       {
-                               case MOVETYPE_NONE:
-                               case MOVETYPE_PUSH:
-                               case MOVETYPE_FOLLOW:
-                               case MOVETYPE_NOCLIP:
-                               case MOVETYPE_FLY_WORLDONLY:
-                                       continue;
-                               default:
-                                       break;
-                       }
-
-                       if (it.owner == this)
-                               continue;
-
-                       if (this.owner == it)
-                               continue;
-
-                       vector pivot = it.mins + 0.5 * (it.maxs - it.mins);
-                       vector move;
-                       if (rotated)
-                       {
-                               vector org = (it.move_origin - this.move_origin) + pivot;
-                               vector org2;
-                               org2.x = org * v_forward;
-                               org2.y = org * v_right;
-                               org2.z = org * v_up;
-                               move = (org2 - org) + move1;
-                       }
-                       else
-                       {
-                               move = move1;
-                       }
-
-                       // physics objects need better collisions than this code can do
-                       if (it.move_movetype == 32)  // MOVETYPE_PHYSICS
-                       {
-                               it.move_origin = it.move_origin + move;
-                               _Movetype_LinkEdict(it, true);
-                               continue;
-                       }
-
-                       // try moving the contacted entity
-                       this.solid = SOLID_NOT;
-                       bool flag = false;
-                       flag = _Movetype_PushEntity(it, move, true);
-                       if (!flag)
-                       {
-                               // entity "it" got teleported
-                               it.move_angles_y += trace_fraction * moveangle.y;
-                               this.solid = savesolid;
-                               continue;  // pushed enough
-                       }
-                       // FIXME: turn players specially
-                       it.move_angles_y += trace_fraction * moveangle.y;
-                       this.solid = savesolid;
-
-                       // this trace.fraction < 1 check causes items to fall off of pushers
-                       // if they pass under or through a wall
-                       // the groundentity check causes items to fall off of ledges
-                       if (it.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || it.move_groundentity != this))
-                               it.move_flags &= ~FL_ONGROUND;
-        });
-       }
-
-       this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0));
-       this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0));
-       this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0));
-}
-
-void _Movetype_Physics_Pusher(entity this, float dt)  // SV_Physics_Pusher
-{
-       float oldltime = this.move_ltime;
-       float thinktime = this.move_nextthink;
-       float movetime;
-       if (thinktime < this.move_ltime + dt)
-       {
-               movetime = thinktime - this.move_ltime;
-               if (movetime < 0)
-                       movetime = 0;
-       }
-       else
-       {
-               movetime = dt;
-       }
-
-       if (movetime)
-               // advances this.move_ltime if not blocked
-               _Movetype_PushMove(this, movetime);
-
-       if (thinktime > oldltime && thinktime <= this.move_ltime)
-       {
-               this.move_nextthink = 0;
-               this.move_time = time;
-               other = NULL;
-               this.move_think(this);
-       }
-}
index d7a2d562747ebd2aa8967daef7028fb150b6eaee..a41269f79c562122fe8f01e6745cfa7df2180748 100644 (file)
@@ -1,10 +1,11 @@
+#include "step.qh"
 void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step
 {
-       if(this.move_flags & FL_ONGROUND)
+       if(IS_ONGROUND(this))
        {
                if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
                {
-                       this.move_flags &= ~FL_ONGROUND;
+                       UNSET_ONGROUND(this);
                        _Movetype_CheckVelocity(this);
                        _Movetype_FlyMove(this, dt, true, '0 0 0', 0);
                        _Movetype_LinkEdict(this, true);
diff --git a/qcsrc/common/physics/movetypes/step.qh b/qcsrc/common/physics/movetypes/step.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index f515a75f9fbbb09c17bd5abc78ef07312650372d..498852135b40fd0607ee78d1b86daff703e48ac5 100644 (file)
@@ -1,20 +1,21 @@
+#include "toss.qh"
 #include "../player.qh"
 
 void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
 {
-       if (this.move_flags & FL_ONGROUND)
+       if (IS_ONGROUND(this))
        {
-               if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
+               if (this.velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this))
                {
-                       this.move_flags &= ~FL_ONGROUND;
+                       UNSET_ONGROUND(this);
                }
-               else if (!this.move_groundentity)
+               else if (!this.groundentity)
                {
                        return;
                }
-               else if (this.move_suspendedinair && wasfreed(this.move_groundentity))
+               else if (this.move_suspendedinair && wasfreed(this.groundentity))
                {
-                       this.move_groundentity = NULL;
+                       this.groundentity = NULL;
                        return;
                }
        }
@@ -23,21 +24,27 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
 
        _Movetype_CheckVelocity(this);
 
-       if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
+       /*if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
        {
                this.move_didgravity = 1;
-               this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
+               this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
                    * dt
                    * (this.gravity ? this.gravity : 1)
                    * PHYS_GRAVITY(this);
+       }*/
+
+       if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS)
+       {
+               this.move_didgravity = true;
+               this.velocity_z -= (((this.gravity) ? this.gravity : 1) * PHYS_GRAVITY(this) * dt);
        }
 
-       this.move_angles = this.move_angles + this.move_avelocity * dt;
+       this.angles = this.angles + this.avelocity * dt;
 
        float movetime = dt;
        for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
        {
-               vector move = this.move_velocity * movetime;
+               vector move = this.velocity * movetime;
                _Movetype_PushEntity(this, move, true);
                if (wasfreed(this))
                        return;
@@ -57,45 +64,45 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
 
                if (this.move_movetype == MOVETYPE_BOUNCEMISSILE)
                {
-                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0);
-                       this.move_flags &= ~FL_ONGROUND;
+                       this.velocity = _Movetype_ClipVelocity(this.velocity, trace_plane_normal, 2.0);
+                       UNSET_ONGROUND(this);
                }
                else if (this.move_movetype == MOVETYPE_BOUNCE)
                {
-                       float bouncefac = this.move_bounce_factor;     if (!bouncefac)  bouncefac = 0.5;
-                       float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800;
-                       bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+                       float bouncefac = this.bouncefactor;     if (!bouncefac)  bouncefac = 0.5;
+                       float bstop = this.bouncestop; if (!bstop) bstop = 60 / 800;
+                       bstop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
 
-                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac);
+                       this.velocity = _Movetype_ClipVelocity(this.velocity, trace_plane_normal, 1 + bouncefac);
 
-                       float d = trace_plane_normal * this.move_velocity;
-                       if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
+                       float d = trace_plane_normal * this.velocity;
+                       if (trace_plane_normal.z > 0.7 && d < bstop && d > -bstop)
                        {
-                               this.move_flags |= FL_ONGROUND;
-                               this.move_groundentity = trace_ent;
-                               this.move_velocity = '0 0 0';
-                               this.move_avelocity = '0 0 0';
+                               SET_ONGROUND(this);
+                               this.groundentity = trace_ent;
+                               this.velocity = '0 0 0';
+                               this.avelocity = '0 0 0';
                        }
                        else
                        {
-                               this.move_flags &= ~FL_ONGROUND;
+                               UNSET_ONGROUND(this);
                        }
                }
                else
                {
-                       this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0);
+                       this.velocity = _Movetype_ClipVelocity(this.velocity, trace_plane_normal, 1.0);
                        if (trace_plane_normal.z > 0.7)
                        {
-                               this.move_flags |= FL_ONGROUND;
-                               this.move_groundentity = trace_ent;
+                               SET_ONGROUND(this);
+                               this.groundentity = trace_ent;
                                if (trace_ent.solid == SOLID_BSP)
                                        this.move_suspendedinair = true;
-                               this.move_velocity = '0 0 0';
-                               this.move_avelocity = '0 0 0';
+                               this.velocity = '0 0 0';
+                               this.avelocity = '0 0 0';
                        }
                        else
                        {
-                               this.move_flags &= ~FL_ONGROUND;
+                               UNSET_ONGROUND(this);
                        }
                }
 
@@ -104,12 +111,12 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
                        break;
 
                // DP revision 8918 (WHY...)
-               if (this.move_flags & FL_ONGROUND)
+               if (IS_ONGROUND(this))
                        break;
        }
 
-       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND))
-               this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
+       //if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !IS_ONGROUND(this))
+       //      this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
 
        _Movetype_CheckWaterTransition(this);
 }
diff --git a/qcsrc/common/physics/movetypes/toss.qh b/qcsrc/common/physics/movetypes/toss.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index e926246266c6761215058a7f293adc421e18562a..c20e82e8342825a9282142d288281ceffea2583d 100644 (file)
@@ -1,3 +1,4 @@
+#include "walk.qh"
 void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
 {
        vector stepnormal = '0 0 0';
@@ -9,15 +10,15 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
        if (GAMEPLAYFIX_UNSTICKPLAYERS(this))
                _Movetype_UnstickEntity(this);
 
-       bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP));
+       bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.flags & FL_WATERJUMP));
 
        _Movetype_CheckVelocity(this);
 
        // do a regular slide move unless it looks like you ran into a step
-       bool oldonground = (this.move_flags & FL_ONGROUND);
+       bool oldonground = IS_ONGROUND(this);
 
-       vector start_origin = this.move_origin;
-       vector start_velocity = this.move_velocity;
+       vector start_origin = this.origin;
+       vector start_velocity = this.velocity;
 
        int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0);
 
@@ -26,8 +27,8 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                // only try this if there was no floor in the way in the trace (no,
                // this check seems to be not REALLY necessary, because if clip & 1,
                // our trace will hit that thing too)
-               vector upmove = this.move_origin + '0 0 1';
-               vector downmove = this.move_origin - '0 0 1';
+               vector upmove = this.origin + '0 0 1';
+               vector downmove = this.origin - '0 0 1';
                int type;
                if (this.move_movetype == MOVETYPE_FLYMISSILE)
                        type = MOVE_MISSILE;
@@ -43,7 +44,7 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
 
        // if the move did not hit the ground at any point, we're not on ground
        if (!(clip & 1))
-               this.move_flags &= ~FL_ONGROUND;
+               UNSET_ONGROUND(this);
 
        _Movetype_CheckVelocity(this);
        _Movetype_LinkEdict(this, true);
@@ -51,17 +52,17 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
        if (clip & 8)  // teleport
                return;
 
-       if (this.move_flags & FL_WATERJUMP)
+       if (this.flags & FL_WATERJUMP)
                return;
 
        if (PHYS_NOSTEP(this))
                return;
 
-       vector originalmove_origin = this.move_origin;
-       vector originalmove_velocity = this.move_velocity;
+       vector originalorigin = this.origin;
+       vector originalvelocity = this.velocity;
        // originalmove_clip = clip;
-       int originalmove_flags = this.move_flags;
-       entity originalmove_groundentity = this.move_groundentity;
+       int originalflags = this.flags;
+       entity originalmove_groundentity = this.groundentity;
 
        // if move didn't block on a step, return
        if (clip & 2)
@@ -78,14 +79,14 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
 
                        // return if attempting to jump while airborn (unless sv_jumpstep)
                        if (!PHYS_JUMPSTEP(this))
-                               if (!oldonground && this.move_waterlevel == 0)
+                               if (!oldonground && this.waterlevel == 0)
                                        return;
                }
 
                // try moving up and forward to go up a step
                // back to start pos
-               this.move_origin = start_origin;
-               this.move_velocity = start_velocity;
+               this.origin = start_origin;
+               this.velocity = start_velocity;
 
                // move up
                vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this);
@@ -99,9 +100,9 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                }
 
                // move forward
-               this.move_velocity_z = 0;
+               this.velocity_z = 0;
                clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0);
-               this.move_velocity_z += start_velocity.z;
+               this.velocity_z += start_velocity.z;
                if (clip & 8)
                {
                        // we got teleported when upstepping... must abort the move
@@ -115,16 +116,16 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                // check for stuckness, possibly due to the limited precision of floats
                // in the clipping hulls
                if (clip
-                   && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125
-                   && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125)
+                   && fabs(originalorigin.y - this.origin.y) < 0.03125
+                   && fabs(originalorigin.x - this.origin.x) < 0.03125)
                {
                        // Con_Printf("wall\n");
                        // stepping up didn't make any progress, revert to original move
-                       this.move_origin = originalmove_origin;
-                       this.move_velocity = originalmove_velocity;
+                       this.origin = originalorigin;
+                       this.velocity = originalvelocity;
                        // clip = originalmove_clip;
-                       this.move_flags = originalmove_flags;
-                       this.move_groundentity = originalmove_groundentity;
+                       this.flags = originalflags;
+                       this.groundentity = originalmove_groundentity;
                        // now try to unstick if needed
                        // clip = SV_TryUnstick (ent, oldvel);
                        return;
@@ -137,7 +138,7 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                        _Movetype_WallFriction(this, stepnormal);
        }
        // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
-       else if (!GAMEPLAYFIX_STEPDOWN(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND))
+       else if (!GAMEPLAYFIX_STEPDOWN(this) || this.waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || IS_ONGROUND(this))
        {
                return;
        }
@@ -165,10 +166,10 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                // if the push down didn't end up on good ground, use the move without
                // the step up.  This happens near wall / slope combinations, and can
                // cause the player to hop up higher on a slope too steep to climb
-               this.move_origin = originalmove_origin;
-               this.move_velocity = originalmove_velocity;
-               this.move_flags = originalmove_flags;
-               this.move_groundentity = originalmove_groundentity;
+               this.origin = originalorigin;
+               this.velocity = originalvelocity;
+               this.flags = originalflags;
+               this.groundentity = originalmove_groundentity;
        }
 
        _Movetype_CheckVelocity(this);
diff --git a/qcsrc/common/physics/movetypes/walk.qh b/qcsrc/common/physics/movetypes/walk.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 5079bd97611d47d92c2251cc44570992be3592fa..58828c410195d1806ad16639a782a1fd89c67898 100644 (file)
@@ -13,7 +13,7 @@ bool Physics_Valid(string thecvar)
        return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar);
 }
 
-float Physics_ClientOption(entity this, string option)
+float Physics_ClientOption(entity this, string option, float defaultval)
 {
        if(Physics_Valid(this.cvar_cl_physics))
        {
@@ -27,40 +27,48 @@ float Physics_ClientOption(entity this, string option)
                if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
                        return cvar(s);
        }
-       return cvar(strcat("sv_", option));
+       return defaultval;
 }
 
 void Physics_UpdateStats(entity this, float maxspd_mod)
 {
-       STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod);
-       STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw"))
-               ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod)
+       STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw", autocvar_sv_airaccel_qw), maxspd_mod);
+       STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw))
+               ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw), maxspd_mod)
                : 0;
-       STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod;
-       STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking
+       STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod;
+       STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed", autocvar_sv_maxspeed) * maxspd_mod; // also slow walking
+
+       STAT(PL_MIN, this) = autocvar_sv_player_mins;
+       STAT(PL_MAX, this) = autocvar_sv_player_maxs;
+       STAT(PL_VIEW_OFS, this) = autocvar_sv_player_viewoffset;
+       STAT(PL_CROUCH_MIN, this) = autocvar_sv_player_crouch_mins;
+       STAT(PL_CROUCH_MAX, this) = autocvar_sv_player_crouch_maxs;
+       STAT(PL_CROUCH_VIEW_OFS, this) = autocvar_sv_player_crouch_viewoffset;
 
        // old stats
        // fix some new settings
-       STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor");
-       STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed");
-       STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed");
-       STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate");
-       STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel");
-       STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction");
-       STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol");
-       STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power");
-       STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty");
-       STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel");
-       STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed");
-       STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel");
-       STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio");
-       STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction");
-       STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate");
-       STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed");
-       STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate");
-       STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate");
-       STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity");
-       STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump");
+       STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor", autocvar_sv_airaccel_qw_stretchfactor);
+       STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed", autocvar_sv_maxairstrafespeed);
+       STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed", autocvar_sv_maxairspeed);
+       STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate", autocvar_sv_airstrafeaccelerate);
+       STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel", autocvar_sv_warsowbunny_turnaccel);
+       STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction", autocvar_sv_airaccel_sideways_friction);
+       STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol", autocvar_sv_aircontrol);
+       STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power", autocvar_sv_aircontrol_power);
+       STAT(MOVEVARS_AIRCONTROL_BACKWARDS, this) = Physics_ClientOption(this, "aircontrol_backwards", autocvar_sv_aircontrol_backwards);
+       STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty", autocvar_sv_aircontrol_penalty);
+       STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel", autocvar_sv_warsowbunny_airforwardaccel);
+       STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed", autocvar_sv_warsowbunny_topspeed);
+       STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel", autocvar_sv_warsowbunny_accel);
+       STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio", autocvar_sv_warsowbunny_backtosideratio);
+       STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction", autocvar_sv_friction);
+       STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate", autocvar_sv_accelerate);
+       STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed", autocvar_sv_stopspeed);
+       STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate", autocvar_sv_airaccelerate);
+       STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate", autocvar_sv_airstopaccelerate);
+       STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity);
+       STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump);
 }
 #endif
 
@@ -73,70 +81,29 @@ float IsMoveInDirection(vector mv, float ang) // key mix factor
        return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang);
 }
 
-float GeomLerp(float a, float lerp, float b)
+float GeomLerp(float a, float _lerp, float b)
 {
-       return a == 0 ? (lerp < 1 ? 0 : b)
-               : b == 0 ? (lerp > 0 ? 0 : a)
-               : a * pow(fabs(b / a), lerp);
-}
-
-#define unstick_offsets(X) \
-/* 1 no nudge (just return the original if this test passes) */ \
-       X(' 0.000  0.000  0.000') \
-/* 6 simple nudges */ \
-       X(' 0.000  0.000  0.125') X('0.000  0.000 -0.125') \
-       X('-0.125  0.000  0.000') X('0.125  0.000  0.000') \
-       X(' 0.000 -0.125  0.000') X('0.000  0.125  0.000') \
-/* 4 diagonal flat nudges */ \
-       X('-0.125 -0.125  0.000') X('0.125 -0.125  0.000') \
-       X('-0.125  0.125  0.000') X('0.125  0.125  0.000') \
-/* 8 diagonal upward nudges */ \
-       X('-0.125  0.000  0.125') X('0.125  0.000  0.125') \
-       X(' 0.000 -0.125  0.125') X('0.000  0.125  0.125') \
-       X('-0.125 -0.125  0.125') X('0.125 -0.125  0.125') \
-       X('-0.125  0.125  0.125') X('0.125  0.125  0.125') \
-/* 8 diagonal downward nudges */ \
-       X('-0.125  0.000 -0.125') X('0.125  0.000 -0.125') \
-       X(' 0.000 -0.125 -0.125') X('0.000  0.125 -0.125') \
-       X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \
-       X('-0.125  0.125 -0.125') X('0.125  0.125 -0.125') \
-/**/
-
-void PM_ClientMovement_Unstick(entity this)
-{
-       #define X(unstick_offset) \
-       { \
-               vector neworigin = unstick_offset + this.origin; \
-               tracebox(neworigin, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL), neworigin, MOVE_NORMAL, this); \
-               if (!trace_startsolid) \
-               { \
-                       setorigin(this, neworigin); \
-                       return; \
-               } \
-       }
-       unstick_offsets(X);
-       #undef X
+       return a == 0 ? (_lerp < 1 ? 0 : b)
+               : b == 0 ? (_lerp > 0 ? 0 : a)
+               : a * pow(fabs(b / a), _lerp);
 }
 
-void PM_ClientMovement_UpdateStatus(entity this, bool ground)
+void PM_ClientMovement_UpdateStatus(entity this)
 {
 #ifdef CSQC
        if(!IS_PLAYER(this))
                return;
-       // make sure player is not stuck
-       if(autocvar_cl_movement == 3)
-               PM_ClientMovement_Unstick(this);
 
        // set crouched
        bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
        if(this.hook && !wasfreed(this.hook))
                do_crouch = false;
+       if(this.waterlevel >= WATERLEVEL_SWIMMING)
+               do_crouch = false;
        if(hud != HUD_NORMAL)
                do_crouch = false;
        if(STAT(FROZEN, this))
                do_crouch = false;
-       if((activeweapon == WEP_SHOCKWAVE || activeweapon == WEP_SHOTGUN) && viewmodel.animstate_startframe == viewmodel.anim_fire2_x && time < viewmodel.weapon_nextthink)
-               do_crouch = false;
 
        if (do_crouch)
        {
@@ -148,156 +115,23 @@ void PM_ClientMovement_UpdateStatus(entity this, bool ground)
                // wants to stand, if currently crouching we need to check for a low ceiling first
                if (IS_DUCKED(this))
                {
-                       tracebox(this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), this.origin, MOVE_NORMAL, this);
+                       tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, MOVE_NORMAL, this);
                        if (!trace_startsolid) UNSET_DUCKED(this);
                }
        }
 
-       // set onground
-       vector origin1 = this.origin + '0 0 1';
-       vector origin2 = this.origin - '0 0 1';
-
-       if (ground && autocvar_cl_movement == 3)
-       {
-               tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this);
-               if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7)
-               {
-                       SET_ONGROUND(this);
-
-                       // this code actually "predicts" an impact; so let's clip velocity first
-                       this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal;
-               }
-               else
-                       UNSET_ONGROUND(this);
-       }
-
-       if(autocvar_cl_movement == 3)
-       {
-               // set watertype/waterlevel
-               origin1 = this.origin;
-               origin1.z += this.mins_z + 1;
-               this.waterlevel = WATERLEVEL_NONE;
-
-               int thepoint = pointcontents(origin1);
-
-               this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME);
-
-               if (this.watertype)
-               {
-                       this.waterlevel = WATERLEVEL_WETFEET;
-                       origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
-                       thepoint = pointcontents(origin1);
-                       if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
-                       {
-                               this.waterlevel = WATERLEVEL_SWIMMING;
-                               origin1.z = this.origin.z + 22;
-                               thepoint = pointcontents(origin1);
-                               if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME)
-                                       this.waterlevel = WATERLEVEL_SUBMERGED;
-                       }
-               }
-       }
-
-       if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
-               pmove_waterjumptime = 0;
+       if (IS_ONGROUND(this) || this.velocity.z <= 0 || PHYS_WATERJUMP_TIME(this) <= 0)
+               PHYS_WATERJUMP_TIME(this) = 0;
 #endif
 }
 
-void PM_ClientMovement_Move(entity this)
+void CPM_PM_Aircontrol(entity this, float dt, vector wishdir, float wishspeed)
 {
-#ifdef CSQC
-
-       PM_ClientMovement_UpdateStatus(this, false);
-       if(autocvar_cl_movement == 1)
-               return;
-
-       int bump;
-       float t;
-       float f;
-       vector neworigin;
-       vector currentorigin2;
-       vector neworigin2;
-       vector primalvelocity;
-
-       vector trace1_endpos = '0 0 0';
-       vector trace2_endpos = '0 0 0';
-       vector trace3_endpos = '0 0 0';
-       float trace1_fraction = 0;
-       float trace2_fraction = 0;
-       float trace3_fraction = 0;
-       vector trace1_plane_normal = '0 0 0';
-       vector trace2_plane_normal = '0 0 0';
-       vector trace3_plane_normal = '0 0 0';
-
-       primalvelocity = this.velocity;
-       for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++)
-       {
-               neworigin = this.origin + t * this.velocity;
-               tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this);
-               trace1_endpos = trace_endpos;
-               trace1_fraction = trace_fraction;
-               trace1_plane_normal = trace_plane_normal;
-               if(trace1_fraction < 1 && trace1_plane_normal_z == 0)
-               {
-                       // may be a step or wall, try stepping up
-                       // first move forward at a higher level
-                       currentorigin2 = this.origin;
-                       currentorigin2_z += PHYS_STEPHEIGHT(this);
-                       neworigin2 = neworigin;
-                       neworigin2_z += PHYS_STEPHEIGHT(this);
-                       tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
-                       trace2_endpos = trace_endpos;
-                       trace2_fraction = trace_fraction;
-                       trace2_plane_normal = trace_plane_normal;
-                       if(!trace_startsolid)
-                       {
-                               // then move down from there
-                               currentorigin2 = trace2_endpos;
-                               neworigin2 = trace2_endpos;
-                               neworigin2_z = this.origin_z;
-                               tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this);
-                               trace3_endpos = trace_endpos;
-                               trace3_fraction = trace_fraction;
-                               trace3_plane_normal = trace_plane_normal;
-                               // accept the new trace if it made some progress
-                               if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125)
-                               {
-                                       trace1_endpos = trace2_endpos;
-                                       trace1_fraction = trace2_fraction;
-                                       trace1_plane_normal = trace2_plane_normal;
-                                       trace1_endpos = trace3_endpos;
-                               }
-                       }
-               }
-
-               // check if it moved at all
-               if(trace1_fraction >= 0.001)
-                       setorigin(this, trace1_endpos);
-
-               // check if it moved all the way
-               if(trace1_fraction == 1)
-                       break;
+       float movity = IsMoveInDirection(this.movement, 0);
+       if(PHYS_AIRCONTROL_BACKWARDS(this))
+               movity += IsMoveInDirection(this.movement, 180);
 
-               // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
-               // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
-               // this got commented out in a change that supposedly makes the code match QW better
-               // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
-               if(trace1_plane_normal_z > 0.7)
-                       SET_ONGROUND(this);
-
-               t -= t * trace1_fraction;
-
-               f = (this.velocity * trace1_plane_normal);
-               this.velocity = this.velocity + -f * trace1_plane_normal;
-       }
-       if(PHYS_TELEPORT_TIME(this) > 0)
-               this.velocity = primalvelocity;
-#endif
-}
-
-void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
-{
-       float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1);
+       float k = 32 * (2 * movity - 1);
        if (k <= 0)
                return;
 
@@ -312,7 +146,7 @@ void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed)
 
        if (dot > 0) // we can't change direction while slowing down
        {
-               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH;
+               k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * dt;
                xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32);
                k *= PHYS_AIRCONTROL(this);
                this.velocity = normalize(this.velocity * xyspeed + wishdir * k);
@@ -332,7 +166,7 @@ float AdjustAirAccelQW(float accelqw, float factor)
 //   sv_airaccel_sideways_friction 0
 //   prvm_globalset server speedclamp_mode 1
 //     (or 2)
-void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
+void PM_Accelerate(entity this, float dt, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit)
 {
        float speedclamp = stretchfactor > 0 ? stretchfactor
        : accelqw < 0 ? 1 // full clamping, no stretch
@@ -348,7 +182,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        vector vel_xy = vec2(this.velocity);
        vector vel_perpend = vel_xy - vel_straight * wishdir;
 
-       float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+       float step = accel * dt * wishspeed0;
 
        float vel_xy_current  = vlen(vel_xy);
        if (speedlimit)
@@ -361,7 +195,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        if (sidefric < 0 && (vel_perpend*vel_perpend))
                // negative: only apply so much sideways friction to stay below the speed you could get by "braking"
        {
-               float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               float f = max(0, 1 + dt * wishspeed * sidefric);
                float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend);
                // assume: themin > 1
                // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend
@@ -377,7 +211,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
                }
        }
        else
-               vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric);
+               vel_perpend *= max(0, 1 - dt * wishspeed * sidefric);
 
        vel_xy = vel_straight * wishdir + vel_perpend;
 
@@ -396,7 +230,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed
        this.velocity = vel_xy + vel_z * '0 0 1';
 }
 
-void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
+void PM_AirAccelerate(entity this, float dt, vector wishdir, float wishspeed)
 {
        if (wishspeed == 0)
                return;
@@ -406,18 +240,18 @@ void PM_AirAccelerate(entity this, vector wishdir, float wishspeed)
        float curspeed = vlen(curvel);
 
        if (wishspeed > curspeed * 1.01)
-               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+               wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * dt);
        else
        {
                float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this)));
-               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH;
+               wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * dt;
        }
        vector wishvel = wishdir * wishspeed;
        vector acceldir = wishvel - curvel;
        float addspeed = vlen(acceldir);
        acceldir = normalize(acceldir);
 
-       float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH);
+       float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * dt);
 
        if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1)
        {
@@ -473,7 +307,7 @@ bool PlayerJump(entity this)
        }
 
        if (!doublejump)
-               if (!IS_ONGROUND(this))
+               if (!IS_ONGROUND(this) && !IS_ONSLICK(this))
                        return IS_JUMP_HELD(this);
 
        bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
@@ -510,11 +344,11 @@ bool PlayerJump(entity this)
                }
        }
 
-       if (!WAS_ONGROUND(this))
+       if (!WAS_ONGROUND(this) && !WAS_ONSLICK(this))
        {
 #ifdef SVQC
                if(autocvar_speedmeter)
-                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
+                       LOG_TRACE("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")");
 #endif
                if(this.lastground < time - 0.3)
                {
@@ -524,7 +358,7 @@ bool PlayerJump(entity this)
                }
 #ifdef SVQC
                if(this.jumppadcount > 1)
-                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
+                       LOG_TRACE(ftos(this.jumppadcount), "x jumppad combo");
                this.jumppadcount = 0;
 #endif
        }
@@ -532,6 +366,7 @@ bool PlayerJump(entity this)
        this.velocity_z += mjumpheight;
 
        UNSET_ONGROUND(this);
+       UNSET_ONSLICK(this);
        SET_JUMP_HELD(this);
 
 #ifdef SVQC
@@ -541,7 +376,7 @@ bool PlayerJump(entity this)
        animdecide_setaction(this, ANIMACTION_JUMP, true);
 
        if (autocvar_g_jump_grunt)
-               PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+               PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
 #endif
        return true;
 }
@@ -570,7 +405,7 @@ void CheckWaterJump(entity this)
                #ifdef SVQC
                        PHYS_TELEPORT_TIME(this) = time + 2;    // safety net
                #elif defined(CSQC)
-                       pmove_waterjumptime = 2;
+                       PHYS_WATERJUMP_TIME(this) = 2;
                #endif
                }
        }
@@ -646,15 +481,15 @@ float racecar_angle(float forward, float down)
                return ret * angle_mult;
 }
 
+#ifdef SVQC
 string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
 .float specialcommand_pos;
 void SpecialCommand(entity this)
 {
-#ifdef SVQC
        if (!CheatImpulse(this, CHIMPULSE_GIVE_ALL.impulse))
                LOG_INFO("A hollow voice says \"Plugh\".\n");
-#endif
 }
+#endif
 
 bool PM_check_specialcommand(entity this, int buttons)
 {
@@ -715,12 +550,12 @@ void PM_check_nickspam(entity this)
 #endif
 }
 
-void PM_check_punch(entity this)
+void PM_check_punch(entity this, float dt)
 {
 #ifdef SVQC
        if (this.punchangle != '0 0 0')
        {
-               float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH;
+               float f = vlen(this.punchangle) - 10 * dt;
                if (f > 0)
                        this.punchangle = normalize(this.punchangle) * f;
                else
@@ -729,7 +564,7 @@ void PM_check_punch(entity this)
 
        if (this.punchvector != '0 0 0')
        {
-               float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH;
+               float f = vlen(this.punchvector) - 30 * dt;
                if (f > 0)
                        this.punchvector = normalize(this.punchvector) * f;
                else
@@ -781,7 +616,8 @@ void PM_check_hitground(entity this)
     entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
        ? GS_FALL_METAL
        : GS_FALL;
-    GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+    float vol = ((IS_DUCKED(this)) ? VOL_MUFFLED : VOL_BASE);
+    GlobalSound(this, gs, CH_PLAYER, vol, VOICETYPE_PLAYERSOUND);
 #endif
 }
 
@@ -801,197 +637,40 @@ void PM_Footsteps(entity this)
                entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
                        ? GS_STEP_METAL
                        : GS_STEP;
-               GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+               GlobalSound(this, gs, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
        }
 #endif
 }
 
-void PM_check_blocked(entity this)
+void PM_check_slick(entity this)
 {
-#ifdef SVQC
-       if (!this.player_blocked)
+       if(!IS_ONGROUND(this))
                return;
-       this.movement = '0 0 0';
-       this.disableclientprediction = 1;
-#endif
-}
-
-void PM_fly(entity this, float maxspd_mod)
-{
-       // noclipping or flying
-       UNSET_ONGROUND(this);
-
-       this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
-       makevectors(this.v_angle);
-       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
-       vector wishvel = v_forward * this.movement.x
-                                       + v_right * this.movement.y
-                                       + '0 0 1' * this.movement.z;
-       // acceleration
-       vector wishdir = normalize(wishvel);
-       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
-       if(time >= PHYS_TELEPORT_TIME(this))
-#endif
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-       PM_ClientMovement_Move(this);
-}
-
-void PM_swim(entity this, float maxspd_mod)
-{
-       // swimming
-       UNSET_ONGROUND(this);
 
-       float jump = PHYS_INPUT_BUTTON_JUMP(this);
-       // water jump only in certain situations
-       // this mimics quakeworld code
-       if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
-       {
-               vector yawangles = '0 1 0' * this.v_angle.y;
-               makevectors(yawangles);
-               vector forward = v_forward;
-               vector spot = this.origin + 24 * forward;
-               spot_z += 8;
-               traceline(spot, spot, MOVE_NOMONSTERS, this);
-               if (trace_startsolid)
-               {
-                       spot_z += 24;
-                       traceline(spot, spot, MOVE_NOMONSTERS, this);
-                       if (!trace_startsolid)
-                       {
-                               this.velocity = forward * 50;
-                               this.velocity_z = 310;
-                       #ifdef CSQC
-                               pmove_waterjumptime = 2;
-                       #endif
-                               UNSET_ONGROUND(this);
-                               SET_JUMP_HELD(this);
-                       }
-               }
-       }
-       makevectors(this.v_angle);
-       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
-       vector wishvel = v_forward * this.movement.x
-                                       + v_right * this.movement.y
-                                       + '0 0 1' * this.movement.z;
-       if(this.viewloc)
-               wishvel.z = -160; // drift anyway
-       else if (wishvel == '0 0 0')
-               wishvel = '0 0 -60'; // drift towards bottom
-
-
-       vector wishdir = normalize(wishvel);
-       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
-
-       if (IS_DUCKED(this))
-       wishspeed *= 0.5;
-
-//     if (pmove_waterjumptime <= 0) // TODO: use
-    {
-               // water friction
-               float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
-               f = min(max(0, f), 1);
-               this.velocity *= f;
-
-               f = wishspeed - this.velocity * wishdir;
-               if (f > 0)
-               {
-                       float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
-                       this.velocity += accelspeed * wishdir;
-               }
+       if(!PHYS_SLICK_APPLYGRAVITY(this))
+               return;
 
-               // holding jump button swims upward slowly
-               if (jump && !this.viewloc)
-               {
-#if 0
-                       if (this.watertype & CONTENT_LAVA)
-                               this.velocity_z =  50;
-                       else if (this.watertype & CONTENT_SLIME)
-                               this.velocity_z =  80;
-                       else
-                       {
-                               if (IS_NEXUIZ_DERIVED(gamemode))
-#endif
-                                       this.velocity_z = 200;
-#if 0
-                               else
-                                       this.velocity_z = 100;
-                       }
-#endif
-               }
-       }
-       if(this.viewloc)
+       tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
        {
-               const float addspeed = wishspeed - this.velocity * wishdir;
-               if (addspeed > 0)
-               {
-                       const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
-                       this.velocity += accelspeed * wishdir;
-               }
+               UNSET_ONGROUND(this);
+               SET_ONSLICK(this);
        }
        else
-       {
-               // water acceleration
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-               PM_ClientMovement_Move(this);
-       }
+               UNSET_ONSLICK(this);
 }
 
-.vector oldmovement;
-void PM_ladder(entity this, float maxspd_mod)
+void PM_check_blocked(entity this)
 {
-       // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
-       UNSET_ONGROUND(this);
-
-       float g;
-       g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if (PHYS_ENTGRAVITY(this))
-               g *= PHYS_ENTGRAVITY(this);
-       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-       {
-               g *= 0.5;
-               this.velocity_z += g;
-       }
-
-       this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
-       makevectors(this.v_angle);
-       //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
-       vector wishvel = v_forward * this.movement_x
-                                       + v_right * this.movement_y
-                                       + '0 0 1' * this.movement_z;
-       if(this.viewloc)
-               wishvel.z = this.oldmovement.x;
-       this.velocity_z += g;
-       if (this.ladder_entity.classname == "func_water")
-       {
-               float f = vlen(wishvel);
-               if (f > this.ladder_entity.speed)
-                       wishvel *= (this.ladder_entity.speed / f);
-
-               this.watertype = this.ladder_entity.skin;
-               f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
-               if ((this.origin_z + this.view_ofs_z) < f)
-                       this.waterlevel = WATERLEVEL_SUBMERGED;
-               else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
-                       this.waterlevel = WATERLEVEL_SWIMMING;
-               else if ((this.origin_z + this.mins_z + 1) < f)
-                       this.waterlevel = WATERLEVEL_WETFEET;
-               else
-               {
-                       this.waterlevel = WATERLEVEL_NONE;
-                       this.watertype = CONTENT_EMPTY;
-               }
-       }
-       // acceleration
-       vector wishdir = normalize(wishvel);
-       float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-       if(time >= PHYS_TELEPORT_TIME(this))
-               // water acceleration
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
-       PM_ClientMovement_Move(this);
+#ifdef SVQC
+       if (!this.player_blocked)
+               return;
+       this.movement = '0 0 0';
+       this.disableclientprediction = 1;
+#endif
 }
 
-void PM_jetpack(entity this, float maxspd_mod)
+void PM_jetpack(entity this, float maxspd_mod, float dt)
 {
        //makevectors(this.v_angle.y * '0 1 0');
        makevectors(this.v_angle);
@@ -1012,11 +691,15 @@ void PM_jetpack(entity this, float maxspd_mod)
        float a_up = PHYS_JETPACK_ACCEL_UP(this);
        float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this);
 
+       if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(self)) { a_up = PHYS_JETPACK_REVERSE_THRUST(this); }
+
        wishvel_x *= a_side;
        wishvel_y *= a_side;
        wishvel_z *= a_up;
        wishvel_z += a_add;
 
+       if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(self)) { wishvel_z *= -1; }
+
        float best = 0;
        //////////////////////////////////////////////////////////////////////////////////////
        // finding the maximum over all vectors of above form
@@ -1072,7 +755,7 @@ void PM_jetpack(entity this, float maxspd_mod)
 
        fvel = min(1, vlen(wishvel) / best);
        if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
-               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel));
+               f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * dt * fvel));
        else
                f = 1;
 
@@ -1080,12 +763,12 @@ void PM_jetpack(entity this, float maxspd_mod)
 
        if (f > 0 && wishvel != '0 0 0')
        {
-               this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH;
+               this.velocity = this.velocity + wishvel * f * dt;
                UNSET_ONGROUND(this);
 
 #ifdef SVQC
                if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
-                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f;
+                       this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f;
 
                ITEMS_STAT(this) |= IT_USING_JETPACK;
 
@@ -1093,195 +776,6 @@ void PM_jetpack(entity this, float maxspd_mod)
                this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
 #endif
        }
-
-#ifdef CSQC
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if(autocvar_cl_movement == 3)
-       {
-               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       this.velocity_z -= g * 0.5;
-               else
-                       this.velocity_z -= g;
-       }
-       PM_ClientMovement_Move(this);
-       if(autocvar_cl_movement == 3)
-       {
-               if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
-                       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                               this.velocity_z -= g * 0.5;
-       }
-#endif
-}
-
-void PM_walk(entity this, float maxspd_mod)
-{
-       if (!WAS_ONGROUND(this))
-       {
-#ifdef SVQC
-               if (autocvar_speedmeter)
-                       LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
-               if (this.lastground < time - 0.3)
-                       this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
-#ifdef SVQC
-               if (this.jumppadcount > 1)
-                       LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
-               this.jumppadcount = 0;
-#endif
-       }
-
-       // walking
-       makevectors(this.v_angle.y * '0 1 0');
-       const vector wishvel = v_forward * this.movement.x
-                                               + v_right * this.movement.y;
-       // acceleration
-       const vector wishdir = normalize(wishvel);
-       float wishspeed = vlen(wishvel);
-       wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
-       if (IS_DUCKED(this)) wishspeed *= 0.5;
-
-       // apply edge friction
-       const float f2 = vlen2(vec2(this.velocity));
-       if (f2 > 0)
-       {
-               trace_dphitq3surfaceflags = 0;
-               tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
-               // TODO: apply edge friction
-               // apply ground friction
-               const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
-                       ? PHYS_FRICTION_SLICK(this)
-                       : PHYS_FRICTION(this);
-
-               float f = sqrt(f2);
-               f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
-               f = max(0, f);
-               this.velocity *= f;
-               /*
-                  Mathematical analysis time!
-
-                  Our goal is to invert this mess.
-
-                  For the two cases we get:
-                       v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
-                         = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
-                       v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
-                  and
-                       v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-                       v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-
-                  These cases would be chosen ONLY if:
-                       v0 < PHYS_STOPSPEED(this)
-                       v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
-                       v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-                  and, respectively:
-                       v0 >= PHYS_STOPSPEED(this)
-                       v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
-                       v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-                */
-       }
-       const float addspeed = wishspeed - this.velocity * wishdir;
-       if (addspeed > 0)
-       {
-               const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
-               this.velocity += accelspeed * wishdir;
-       }
-#ifdef CSQC
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if(autocvar_cl_movement == 3)
-       {
-               if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
-                       this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
-       }
-       if (vdist(this.velocity, >, 0))
-               PM_ClientMovement_Move(this);
-       if(autocvar_cl_movement == 3)
-       {
-               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
-                               this.velocity_z -= g * 0.5;
-       }
-#endif
-}
-
-void PM_air(entity this, float buttons_prev, float maxspd_mod)
-{
-       makevectors(this.v_angle.y * '0 1 0');
-       vector wishvel = v_forward * this.movement.x
-                                       + v_right * this.movement.y;
-       // acceleration
-       vector wishdir = normalize(wishvel);
-       float wishspeed = vlen(wishvel);
-
-#ifdef SVQC
-       if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
-       if(pmove_waterjumptime <= 0)
-#endif
-       {
-               float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
-
-               // apply air speed limit
-               float airaccelqw = PHYS_AIRACCEL_QW(this);
-               float wishspeed0 = wishspeed;
-               wishspeed = min(wishspeed, maxairspd);
-               if (IS_DUCKED(this))
-                       wishspeed *= 0.5;
-               float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
-
-               float accelerating = (this.velocity * wishdir > 0);
-               float wishspeed2 = wishspeed;
-
-               // CPM: air control
-               if (PHYS_AIRSTOPACCELERATE(this))
-               {
-                       vector curdir = normalize(vec2(this.velocity));
-                       airaccel += (PHYS_AIRSTOPACCELERATE(this)*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
-               }
-               // note that for straight forward jumping:
-               // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
-               // accel  = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
-               // -->
-               // dv/dt = accel * maxspeed (when slow)
-               // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
-               // log dv/dt = logaccel + logmaxspeed (when slow)
-               // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
-               float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
-               if (PHYS_MAXAIRSTRAFESPEED(this))
-                       wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
-               if (PHYS_AIRSTRAFEACCELERATE(this))
-                       airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
-               if (PHYS_AIRSTRAFEACCEL_QW(this))
-                       airaccelqw =
-               (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
-               *
-               (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
-               // !CPM
-
-               if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
-                       PM_AirAccelerate(this, wishdir, wishspeed2);
-               else {
-                   float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
-                       PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this));
-        }
-
-               if (PHYS_AIRCONTROL(this))
-                       CPM_PM_Aircontrol(this, wishdir, wishspeed2);
-       }
-#ifdef CSQC
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
-       if(autocvar_cl_movement == 3)
-       if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-               this.velocity_z -= g * 0.5;
-       else
-               this.velocity_z -= g;
-#endif
-       PM_ClientMovement_Move(this);
-#ifdef CSQC
-       if(autocvar_cl_movement == 3)
-       if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND))
-               if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-                       this.velocity_z -= g * 0.5;
-#endif
 }
 
 // used for calculating airshots
@@ -1297,262 +791,22 @@ bool IsFlying(entity this)
        return true;
 }
 
-void PM_Main(entity this)
-{
-       int buttons = PHYS_INPUT_BUTTON_MASK(this);
-#ifdef CSQC
-       this.items = STAT(ITEMS);
-
-       this.movement = PHYS_INPUT_MOVEVALUES(this);
-
-       this.spectatorspeed = STAT(SPECTATORSPEED);
-
-       this.team = myteam + 1; // is this correct?
-       if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
-               UNSET_JUMP_HELD(this); // canjump = true
-       pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
-
-       PM_ClientMovement_UpdateStatus(this, true);
-#endif
-
-       this.oldmovement = this.movement;
-
-
-#ifdef SVQC
-       WarpZone_PlayerPhysics_FixVAngle(this);
-#endif
-       float maxspeed_mod = 1;
-       maxspeed_mod *= PHYS_HIGHSPEED(this);
-
-#ifdef SVQC
-       Physics_UpdateStats(this, maxspeed_mod);
-
-       if (this.PlayerPhysplug)
-               if (this.PlayerPhysplug(this))
-                       return;
-#endif
-
-#ifdef SVQC
-       anticheat_physics(this);
-#endif
-
-       if (PM_check_specialcommand(this, buttons))
-               return;
-#ifdef SVQC
-       if (sv_maxidle > 0)
-       {
-               if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
-                       this.parm_idlesince = time;
-       }
-#endif
-       int buttons_prev = this.buttons_old;
-       this.buttons_old = buttons;
-       this.movement_old = this.movement;
-       this.v_angle_old = this.v_angle;
-
-       PM_check_nickspam(this);
-
-       PM_check_punch(this);
-#ifdef SVQC
-       if (IS_BOT_CLIENT(this))
-       {
-               if (playerdemo_read(this))
-                       return;
-               bot_think(this);
-       }
-#endif
-
-#ifdef SVQC
-       if (IS_PLAYER(this))
-       {
-               const bool allowed_to_move = (time >= game_starttime);
-               if (!allowed_to_move)
-               {
-                       this.velocity = '0 0 0';
-                       this.movetype = MOVETYPE_NONE;
-                       this.disableclientprediction = 2;
-               }
-               else if (this.disableclientprediction == 2)
-               {
-                       if (this.movetype == MOVETYPE_NONE)
-                               this.movetype = MOVETYPE_WALK;
-                       this.disableclientprediction = 0;
-               }
-       }
-#endif
-
-#ifdef SVQC
-       if (this.movetype == MOVETYPE_NONE)
-               return;
-
-       // when we get here, disableclientprediction cannot be 2
-       this.disableclientprediction = 0;
-#endif
-
-       viewloc_PlayerPhysics(this);
-
-       PM_check_frozen(this);
-
-       PM_check_blocked(this);
-
-       maxspeed_mod = 1;
-
-       if (this.in_swamp)
-               maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-
-       // conveyors: first fix velocity
-       if (this.conveyor.state)
-               this.velocity -= this.conveyor.movedir;
-
-       MUTATOR_CALLHOOK(PlayerPhysics, this);
-
-       if (!IS_PLAYER(this))
-       {
-#ifdef SVQC
-               maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
-               if (!this.spectatorspeed)
-                       this.spectatorspeed = maxspeed_mod;
-               if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
-               {
-                       if (this.lastclassname != STR_PLAYER)
-                       {
-                               if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
-                                       this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
-                               else if (this.impulse == 11)
-                                       this.spectatorspeed = maxspeed_mod;
-                               else if (this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
-                                       this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
-                               else if (this.impulse >= 1 && this.impulse <= 9)
-                                       this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
-                       } // otherwise just clear
-                       this.impulse = 0;
-               }
-#endif
-               maxspeed_mod = this.spectatorspeed;
-       }
-#ifdef SVQC
-
-       float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
-       if(this.speed != spd)
-       {
-               this.speed = spd;
-               string temps = ftos(spd);
-               stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
-               stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
-               stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
-               stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
-       }
-
-       if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min)
-       {
-               this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
-               stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
-       }
-       if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max)
-       {
-               this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
-               stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
-       }
-#endif
-
-       if(IS_DEAD(this))
-       {
-               // handle water here
-               vector midpoint = ((this.absmin + this.absmax) * 0.5);
-               if(pointcontents(midpoint) == CONTENT_WATER)
-               {
-                       this.velocity = this.velocity * 0.5;
-
-                       // do we want this?
-                       //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
-                               //{ this.velocity_z = 70; }
-               }
-               goto end;
-       }
-
-#ifdef SVQC
-       if (!this.fixangle)
-               this.angles = '0 1 0' * this.v_angle.y;
-#endif
-
-       if (IS_PLAYER(this) && IS_ONGROUND(this))
-       {
-               PM_check_hitground(this);
-               PM_Footsteps(this);
-       }
-
-#ifdef SVQC
-       if(IsFlying(this))
-               this.wasFlying = 1;
-#endif
-
-       if (IS_PLAYER(this))
-               CheckPlayerJump(this);
-
-       if (this.flags & FL_WATERJUMP)
-       {
-               this.velocity_x = this.movedir.x;
-               this.velocity_y = this.movedir.y;
-               if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE
-               #ifdef CSQC
-                       || pmove_waterjumptime <= 0
-               #endif
-                       )
-               {
-                       this.flags &= ~FL_WATERJUMP;
-                       PHYS_TELEPORT_TIME(this) = 0;
-               #ifdef CSQC
-                       pmove_waterjumptime = 0;
-               #endif
-               }
-       }
-
-       else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
-               { }
-
-#ifdef SVQC
-       else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#elif defined(CSQC)
-       else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#endif
-               PM_fly(this, maxspeed_mod);
-
-       else if (this.waterlevel >= WATERLEVEL_SWIMMING)
-               PM_swim(this, maxspeed_mod);
-
-       else if (time < this.ladder_time)
-               PM_ladder(this, maxspeed_mod);
-
-       else if (ITEMS_STAT(this) & IT_USING_JETPACK)
-               PM_jetpack(this, maxspeed_mod);
-
-       else if (IS_ONGROUND(this))
-               PM_walk(this, maxspeed_mod);
-
-       else
-               PM_air(this, buttons_prev, maxspeed_mod);
-
-LABEL(end)
-       if (IS_ONGROUND(this))
-               this.lastground = time;
-
-       // conveyors: then break velocity again
-       if(this.conveyor.state)
-               this.velocity += this.conveyor.movedir;
-
-       this.lastflags = this.flags;
-
-       this.lastclassname = this.classname;
-}
 
+void sys_phys_update(entity this, float dt);
 #if defined(SVQC)
-void SV_PlayerPhysics()
+void SV_PlayerPhysics(entity this)
 #elif defined(CSQC)
 void CSQC_ClientMovement_PlayerMove_Frame(entity this)
 #endif
 {
+       sys_phys_update(this, PHYS_INPUT_TIMELENGTH);
+
 #ifdef SVQC
-       ENGINE_EVENT();
+       this.pm_frametime = frametime;
+#elif defined(CSQC)
+       if((ITEMS_STAT(this) & IT_USING_JETPACK) && !IS_DEAD(this) && !intermission)
+               this.csqcmodel_modelflags |= MF_ROCKET;
+       else
+               this.csqcmodel_modelflags &= ~MF_ROCKET;
 #endif
-       PM_Main(this);
 }
index fd1b610de68811c11c405afdd6eef9f8eed43b58..39cc573ee99d9bc96cff1177dd1b3c457d26cfa1 100644 (file)
@@ -1,8 +1,9 @@
-#ifndef COMMON_PHYSICS_H
-#define COMMON_PHYSICS_H
+#pragma once
 
 // Client/server mappings
 
+.float pm_frametime;
+
 .entity conveyor;
 
 .float race_penalty;
@@ -23,7 +24,7 @@
 .vector v_angle_old;
 .string lastclassname;
 
-.float(entity) PlayerPhysplug;
+.float(entity,float) PlayerPhysplug;
 float AdjustAirAccelQW(float accelqw, float factor);
 
 bool IsFlying(entity a);
@@ -35,6 +36,7 @@ bool IsFlying(entity a);
 #define GAMEPLAYFIX_STEPDOWN(s)             STAT(GAMEPLAYFIX_STEPDOWN, s)
 #define GAMEPLAYFIX_STEPMULTIPLETIMES(s)    STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s)
 #define GAMEPLAYFIX_UNSTICKPLAYERS(s)       STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s)
+#define GAMEPLAYFIX_WATERTRANSITION(s) STAT(GAMEPLAYFIX_WATERTRANSITION, s)
 
 #define PHYS_ACCELERATE(s)                  STAT(MOVEVARS_ACCELERATE, s)
 #define PHYS_AIRACCELERATE(s)               STAT(MOVEVARS_AIRACCELERATE, s)
@@ -44,6 +46,7 @@ bool IsFlying(entity a);
 #define PHYS_AIRCONTROL(s)                  STAT(MOVEVARS_AIRCONTROL, s)
 #define PHYS_AIRCONTROL_PENALTY(s)          STAT(MOVEVARS_AIRCONTROL_PENALTY, s)
 #define PHYS_AIRCONTROL_POWER(s)            STAT(MOVEVARS_AIRCONTROL_POWER, s)
+#define PHYS_AIRCONTROL_BACKWARDS(s)        STAT(MOVEVARS_AIRCONTROL_BACKWARDS, s)
 #define PHYS_AIRSPEEDLIMIT_NONQW(s)         STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s)
 #define PHYS_AIRSTOPACCELERATE(s)           STAT(MOVEVARS_AIRSTOPACCELERATE, s)
 #define PHYS_AIRSTRAFEACCELERATE(s)         STAT(MOVEVARS_AIRSTRAFEACCELERATE, s)
@@ -67,6 +70,7 @@ bool IsFlying(entity a);
 #define PHYS_JETPACK_FUEL(s)                STAT(JETPACK_FUEL, s)
 #define PHYS_JETPACK_MAXSPEED_SIDE(s)       STAT(JETPACK_MAXSPEED_SIDE, s)
 #define PHYS_JETPACK_MAXSPEED_UP(s)         STAT(JETPACK_MAXSPEED_UP, s)
+#define PHYS_JETPACK_REVERSE_THRUST(s)         STAT(JETPACK_REVERSE_THRUST, s)
 
 #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s)
 #define PHYS_JUMPSTEP(s)                    STAT(MOVEVARS_JUMPSTEP, s)
@@ -93,6 +97,8 @@ bool IsFlying(entity a);
 
 #define UPWARD_VELOCITY_CLEARS_ONGROUND(s)  STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s)
 
+#define PHYS_SLICK_APPLYGRAVITY(s)             STAT(SLICK_APPLYGRAVITY, s)
+
 #define PHYS_INPUT_BUTTON_ATCK(s)           PHYS_INPUT_BUTTON_BUTTON1(s)
 #define PHYS_INPUT_BUTTON_JUMP(s)           PHYS_INPUT_BUTTON_BUTTON2(s)
 #define PHYS_INPUT_BUTTON_ATCK2(s)          PHYS_INPUT_BUTTON_BUTTON3(s)
@@ -115,6 +121,7 @@ STATIC_INIT(PHYS_INPUT_BUTTON_HOOK)
 #define PHYS_INPUT_BUTTON_PRYDON(s)         PHYS_INPUT_BUTTON_BUTTON_PRYDON(s)
 #define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s)     PHYS_INPUT_BUTTON_BUTTON9(s)
 #define PHYS_INPUT_BUTTON_JETPACK(s)        PHYS_INPUT_BUTTON_BUTTON10(s)
+#define PHYS_INPUT_BUTTON_DODGE(s)                     PHYS_INPUT_BUTTON_BUTTON11(s)
 
 #ifdef CSQC
 STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
@@ -122,6 +129,12 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
        localcmd("alias +jetpack +button10\n");
     localcmd("alias -jetpack -button10\n");
 }
+
+STATIC_INIT(PHYS_INPUT_BUTTON_DODGE)
+{
+    localcmd("alias +dodge +button11\n");
+    localcmd("alias -dodge -button11\n");
+}
 #endif
 
 // if more buttons are needed, start using impulse bits as buttons
@@ -152,18 +165,23 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
 #define UNSET_JUMP_HELD(s)                  ((s).flags |= FL_JUMPRELEASED)
 
 #define WAS_ONGROUND(s)                     boolean((s).lastflags & FL_ONGROUND)
+#define WAS_ONSLICK(s)                      boolean((s).lastflags & FL_ONSLICK)
 
 #define ITEMS_STAT(s)                       ((s).items)
 
 .float teleport_time;
+#define PHYS_TELEPORT_TIME(s)               ((s).teleport_time)
+
+.float waterjump_time;
+#define PHYS_WATERJUMP_TIME(s)               ((s).waterjump_time)
 
 #ifdef CSQC
 
+       #define PHYS_FIXANGLE(s) ('0 0 0')
+
        string autocvar_cl_jumpspeedcap_min;
        string autocvar_cl_jumpspeedcap_max;
 
-       noref float pmove_waterjumptime;
-
        const int FL_WATERJUMP = 2048;  // player jumping out of water
        const int FL_JUMPRELEASED = 4096;  // for jump debouncing
 
@@ -177,9 +195,9 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
        .entity hook;
 
 // TODO
-       #define IS_CLIENT(s)                        ((s).isplayermodel)
+       #define IS_CLIENT(s)                        ((s).isplayermodel || (s) == csqcplayer)
        #define IS_PLAYER(s)                        ((s).isplayermodel)
-       #define IS_NOT_A_CLIENT(s)                  (!(s).isplayermodel)
+       #define IS_NOT_A_CLIENT(s)                  (!(s).isplayermodel && (s) != csqcplayer)
        #define isPushable(s)                       ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE))
 
        //float player_multijump;
@@ -187,8 +205,6 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
 
        #define PHYS_GRAVITY(s)                     STAT(MOVEVARS_GRAVITY, s)
 
-       #define PHYS_TELEPORT_TIME(s)               ((s).teleport_time)
-
        #define TICRATE                             ticrate
 
        #define PHYS_INPUT_ANGLES(s)                input_angles
@@ -237,8 +253,12 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
 
 #elif defined(SVQC)
 
+       #define PHYS_FIXANGLE(s) ((s).fixangle)
+
        bool Physics_Valid(string thecvar);
 
+       void Physics_UpdateStats(entity this, float maxspd_mod);
+
        .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
        .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED);
 
@@ -246,8 +266,6 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
        .string jumpspeedcap_min;
        .string jumpspeedcap_max;
 
-       #define PHYS_TELEPORT_TIME(s)               ((s).teleport_time)
-
        #define PHYS_GRAVITY(s)                     autocvar_sv_gravity
 
        #define TICRATE sys_frametime
@@ -319,5 +337,3 @@ NET_HANDLE(setpause, bool)
        return true;
 }
 #endif
-
-#endif
index 7e99bebea7480cfe32a9e32e670b3e58945f2a5e..b879b63b09587a8846ec9bf910154b796c4cfab3 100644 (file)
@@ -1,13 +1,14 @@
+#include "playerstats.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include "constants.qh"
     #include "util.qh"
-    #include <common/weapons/all.qh>
-    #include "../server/weapons/accuracy.qh"
+    #include <common/weapons/_all.qh>
+    #include "../server/anticheat.qh"
     #include "../server/defs.qh"
-    #include "playerstats.qh"
     #include "../server/scores.qh"
+    #include "../server/weapons/accuracy.qh"
 #endif
 
 #ifdef SVQC
@@ -141,6 +142,7 @@ void PlayerStats_GameReport_FinalizePlayer(entity p)
                PS_GR_P_ADDVAL(p, PLAYERSTATS_JOINS, 1);
 
        PlayerStats_GameReport_Accuracy(p);
+       anticheat_report_to_playerstats(p);
 
        if(IS_REAL_CLIENT(p))
        {
@@ -249,6 +251,8 @@ void PlayerStats_GameReport_Init() // initiated before InitGameplayMode so that
                PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_BOTLIKE);
                PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD);
                PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM);
+
+               anticheat_register_to_playerstats();
        }
        else { PlayerStats_GameReport_DelayMapVote = false; }
 }
@@ -379,9 +383,9 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
                {
                        // url_fclose is processing, we got a response for writing the data
                        // this must come from HTTP
-                       LOG_TRACE("Got response from player stats server:\n");
-                       while((s = url_fgets(fh))) { LOG_TRACE("  ", s, "\n"); }
-                       LOG_TRACE("End of response.\n");
+                       LOG_TRACE("Got response from player stats server:");
+                       while((s = url_fgets(fh))) { LOG_TRACE("  ", s); }
+                       LOG_TRACE("End of response.");
                        url_fclose(fh);
                        break;
                }
@@ -389,7 +393,7 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
                case URL_READY_CLOSED:
                {
                        // url_fclose has finished
-                       LOG_TRACE("Player stats written\n");
+                       LOG_TRACE("Player stats written");
                        PlayerStats_GameReport_DelayMapVote = false;
                        if(PS_GR_OUT_DB >= 0)
                        {
@@ -416,19 +420,21 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
 
 void PlayerStats_PlayerBasic(entity joiningplayer, float newrequest)
 {
+       PlayerScore_Add(joiningplayer, SP_ELO, -1);
        // http://stats.xonotic.org/player/GgXRw6piDtFIbMArMuiAi8JG4tiin8VLjZgsKB60Uds=/elo.txt
        if(autocvar_g_playerstats_playerbasic_uri != "")
        {
                string uri = autocvar_g_playerstats_playerbasic_uri;
-               if(joiningplayer.crypto_idfp != "")
-               {
+               if (joiningplayer.crypto_idfp == "") {
+                       PlayerScore_Add(joiningplayer, SP_ELO, -1);
+               } else {
                        // create the database if it doesn't already exist
                        if(PS_B_IN_DB < 0)
                                PS_B_IN_DB = db_create();
 
                        // now request the information
                        uri = strcat(uri, "/player/", uri_escape(uri_escape(uri_escape(joiningplayer.crypto_idfp))), "/elo.txt");
-                       LOG_TRACE("Retrieving playerstats from URL: ", uri, "\n");
+                       LOG_TRACE("Retrieving playerstats from URL: ", uri);
                        url_single_fopen(
                                uri,
                                FILE_APPEND,
@@ -452,6 +458,7 @@ void PlayerStats_PlayerBasic(entity joiningplayer, float newrequest)
        else
        {
                // server has this disabled, kill the DB and set status to idle
+               PlayerScore_Add(joiningplayer, SP_ELO, -1);
                if(PS_B_IN_DB >= 0)
                {
                        db_close(PS_B_IN_DB);
@@ -466,12 +473,10 @@ void PlayerStats_PlayerBasic_CheckUpdate(entity joiningplayer)
 {
        // determine whether we should retrieve playerbasic information again
 
-       #if 0
-       LOG_INFOF("PlayerStats_PlayerBasic_CheckUpdate('%s'): %f\n",
+       LOG_TRACEF("PlayerStats_PlayerBasic_CheckUpdate('%s'): %f",
                joiningplayer.netname,
                time
        );
-       #endif
 
        // TODO: check to see if this playerid is inside the database already somehow...
        // for now we'll just check the field, but this won't work for players who disconnect and reconnect properly
@@ -491,7 +496,7 @@ void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status)
        {
                case URL_READY_CANWRITE:
                {
-                       LOG_TRACE("-- Sending data to player stats server\n");
+                       LOG_TRACE("-- Sending data to player stats server");
                        /*url_fputs(fh, "V 1\n");
                        #ifdef WATERMARK
                        url_fputs(fh, sprintf("R %s\n", WATERMARK));
@@ -503,76 +508,76 @@ void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status)
                        url_fputs(fh, sprintf("m %s %s\n", cvar_string("_cl_playermodel"), cvar_string("_cl_playerskin"))); // model/skin
                        */url_fputs(fh, "\n");
                        url_fclose(fh);
-                       break;
+                       return;
                }
 
                case URL_READY_CANREAD:
                {
-                       string s = "";
-                       LOG_TRACE("-- Got response from player stats server:\n");
-                       //string gametype = string_null;
-                       while((s = url_fgets(fh)))
-                       {
-                               LOG_TRACE("  ", s, "\n");
-                               /*
+                       bool handled = false;
+                       string gt = string_null;
+                       for (string s = ""; (s = url_fgets(fh)); ) {
+                               int n = tokenizebyseparator(s, " "); // key value? data
+                               if (n == 1) continue;
                                string key = "", value = "", data = "";
-
-                               n = tokenizebyseparator(s, " "); // key (value) data
-                               if (n == 1)
-                                       continue;
-                               else if (n == 2)
-                               {
-                               key = argv(0);
-                               data = argv(1);
-                               }
-                               else if (n >= 3)
-                               {
-                                                               key = argv(0);
-                                                               value = argv(1);
-                                                               data = argv(2);
+                               if (n == 2) {
+                    key = argv(0);
+                    data = argv(1);
+                               } else if (n >= 3) {
+                    key = argv(0);
+                    value = argv(1);
+                    data = argv(2);
                                }
-
-                               if (data == "")
-                               continue;
-
-                               if (key == "#")
-                               continue;
-                               else if (key == "V")
-                               PlayerInfo_AddItem(p, "_version", data);
-                               else if (key == "R")
-                               PlayerInfo_AddItem(p, "_release", data);
-                               else if (key == "T")
-                               PlayerInfo_AddItem(p, "_time", data);
-                               else if (key == "S")
-                               PlayerInfo_AddItem(p, "_statsurl", data);
-                               else if (key == "P")
-                               PlayerInfo_AddItem(p, "_hashkey", data);
-                               else if (key == "n")
-                               PlayerInfo_AddItem(p, "_playernick", data);
-                               else if (key == "i")
-                               PlayerInfo_AddItem(p, "_playerid", data);
-                               else if (key == "G")
-                               gametype = data;
-                               else if (key == "e" && value != "")
-                               {
-                               if (gametype == "")
-                               PlayerInfo_AddItem(p, value, data);
-                               else
-                               PlayerInfo_AddItem(p, sprintf("%s/%s", gametype, value), data);
-                               }
-                               else
-                               continue;
-                               */
+                switch (key) {
+                    case "V":
+                        // PlayerInfo_AddItem(p, "_version", data);
+                        break;
+                    case "R":
+                        // PlayerInfo_AddItem(p, "_release", data);
+                        break;
+                    case "T":
+                        // PlayerInfo_AddItem(p, "_time", data);
+                        break;
+                    case "S":
+                        // PlayerInfo_AddItem(p, "_statsurl", data);
+                        break;
+                    case "P":
+                        // PlayerInfo_AddItem(p, "_hashkey", data);
+                        break;
+                    case "n":
+                        // PlayerInfo_AddItem(p, "_playernick", data);
+                        break;
+                    case "i":
+                        // PlayerInfo_AddItem(p, "_playerid", data);
+                        // p.xonstat_id = stof(data);
+                        break;
+                    case "G":
+                        gt = data;
+                        break;
+                    case "e":
+                        LOG_TRACE("G: ", gt);
+                        LOG_TRACE("e: ", data);
+                        if (gt == GetGametype()) {
+                            handled = true;
+                            float e = stof(data);
+                            PlayerScore_Add(p, SP_ELO, +1 + e);
+                        }
+                        if (gt == "") {
+                            // PlayerInfo_AddItem(p, value, data);
+                        } else {
+                            // PlayerInfo_AddItem(p, sprintf("%s/%s", gt, value), data);
+                        }
+                        break;
+                }
                        }
-                       LOG_TRACE("-- End of response.\n");
                        url_fclose(fh);
+                       if (handled) return;
                        break;
                }
                case URL_READY_CLOSED:
                {
                        // url_fclose has finished
                        LOG_INFO("Player stats synchronized with server\n");
-                       break;
+                       return;
                }
 
                case URL_READY_ERROR:
@@ -582,6 +587,7 @@ void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status)
                        break;
                }
        }
+       PlayerScore_Add(p, SP_ELO, -1);
 }
 #endif // SVQC
 
@@ -617,7 +623,7 @@ void PlayerStats_PlayerDetail_AddItem(string event, string data)
 
        // now actually set the event data
        db_put(PS_D_IN_DB, sprintf("#%s", event), data);
-       LOG_TRACE("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB\n");
+       LOG_TRACE("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB");
 }
 
 void PlayerStats_PlayerDetail()
@@ -630,7 +636,7 @@ void PlayerStats_PlayerDetail()
                        PS_D_IN_DB = db_create();
 
                //uri = strcat(uri, "/player/", uri_escape(crypto_getmyidfp(0)));
-               LOG_TRACE("Retrieving playerstats from URL: ", autocvar_g_playerstats_playerdetail_uri, "\n");
+               LOG_TRACE("Retrieving playerstats from URL: ", autocvar_g_playerstats_playerdetail_uri);
                url_single_fopen(
                        autocvar_g_playerstats_playerdetail_uri,
                        FILE_APPEND,
@@ -685,7 +691,7 @@ void PlayerStats_PlayerDetail_Handler(entity fh, entity unused, float status)
        {
                case URL_READY_CANWRITE:
                {
-                       LOG_TRACE("PlayerStats_PlayerDetail_Handler(): Sending data to player stats server...\n");
+                       LOG_TRACE("PlayerStats_PlayerDetail_Handler(): Sending data to player stats server...");
                        url_fputs(fh, "V 1\n");
                        #ifdef WATERMARK
                        url_fputs(fh, sprintf("R %s\n", WATERMARK));
index ec309c4f4c191fc9d46500e57ff8de3d0571e2ab..1fafb13978dc9c43c1467ff3accb10089c55b6ad 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef PLAYERSTATS_H
-#define PLAYERSTATS_H
+#pragma once
 
 #ifdef SVQC
 //float PS_PM_IN_DB = -1;   // playerstats_prematch_in_db      // db for info COLLECTED at the beginning of a match
@@ -41,6 +40,7 @@ const string PLAYERSTATS_SCOREBOARD_POS = "scoreboardpos";
 
 const string PLAYERSTATS_TOTAL = "total-";
 const string PLAYERSTATS_SCOREBOARD = "scoreboard-";
+const string PLAYERSTATS_ANTICHEAT = "anticheat-";
 
 const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3 = "achievement-kill-spree-3";
 const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5 = "achievement-kill-spree-5";
@@ -116,4 +116,3 @@ void PlayerStats_PlayerDetail();
 void PlayerStats_PlayerDetail_CheckUpdate();
 void PlayerStats_PlayerDetail_Handler(entity fh, entity p, float status);
 #endif
-#endif
index 69a95be0cce22074d75686a5aaf52c90c017ad80..4394e174067ab93c12236e3f7b00fa574ffb65c6 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "../teams.qh"
 string W_Sound(string w_snd);
+string Item_Sound(string it_snd);
 
 SOUND(ARC_FIRE, W_Sound("arc_fire"));
 SOUND(ARC_LOOP, W_Sound("arc_loop"));
@@ -236,20 +237,20 @@ SOUND(NADE_BEEP, "overkill/grenadebip");
 
 SOUND(BUFF_LOST, "relics/relic_effect");
 
-SOUND(POWEROFF, "misc/poweroff");
-SOUND(POWERUP, "misc/powerup");
-SOUND(SHIELD_RESPAWN, "misc/shield_respawn");
-SOUND(STRENGTH_RESPAWN, "misc/strength_respawn");
+SOUND(POWEROFF, Item_Sound("poweroff"));
+SOUND(POWERUP, Item_Sound("powerup"));
+SOUND(SHIELD_RESPAWN, Item_Sound("shield_respawn"));
+SOUND(STRENGTH_RESPAWN, Item_Sound("strength_respawn"));
 
-SOUND(ARMOR25, "misc/armor25");
+SOUND(ARMOR25, Item_Sound("armor25"));
 SOUND(ARMORIMPACT, "misc/armorimpact");
 SOUND(BODYIMPACT1, "misc/bodyimpact1");
 SOUND(BODYIMPACT2, "misc/bodyimpact2");
 
-SOUND(ITEMPICKUP, "misc/itempickup");
-SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown");
-SOUND(ITEMRESPAWN, "misc/itemrespawn");
-SOUND(MEGAHEALTH, "misc/megahealth");
+SOUND(ITEMPICKUP, Item_Sound("itempickup"));
+SOUND(ITEMRESPAWNCOUNTDOWN, Item_Sound("itemrespawncountdown"));
+SOUND(ITEMRESPAWN, Item_Sound("itemrespawn"));
+SOUND(MEGAHEALTH, Item_Sound("megahealth"));
 
 SOUND(LAVA, "player/lava");
 SOUND(SLIME, "player/slime");
index 9096dddfa8c0e6a31dd1dc2d3f5f8ba069df4b56..b3bdf99e9177e765d1280e880fa6b5ecdef1fbed 100644 (file)
@@ -1,3 +1,4 @@
+#include "all.qh"
 #ifdef SVQC
 
 bool autocvar_bot_sound_monopoly;
index a7fe25f17d1e324d07069f46e16057efbd22ba96..b7b296341b5a02ba5829035e158f13ad673cbefe 100644 (file)
@@ -1,9 +1,8 @@
-#ifndef SOUNDS_ALL_H
-#define SOUNDS_ALL_H
+#pragma once
 
 #include "sound.qh"
 
-REGISTRY(Sounds, BITS(8))
+REGISTRY(Sounds, BITS(9))
 #define Sounds_from(i) _Sounds_from(i, SND_Null)
 REGISTER_REGISTRY(Sounds)
 
@@ -21,4 +20,3 @@ PRECACHE(Sounds) {
 SOUND(Null, "misc/null");
 #include "all.inc"
 #include "all.qc"
-#endif
index 90b2758aca3569e841d6cfa185ca2a556847bd2d..8c4aecbda029829c753e9b1641ceeb490cc04259 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SOUND_H
-#define SOUND_H
+#pragma once
 
 // negative = SVQC autochannels
 // positive = one per entity
@@ -35,6 +34,7 @@ const float ATTEN_MAX = 3.984375;
 
 const float VOL_BASE = 0.7;
 const float VOL_BASEVOICE = 1.0;
+const float VOL_MUFFLED = 0.35;
 
 // Play all sounds via sound7, for access to the extra channels.
 // Otherwise, channels 8 to 15 would be blocked for a weird QW feature.
@@ -93,8 +93,8 @@ const float VOL_BASEVOICE = 1.0;
        } MACRO_END
 
 CLASS(Sound, Object)
-       ATTRIB(Sound, m_id, int, 0)
-       ATTRIB(Sound, sound_str, string(), func_null)
+       ATTRIB(Sound, m_id, int, 0);
+       ATTRIB(Sound, sound_str, string());
        CONSTRUCTOR(Sound, string() path)
        {
                CONSTRUCT(Sound);
@@ -114,7 +114,7 @@ CLASS(Sound, Object)
                        /**/
                #define tryext(ext) { string s = strcat(base, "." #ext); if (fexists(strcat("sound/", s))) return s; }
                extensions(tryext);
-               LOG_WARNINGF("Missing sound: \"%s\"\n", strcat("sound/", base));
+               LOG_WARNF("Missing sound: \"%s\"", strcat("sound/", base));
                #undef tryext
                #undef extensions
                return string_null;
@@ -125,9 +125,7 @@ CLASS(Sound, Object)
            TC(Sound, this);
                string s = Sound_fixpath(this);
                if (!s) return;
-               LOG_DEBUGF("precache_sound(\"%s\")\n", s);
+               profile(sprintf("precache_sound(\"%s\")", s));
                precache_sound(s);
        }
 ENDCLASS(Sound)
-
-#endif
index 0ffe55eb4fa50ba77a3c0c22ddb5c2b4defa2812..1a2fb1e7bfb171bb0405c1f8a02f83f09bde43dd 100644 (file)
@@ -15,9 +15,15 @@ void PlayerState_detach(entity this)
     PlayerState ps = PS(this);
        if (!ps) return;  // initial connect
        PS(this) = NULL;
+
        if (ps.m_client != this) return;  // don't own state, spectator
+       ps.m_switchweapon = WEP_Null;
+       ps.m_weapon = WEP_Null;
+       ps.m_switchingweapon = WEP_Null;
+       ps.ps_push(ps, this);
+       
        FOREACH_CLIENT(PS(it) == ps, { PS(it) = NULL; });
-       remove(ps);
+       delete(ps);
 
     Inventory_delete(this);
 }
@@ -39,13 +45,12 @@ void ClientState_attach(entity this)
 
     GetCvars(this, 0);  // get other cvars from player
 
-       if (IS_REAL_CLIENT(this)) { PlayerStats_PlayerBasic_CheckUpdate(this); }
-
        // TODO: fold all of these into ClientState
 
        DecodeLevelParms(this);
 
        PlayerScore_Attach(this);
+       PlayerStats_PlayerBasic_CheckUpdate(this);
        ClientData_Attach(this);
        accuracy_init(this);
        entcs_attach(this);
@@ -58,7 +63,7 @@ void ClientState_attach(entity this)
 
 void bot_clientdisconnect(entity this);
 void W_HitPlotClose(entity this);
-void anticheat_report(entity this);
+void anticheat_report_to_eventlog(entity this);
 void playerdemo_shutdown(entity this);
 void entcs_detach(entity this);
 void accuracy_free(entity this);
@@ -67,7 +72,7 @@ void PlayerScore_Detach(entity this);
 
 void ClientState_detach(entity this)
 {
-       remove(CS(this));
+       delete(CS(this));
        this._cs = NULL;
 
     GetCvars(this, -1);  // free cvars
@@ -75,7 +80,7 @@ void ClientState_detach(entity this)
     bot_clientdisconnect(this);
 
     W_HitPlotClose(this);
-    anticheat_report(this);
+    anticheat_report_to_eventlog(this);
     playerdemo_shutdown(this);
     entcs_detach(this);
     accuracy_free(this);
index 110df1c575840421fb05a47ebb66cc16c89f8efa..94b9bc325f26288ebe8ccc8ff8cf67756366c334 100644 (file)
@@ -6,15 +6,15 @@
  * Server: instance per client, clients decoupled from players
  */
 CLASS(PlayerState, Object)
-       ATTRIB(PlayerState, m_client, entity, NULL)
+       ATTRIB(PlayerState, m_client, entity);
        CONSTRUCTOR(PlayerState, entity client)
        {
                CONSTRUCT(PlayerState);
                this.m_client = client;
        }
-       ATTRIB(PlayerState, m_switchingweapon, Weapon, Weapons_from(-1))
-       ATTRIB(PlayerState, m_switchweapon, Weapon, Weapons_from(-1))
-       ATTRIB(PlayerState, m_weapon, Weapon, Weapons_from(-1))
+       ATTRIB(PlayerState, m_switchingweapon, Weapon, Weapons_from(-1));
+       ATTRIB(PlayerState, m_switchweapon, Weapon, Weapons_from(-1));
+       ATTRIB(PlayerState, m_weapon, Weapon, Weapons_from(-1));
        METHOD(PlayerState, ps_push, void(PlayerState this, entity cl))
        {
            TC(PlayerState, this);
@@ -37,7 +37,7 @@ void PlayerState_detach(entity this);
  * Server: instance per client
  */
 CLASS(ClientState, Object)
-       ATTRIB(ClientState, m_client, entity, NULL)
+       ATTRIB(ClientState, m_client, entity);
        CONSTRUCTOR(ClientState, entity client)
        {
                CONSTRUCT(ClientState);
index 2c471f0db30bb63d39b53966ed92ff6bf2e573ad..7649954b325e6c40b85eefd3691bb12859840060 100644 (file)
@@ -1,8 +1,7 @@
-#ifndef STATS_H
-#define STATS_H
+#pragma once
 
 #ifdef SVQC
-#include <server/cl_client.qh>
+#include <server/client.qh>
 #endif
 
 // Full list of all stat constants, included in a single location for easy reference
@@ -45,14 +44,14 @@ const int MAX_CL_STATS = 256;
 REGISTER_STAT(WEAPONS, vectori)
 REGISTER_STAT(WEAPONSINMAP, vectori)
 
-REGISTER_STAT(PL_VIEW_OFS, vector, autocvar_sv_player_viewoffset)
-REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector, autocvar_sv_player_crouch_viewoffset)
+REGISTER_STAT(PL_VIEW_OFS, vector)
+REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector)
 
-REGISTER_STAT(PL_MIN, vector, autocvar_sv_player_mins)
-REGISTER_STAT(PL_CROUCH_MIN, vector, autocvar_sv_player_crouch_mins)
+REGISTER_STAT(PL_MIN, vector)
+REGISTER_STAT(PL_CROUCH_MIN, vector)
 
-REGISTER_STAT(PL_MAX, vector, autocvar_sv_player_maxs)
-REGISTER_STAT(PL_CROUCH_MAX, vector, autocvar_sv_player_crouch_maxs)
+REGISTER_STAT(PL_MAX, vector)
+REGISTER_STAT(PL_CROUCH_MAX, vector)
 
 REGISTER_STAT(KH_KEYS, int)
 
@@ -64,9 +63,10 @@ REGISTER_STAT(WEAPON_NEXTTHINK, float)
 #ifdef SVQC
 SPECTATE_COPYFIELD(_STAT(WEAPON_NEXTTHINK))
 float W_WeaponRateFactor(entity this);
+float gameover;
 #endif
 REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
-
+REGISTER_STAT(GAMEOVER, int, gameover)
 REGISTER_STAT(GAMESTARTTIME, float)
 REGISTER_STAT(STRENGTH_FINISHED, float)
 REGISTER_STAT(INVINCIBLE_FINISHED, float)
@@ -122,8 +122,10 @@ REGISTER_STAT(REVIVE_PROGRESS, float)
 REGISTER_STAT(ROUNDLOST, int)
 REGISTER_STAT(BUFF_TIME, float)
 REGISTER_STAT(CTF_FLAGSTATUS, int)
+REGISTER_STAT(CAPTURE_PROGRESS, float)
 REGISTER_STAT(ENTRAP_ORB, float)
 REGISTER_STAT(ENTRAP_ORB_ALPHA, float)
+REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime)
 
 #ifdef SVQC
 int autocvar_g_multijump;
@@ -174,12 +176,21 @@ REGISTER_STAT(BUGRIGS_SPEED_POW, float, g_bugrigs_speed_pow)
 REGISTER_STAT(BUGRIGS_SPEED_REF, float, g_bugrigs_speed_ref)
 REGISTER_STAT(BUGRIGS_STEER, float, g_bugrigs_steer)
 
-REGISTER_STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, int, cvar("sv_gameplayfix_downtracesupportsongroundflag"))
-REGISTER_STAT(GAMEPLAYFIX_EASIERWATERJUMP, int, cvar("sv_gameplayfix_easierwaterjump"))
-REGISTER_STAT(GAMEPLAYFIX_STEPDOWN, int, cvar("sv_gameplayfix_stepdown"))
-REGISTER_STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, int, cvar("sv_gameplayfix_stepmultipletimes"))
-REGISTER_STAT(GAMEPLAYFIX_UNSTICKPLAYERS, int, cvar("sv_gameplayfix_unstickplayers"))
+#ifdef SVQC
+int autocvar_sv_gameplayfix_downtracesupportsongroundflag;
+int autocvar_sv_gameplayfix_easierwaterjump;
+int autocvar_sv_gameplayfix_stepdown;
+int autocvar_sv_gameplayfix_stepmultipletimes;
+int autocvar_sv_gameplayfix_unstickplayers;
+int autocvar_sv_gameplayfix_fixedcheckwatertransition;
+#endif
+REGISTER_STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, int, autocvar_sv_gameplayfix_downtracesupportsongroundflag)
+REGISTER_STAT(GAMEPLAYFIX_EASIERWATERJUMP, int, autocvar_sv_gameplayfix_easierwaterjump)
+REGISTER_STAT(GAMEPLAYFIX_STEPDOWN, int, autocvar_sv_gameplayfix_stepdown)
+REGISTER_STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, int, autocvar_sv_gameplayfix_stepmultipletimes)
+REGISTER_STAT(GAMEPLAYFIX_UNSTICKPLAYERS, int, autocvar_sv_gameplayfix_unstickplayers)
 REGISTER_STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, int, autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag)
+REGISTER_STAT(GAMEPLAYFIX_WATERTRANSITION, int, autocvar_sv_gameplayfix_fixedcheckwatertransition)
 
 REGISTER_STAT(MOVEVARS_JUMPSTEP, int, cvar("sv_jumpstep"))
 REGISTER_STAT(NOSTEP, int, cvar("sv_nostep"))
@@ -205,6 +216,7 @@ float autocvar_sv_dodging_horiz_speed_frozen;
 float autocvar_sv_dodging_ramp_time;
 float autocvar_sv_dodging_up_speed;
 bool autocvar_sv_dodging_wall_dodging;
+bool autocvar_sv_dodging_air_dodging;
 #endif
 
 REGISTER_STAT(DODGING, int, g_dodging)
@@ -219,7 +231,8 @@ REGISTER_STAT(DODGING_RAMP_TIME, float, autocvar_sv_dodging_ramp_time)
 /** cvar loopback */
 REGISTER_STAT(DODGING_TIMEOUT, float)
 REGISTER_STAT(DODGING_UP_SPEED, float, autocvar_sv_dodging_up_speed)
-REGISTER_STAT(DODGING_WALL, int, autocvar_sv_dodging_wall_dodging)
+REGISTER_STAT(DODGING_WALL, bool, autocvar_sv_dodging_wall_dodging)
+REGISTER_STAT(DODGING_AIR, bool, autocvar_sv_dodging_air_dodging)
 
 REGISTER_STAT(JETPACK_ACCEL_SIDE, float, autocvar_g_jetpack_acceleration_side)
 REGISTER_STAT(JETPACK_ACCEL_UP, float, autocvar_g_jetpack_acceleration_up)
@@ -227,9 +240,24 @@ REGISTER_STAT(JETPACK_ANTIGRAVITY, float, autocvar_g_jetpack_antigravity)
 REGISTER_STAT(JETPACK_FUEL, float, autocvar_g_jetpack_fuel)
 REGISTER_STAT(JETPACK_MAXSPEED_SIDE, float, autocvar_g_jetpack_maxspeed_side)
 REGISTER_STAT(JETPACK_MAXSPEED_UP, float, autocvar_g_jetpack_maxspeed_up)
+REGISTER_STAT(JETPACK_REVERSE_THRUST, float, autocvar_g_jetpack_reverse_thrust)
 
 REGISTER_STAT(MOVEVARS_HIGHSPEED, float, autocvar_g_movement_highspeed)
 
+#ifdef SVQC
+AUTOCVAR(g_walljump, bool, false, "Enable wall jumping mutator");
+AUTOCVAR(g_walljump_delay, float, 1, "Minimum delay between wall jumps");
+AUTOCVAR(g_walljump_force, float, 300, "How far to bounce/jump off the wall");
+AUTOCVAR(g_walljump_velocity_xy_factor, float, 1.15, "How much to slow down along horizontal axis, higher value = higher deceleration, if factor is < 1, you accelerate by wall jumping");
+AUTOCVAR(g_walljump_velocity_z_factor, float, 0.5, "Upwards velocity factor, multiplied by normal jump velocity");
+#endif
+REGISTER_STAT(WALLJUMP, int, autocvar_g_walljump)
+REGISTER_STAT(WALLJUMP_VELOCITY_Z_FACTOR, float, autocvar_g_walljump_velocity_z_factor)
+REGISTER_STAT(WALLJUMP_VELOCITY_XY_FACTOR, float, autocvar_g_walljump_velocity_xy_factor)
+REGISTER_STAT(WALLJUMP_DELAY, float, autocvar_g_walljump_delay)
+REGISTER_STAT(WALLJUMP_FORCE, float, autocvar_g_walljump_force)
+REGISTER_STAT(LASTWJ, float)
+
 // freeze tag, clan arena
 REGISTER_STAT(REDALIVE, int)
 REGISTER_STAT(BLUEALIVE, int)
@@ -246,8 +274,15 @@ REGISTER_STAT(DOM_PPS_PINK, float)
 REGISTER_STAT(TELEPORT_MAXSPEED, float, autocvar_g_teleport_maxspeed)
 REGISTER_STAT(TELEPORT_TELEFRAG_AVOID, int, autocvar_g_telefrags_avoid)
 
+REGISTER_STAT(CAMERA_SPECTATOR, int)
+
 REGISTER_STAT(SPECTATORSPEED, float)
 
+#ifdef SVQC
+bool autocvar_sv_slick_applygravity;
+#endif
+REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity)
+
 #ifdef SVQC
 #include "physics/movetypes/movetypes.qh"
 #endif
@@ -257,6 +292,7 @@ REGISTER_STAT(MOVEVARS_AIRCONTROL_PENALTY, float)
 REGISTER_STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, float)
 REGISTER_STAT(MOVEVARS_AIRSTRAFEACCEL_QW, float)
 REGISTER_STAT(MOVEVARS_AIRCONTROL_POWER, float)
+REGISTER_STAT(MOVEVARS_AIRCONTROL_BACKWARDS, bool)
 noref bool autocvar_sv_gameplayfix_nogravityonground;
 REGISTER_STAT(MOVEFLAGS, int, MOVEFLAG_VALID
                               | (autocvar_sv_gameplayfix_q2airaccelerate ? MOVEFLAG_Q2AIRACCELERATE : 0)
@@ -308,6 +344,3 @@ REGISTER_STAT(GUNALIGN, int, this.cvar_cl_gunalign)
 #ifdef SVQC
 SPECTATE_COPYFIELD(_STAT(GUNALIGN))
 #endif
-
-
-#endif
index a0255d7d61d7dcf9bdb195bd52cf0603debd2d88..9d89679e3be9f88817ea8abd9883774c36eca22a 100644 (file)
@@ -1,13 +1,12 @@
 #include "t_items.qh"
 
-#include "items/all.qc"
+#include "items/_mod.qh"
 
 #if defined(SVQC)
 
-    #include "../server/bot/bot.qh"
-    #include "../server/bot/waypoints.qh"
+    #include "../server/bot/api.qh"
 
-    #include <server/mutators/all.qh>
+    #include <server/mutators/_mod.qh>
 
     #include "../server/weapons/common.qh"
     #include "../server/weapons/selection.qh"
        #include "triggers/subs.qh"
     #include "util.qh"
 
-    #include <common/monsters/all.qh>
+    #include <common/monsters/_mod.qh>
 
-    #include <common/weapons/all.qh>
+    #include <common/weapons/_all.qh>
 
     #include "../lib/warpzone/util_server.qh"
 #elif defined(CSQC)
        #include "physics/movetypes/movetypes.qh"
-       #include <common/weapons/all.qh>
+       #include <common/weapons/_all.qh>
        #include "../lib/csqcmodel/cl_model.qh"
        #include "../lib/csqcmodel/common.qh"
 #endif
@@ -35,6 +34,7 @@ REGISTER_NET_LINKED(ENT_CLIENT_ITEM)
 
 #ifdef CSQC
 bool autocvar_cl_ghost_items_vehicle = true;
+.vector item_glowmod;
 void Item_SetAlpha(entity this)
 {
        bool veh_hud = (hud && autocvar_cl_ghost_items_vehicle);
@@ -42,7 +42,8 @@ void Item_SetAlpha(entity this)
        if(!veh_hud && (this.ItemStatus & ITS_AVAILABLE))
        {
                this.alpha = 1;
-               this.colormod = this.glowmod = '1 1 1';
+               this.colormod = '1 1 1';
+               this.glowmod = this.item_glowmod;
        }
        else
        {
@@ -68,33 +69,40 @@ void ItemDraw(entity this)
     if(this.gravity)
     {
         Movetype_Physics_MatchServer(this, false);
-        if(this.move_flags & FL_ONGROUND)
-        { // For some reason move_avelocity gets set to '0 0 0' here ...
+        if(IS_ONGROUND(this))
+        { // For some reason avelocity gets set to '0 0 0' here ...
             this.oldorigin = this.origin;
             this.gravity = 0;
 
             if(autocvar_cl_animate_items)
             { // ... so reset it if animations are requested.
                 if(this.ItemStatus & ITS_ANIMATE1)
-                    this.move_avelocity = '0 180 0';
+                    this.avelocity = '0 180 0';
 
                 if(this.ItemStatus & ITS_ANIMATE2)
-                    this.move_avelocity = '0 -90 0';
+                    this.avelocity = '0 -90 0';
             }
+
+            // delay is for blocking item's position for a while;
+            // it's a workaround for dropped weapons that receive the position
+            // another time right after they spawn overriding animation position
+            this.onground_time = time + 0.5;
         }
     }
     else if (autocvar_cl_animate_items)
     {
         if(this.ItemStatus & ITS_ANIMATE1)
         {
-            this.angles += this.move_avelocity * frametime;
-            setorigin(this, '0 0 10' + this.oldorigin + '0 0 8' * sin(time * 2));
+            this.angles += this.avelocity * frametime;
+            float fade_in = bound(0, time - this.onground_time, 1);
+            setorigin(this, this.oldorigin + fade_in * ('0 0 10' + '0 0 8' * sin((time - this.onground_time) * 2)));
         }
 
         if(this.ItemStatus & ITS_ANIMATE2)
         {
-            this.angles += this.move_avelocity * frametime;
-            setorigin(this, '0 0 8' + this.oldorigin + '0 0 4' * sin(time * 3));
+            this.angles += this.avelocity * frametime;
+            float fade_in = bound(0, time - this.onground_time, 1);
+            setorigin(this, this.oldorigin + fade_in * ('0 0 8' + '0 0 4' * sin((time - this.onground_time) * 3)));
         }
     }
 
@@ -107,7 +115,7 @@ void ItemDrawSimple(entity this)
     {
         Movetype_Physics_MatchServer(this, false);
 
-        if(this.move_flags & FL_ONGROUND)
+        if(IS_ONGROUND(this))
             this.gravity = 0;
     }
 
@@ -164,7 +172,6 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
         this.angles_x = ReadAngle();
         this.angles_y = ReadAngle();
         this.angles_z = ReadAngle();
-        this.move_angles = this.angles;
     }
 
     if(sf & ISF_SIZE)
@@ -183,7 +190,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
             if(this.ItemStatus & ITS_ALLOWFB)
                 this.effects |= EF_FULLBRIGHT;
 
-        if(this.ItemStatus & ITS_POWERUP)
+        if(this.ItemStatus & ITS_GLOW)
         {
             if(this.ItemStatus & ITS_AVAILABLE)
                 this.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
@@ -195,10 +202,11 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
     if(sf & ISF_MODEL)
     {
         this.drawmask  = MASK_NORMAL;
-               this.move_movetype = MOVETYPE_TOSS;
+               set_movetype(this, MOVETYPE_TOSS);
+               if (isnew) IL_PUSH(g_drawables, this);
         this.draw       = ItemDraw;
         this.solid = SOLID_TRIGGER;
-        //this.move_flags |= FL_ITEM;
+        //this.flags |= FL_ITEM;
 
         bool use_bigsize = ReadByte();
 
@@ -229,7 +237,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
             else
             {
                 this.draw = ItemDraw;
-                LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it\n");
+                LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it");
             }
         }
 
@@ -238,7 +246,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
 
 
         if(this.mdl == "")
-            LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!\n");
+            LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!");
 
         precache_model(this.mdl);
         _setmodel(this, this.mdl);
@@ -247,19 +255,23 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
     }
 
     if(sf & ISF_COLORMAP)
+    {
         this.colormap = ReadShort();
+        this.item_glowmod_x = ReadByte() / 255.0;
+        this.item_glowmod_y = ReadByte() / 255.0;
+        this.item_glowmod_z = ReadByte() / 255.0;
+    }
 
     if(sf & ISF_DROP)
     {
         this.gravity = 1;
         this.pushable = true;
-        //this.move_angles = '0 0 0';
-        this.move_movetype = MOVETYPE_TOSS;
-        this.move_velocity_x = ReadCoord();
-        this.move_velocity_y = ReadCoord();
-        this.move_velocity_z = ReadCoord();
-        this.velocity = this.move_velocity;
-        this.move_origin = this.oldorigin;
+        //this.angles = '0 0 0';
+        set_movetype(this, MOVETYPE_TOSS);
+        this.velocity_x = ReadCoord();
+        this.velocity_y = ReadCoord();
+        this.velocity_z = ReadCoord();
+        setorigin(this, this.oldorigin);
 
         if(!this.move_time)
         {
@@ -273,10 +285,10 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
     if(autocvar_cl_animate_items)
     {
         if(this.ItemStatus & ITS_ANIMATE1)
-            this.move_avelocity = '0 180 0';
+            this.avelocity = '0 180 0';
 
         if(this.ItemStatus & ITS_ANIMATE2)
-            this.move_avelocity = '0 -90 0';
+            this.avelocity = '0 -90 0';
     }
 
     this.entremove = ItemRemove;
@@ -329,14 +341,19 @@ bool ItemSend(entity this, entity to, int sf)
                WriteShort(MSG_ENTITY, this.fade_start);
 
                if(this.mdl == "")
-                       LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "exspect a crash just aboute now\n");
+                       LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now");
 
                WriteString(MSG_ENTITY, this.mdl);
        }
 
 
        if(sf & ISF_COLORMAP)
+       {
                WriteShort(MSG_ENTITY, this.colormap);
+               WriteByte(MSG_ENTITY, this.glowmod.x * 255.0);
+        WriteByte(MSG_ENTITY, this.glowmod.y * 255.0);
+        WriteByte(MSG_ENTITY, this.glowmod.z * 255.0);
+       }
 
        if(sf & ISF_DROP)
        {
@@ -376,7 +393,7 @@ bool have_pickup_item(entity this)
                if(autocvar_g_pickup_items == 0)
                        return false;
                if(g_weaponarena)
-                       if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena
+                       if(this.weapons || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena
                                return false;
        }
        return true;
@@ -413,6 +430,7 @@ void Item_Show (entity e, float mode)
 {
        e.effects &= ~(EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST);
        e.ItemStatus &= ~ITS_STAYWEP;
+       entity def = e.itemdef;
        if (mode > 0)
        {
                // make the item look normal, and be touchable
@@ -430,7 +448,6 @@ void Item_Show (entity e, float mode)
                e.ItemStatus &= ~ITS_AVAILABLE;
        }
        else {
-       entity def = e.itemdef;
        bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons
                || e.team // weapon stay isn't supported for teamed weapons
                ;
@@ -448,13 +465,13 @@ void Item_Show (entity e, float mode)
                //setmodel(e, "null");
                e.solid = SOLID_NOT;
                e.colormod = '0 0 0';
-               e.glowmod = e.colormod;
+               //e.glowmod = e.colormod;
                e.spawnshieldtime = 1;
                e.ItemStatus &= ~ITS_AVAILABLE;
        }}
 
-       if (e.items & ITEM_Strength.m_itemid || e.items & ITEM_Shield.m_itemid)
-               e.ItemStatus |= ITS_POWERUP;
+       if (def.m_glow)
+               e.ItemStatus |= ITS_GLOW;
 
        if (autocvar_g_nodepthtestitems)
                e.effects |= EF_NODEPTHTEST;
@@ -487,13 +504,7 @@ void Item_ItemsTime_SetTimesForAllPlayers();
 void Item_Respawn (entity this)
 {
        Item_Show(this, 1);
-       // this is ugly...
-       if(this.items == ITEM_Strength.m_itemid)
-               sound (this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTEN_NORM);   // play respawn sound
-       else if(this.items == ITEM_Shield.m_itemid)
-               sound (this, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTEN_NORM);     // play respawn sound
-       else
-               sound (this, CH_TRIGGER, SND_ITEMRESPAWN, VOL_BASE, ATTEN_NORM);        // play respawn sound
+       sound(this, CH_TRIGGER, this.itemdef.m_respawnsound, VOL_BASE, ATTEN_NORM);     // play respawn sound
        setorigin(this, this.origin);
 
     if (Item_ItemsTime_Allow(this.itemdef) || this.weapons & WEPSET_SUPERWEAPONS)
@@ -761,7 +772,7 @@ LABEL(skip)
        return 1;
 }
 
-void Item_Touch(entity this)
+void Item_Touch(entity this, entity toucher)
 {
 
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
@@ -769,26 +780,26 @@ void Item_Touch(entity this)
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
-                       remove(this);
+                       delete(this);
                        return;
                }
        }
 
-       if(!(other.flags & FL_PICKUPITEMS)
-       || STAT(FROZEN, other)
-       || IS_DEAD(other)
+       if(!(toucher.flags & FL_PICKUPITEMS)
+       || STAT(FROZEN, toucher)
+       || IS_DEAD(toucher)
        || (this.solid != SOLID_TRIGGER)
-       || (this.owner == other)
+       || (this.owner == toucher)
        || (time < this.item_spawnshieldtime)
        ) { return; }
 
-       switch (MUTATOR_CALLHOOK(ItemTouch, this, other))
+       switch (MUTATOR_CALLHOOK(ItemTouch, this, toucher))
        {
                case MUT_ITEMTOUCH_RETURN: { return; }
-               case MUT_ITEMTOUCH_PICKUP: { other = M_ARGV(1, entity); goto pickup; }
+               case MUT_ITEMTOUCH_PICKUP: { toucher = M_ARGV(1, entity); goto pickup; }
        }
 
-       other = M_ARGV(1, entity);
+       toucher = M_ARGV(1, entity);
 
        if (this.classname == "droppedweapon")
        {
@@ -797,7 +808,7 @@ void Item_Touch(entity this)
                this.superweapons_finished = max(0, this.superweapons_finished - time);
        }
        entity it = this.itemdef;
-       bool gave = ITEM_HANDLE(Pickup, it, this, other);
+       bool gave = ITEM_HANDLE(Pickup, it, this, toucher);
        if (!gave)
        {
                if (this.classname == "droppedweapon")
@@ -812,28 +823,27 @@ void Item_Touch(entity this)
 
 LABEL(pickup)
 
-       other.last_pickup = time;
+       toucher.last_pickup = time;
 
        Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
-       _sound (other, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
+       _sound (toucher, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
 
        if (this.classname == "droppedweapon")
-               remove (this);
+               delete (this);
        else if (this.spawnshieldtime)
        {
                entity e;
                if(this.team)
                {
                        RandomSelection_Init();
-                       for(entity head = NULL; (head = findfloat(head, team, this.team)); )
+                       IL_EACH(g_items, it.team == this.team,
                        {
-                               if(head.flags & FL_ITEM)
-                               if(head.classname != "item_flag_team" && head.classname != "item_key_team")
+                               if(it.itemdef) // is a registered item
                                {
-                                       Item_Show(head, -1);
-                                       RandomSelection_Add(head, 0, string_null, head.cnt, 0);
+                                       Item_Show(it, -1);
+                                       RandomSelection_AddEnt(it, it.cnt, 0);
                                }
-                       }
+                       });
                        e = RandomSelection_chosen_ent;
 
                }
@@ -868,23 +878,21 @@ void Item_FindTeam(entity this)
        if(this.effects & EF_NODRAW)
        {
                // marker for item team search
-               LOG_TRACE("Initializing item team ", ftos(this.team), "\n");
+               LOG_TRACE("Initializing item team ", ftos(this.team));
                RandomSelection_Init();
-               FOREACH_ENTITY_FLOAT(team, this.team,
+               IL_EACH(g_items, it.team == this.team,
                {
-                       if(it.flags & FL_ITEM)
-                       if(it.classname != "item_flag_team" && it.classname != "item_key_team")
-                               RandomSelection_Add(it, 0, string_null, it.cnt, 0);
+                       if(it.itemdef) // is a registered item
+                               RandomSelection_AddEnt(it, it.cnt, 0);
                });
 
                e = RandomSelection_chosen_ent;
                e.state = 0;
                Item_Show(e, 1);
 
-               FOREACH_ENTITY_FLOAT(team, this.team,
+               IL_EACH(g_items, it.team == this.team,
                {
-                       if(it.flags & FL_ITEM)
-                       if(it.classname != "item_flag_team" && it.classname != "item_key_team")
+                       if(it.itemdef) // is a registered item
                        {
                                if(it != e)
                                {
@@ -905,7 +913,7 @@ void RemoveItem(entity this)
 {
        if(wasfreed(this) || !this) { return; }
        Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
-       remove(this);
+       delete(this);
 }
 
 // pickup evaluation functions
@@ -916,17 +924,20 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup
 float weapon_pickupevalfunc(entity player, entity item)
 {
        float c;
+       int rating = item.bot_pickupbasevalue;
 
        // See if I have it already
-       if(item.weapons & ~player.weapons)
+       if(player.weapons & item.weapons)
        {
                // If I can pick it up
                if(!item.spawnshieldtime)
                        c = 0;
                else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets)
                {
+                       if (rating > 0)
+                               rating = BOT_PICKUP_RATING_LOW * 0.5 * (1 + rating / BOT_PICKUP_RATING_HIGH);
                        // Skilled bots will grab more
-                       c = bound(0, skill / 10, 1) * 0.5;
+                       c = 1 + bound(0, skill / 10, 1) * 0.5;
                }
                else
                        c = 0;
@@ -934,58 +945,74 @@ float weapon_pickupevalfunc(entity player, entity item)
        else
                c = 1;
 
+       if (c <= 0)
+               return 0;
+
        // If custom weapon priorities for bots is enabled rate most wanted weapons higher
-       if( bot_custom_weapon && c )
-       {
-               // Find the highest position on any range
-               int position = -1;
-               for (int j = 0; j < WEP_LAST ; ++j){
-                       if(
-                                       bot_weapons_far[j] == item.weapon ||
-                                       bot_weapons_mid[j] == item.weapon ||
-                                       bot_weapons_close[j] == item.weapon
-                         )
+       if(bot_custom_weapon)
+       {
+               int best_ratio = 0;
+               int missing = 0;
+
+               // evaluate weapon usefulness in all ranges
+               for(int list = 0; list < 3; list++)
+               {
+                       int position = -1;
+                       int wep_count = 0;
+                       int wpn = item.weapon;
+                       for (int j = 0; j < WEP_LAST; ++j)
                        {
-                               position = j;
-                               break;
+                               int list_wpn = 0;
+                               if (list == 0) list_wpn = bot_weapons_far[j];
+                               else if (list == 1) list_wpn = bot_weapons_mid[j];
+                               else list_wpn = bot_weapons_close[j];
+
+                               if (weaponsInMap & Weapons_from(list_wpn).m_wepset) // only if available
+                               {
+                                       if (list_wpn > 0)
+                                               wep_count++;
+                                       if (position == -1 && list_wpn == wpn)
+                                               position = wep_count;
+                               }
+                       }
+                       if (position == -1)
+                       {
+                               missing++;
+                               position = wep_count; // if missing assume last
+                       }
+                       if (wep_count)
+                       {
+                               if (!best_ratio || position / wep_count < best_ratio)
+                                       best_ratio = position / wep_count;
                        }
                }
 
-               // Rate it
-               if (position >= 0 )
-               {
-                       position = WEP_LAST - position;
-                       // item.bot_pickupbasevalue is overwritten here
-                       return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c;
-               }
+               if (missing < 3 && best_ratio)
+                       c = c - best_ratio * 0.3;
        }
 
-       return item.bot_pickupbasevalue * c;
+       return rating * c;
 }
 
 float commodity_pickupevalfunc(entity player, entity item)
 {
-       float c;
-       float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
-       c = 0;
+       bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
+       float c = 0;
 
        // Detect needed ammo
        FOREACH(Weapons, it != WEP_Null, {
                if(!(player.weapons & (it.m_wepset)))
                        continue;
 
-               if(it.items & ITEM_Shells.m_itemid)
-                       need_shells = true;
-               else if(it.items & ITEM_Bullets.m_itemid)
-                       need_nails = true;
-               else if(it.items & ITEM_Rockets.m_itemid)
-                       need_rockets = true;
-               else if(it.items & ITEM_Cells.m_itemid)
-                       need_cells = true;
-               else if(it.items & ITEM_Plasma.m_itemid)
-                       need_plasma = true;
-               else if(it.items & ITEM_JetpackFuel.m_itemid)
-                       need_fuel = true;
+               switch(it.ammo_field)
+               {
+                       case ammo_shells:  need_shells  = true; break;
+                       case ammo_nails:   need_nails   = true; break;
+                       case ammo_rockets: need_rockets = true; break;
+                       case ammo_cells:   need_cells   = true; break;
+                       case ammo_plasma:  need_plasma  = true; break;
+                       case ammo_fuel:    need_fuel    = true; break;
+               }
        });
 
        // TODO: figure out if the player even has the weapon this ammo is for?
@@ -1066,11 +1093,12 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                this.weapons = WepSet_FromWeapon(Weapons_from(weaponid));
 
        this.flags = FL_ITEM | itemflags;
+       IL_PUSH(g_items, this);
 
        if(MUTATOR_CALLHOOK(FilterItem, this)) // error means we do not want the item
        {
                startitem_failed = true;
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -1079,7 +1107,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        {
                this.reset = SUB_Remove;
                // it's a dropped weapon
-               this.movetype = MOVETYPE_TOSS;
+               set_movetype(this, MOVETYPE_TOSS);
 
                // Savage: remove thrown items after a certain period of time ("garbage collection")
                setthink(this, RemoveItem);
@@ -1099,7 +1127,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                if (trace_dpstartcontents & DPCONTENTS_NODROP)
                {
                        startitem_failed = true;
-                       remove(this);
+                       delete(this);
                        return;
                }
        }
@@ -1108,7 +1136,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                if(!have_pickup_item(this))
                {
                        startitem_failed = true;
-                       remove (this);
+                       delete (this);
                        return;
                }
 
@@ -1120,9 +1148,9 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                if(this.spawnflags & 1)
                        this.noalign = 1;
                if (this.noalign > 0)
-                       this.movetype = MOVETYPE_NONE;
+                       set_movetype(this, MOVETYPE_NONE);
                else
-                       this.movetype = MOVETYPE_TOSS;
+                       set_movetype(this, MOVETYPE_TOSS);
                // do item filtering according to game mode and other things
                if (this.noalign <= 0)
                {
@@ -1156,7 +1184,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
             // why not flags & fl_item?
                    FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
                 LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
-                LOG_TRACE(" vs ", it.netname, vtos(it.origin), "\n");
+                LOG_TRACE(" vs ", it.netname, vtos(it.origin));
                 error("Mapper sucks.");
             });
                        this.is_item = true;
@@ -1206,7 +1234,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                        this.gravity = 1;
                if (!(this.spawnflags & 1024))
                        this.ItemStatus |= ITS_ANIMATE1;
-               this.ItemStatus |= ISF_COLORMAP;
+               this.SendFlags |= ISF_COLORMAP;
        }
 
        this.state = 0;
@@ -1227,7 +1255,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        if (MUTATOR_CALLHOOK(Item_Spawn, this))
        {
                startitem_failed = true;
-               remove(this);
+               delete(this);
                return;
        }
 }
@@ -1337,17 +1365,17 @@ spawnfunc(item_armor_big)
                this.max_armorvalue = g_pickup_armorbig_max;
        if(!this.pickup_anyway)
                this.pickup_anyway = g_pickup_armorbig_anyway;
-       StartItem(this, ITEM_ArmorLarge);
+       StartItem(this, ITEM_ArmorBig);
 }
 
-spawnfunc(item_armor_large)
+spawnfunc(item_armor_mega)
 {
        if(!this.armorvalue)
-               this.armorvalue = g_pickup_armorlarge;
+               this.armorvalue = g_pickup_armormega;
        if(!this.max_armorvalue)
-               this.max_armorvalue = g_pickup_armorlarge_max;
+               this.max_armorvalue = g_pickup_armormega_max;
        if(!this.pickup_anyway)
-               this.pickup_anyway = g_pickup_armorlarge_anyway;
+               this.pickup_anyway = g_pickup_armormega_anyway;
        StartItem(this, ITEM_ArmorMega);
 }
 
@@ -1373,15 +1401,15 @@ spawnfunc(item_health_medium)
     StartItem(this, ITEM_HealthMedium);
 }
 
-spawnfunc(item_health_large)
+spawnfunc(item_health_big)
 {
        if(!this.max_health)
-               this.max_health = g_pickup_healthlarge_max;
+               this.max_health = g_pickup_healthbig_max;
        if(!this.health)
-               this.health = g_pickup_healthlarge;
+               this.health = g_pickup_healthbig;
        if(!this.pickup_anyway)
-               this.pickup_anyway = g_pickup_healthlarge_anyway;
-       StartItem(this, ITEM_HealthLarge);
+               this.pickup_anyway = g_pickup_healthbig_anyway;
+       StartItem(this, ITEM_HealthBig);
 }
 
 spawnfunc(item_health_mega)
@@ -1397,9 +1425,11 @@ spawnfunc(item_health_mega)
 
 // support old misnamed entities
 spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-spawnfunc(item_armor25) { spawnfunc_item_armor_large(this); }
+spawnfunc(item_armor25) { spawnfunc_item_armor_mega(this); }
+spawnfunc(item_armor_large) { spawnfunc_item_armor_mega(this); }
 spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
 spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
+spawnfunc(item_health_large) { spawnfunc_item_health_big(this); }
 spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
 
 spawnfunc(item_strength)
@@ -1421,12 +1451,10 @@ spawnfunc(item_quad) { this.classname = "item_strength";spawnfunc_item_strength(
 
 void target_items_use(entity this, entity actor, entity trigger)
 {
-       other = trigger; // TODO
-
        if(actor.classname == "droppedweapon")
        {
-               EXACTTRIGGER_TOUCH;
-               remove(actor);
+               EXACTTRIGGER_TOUCH(this, trigger);
+               delete(actor);
                return;
        }
 
@@ -1436,13 +1464,12 @@ void target_items_use(entity this, entity actor, entity trigger)
                return;
        if(trigger.solid == SOLID_TRIGGER)
        {
-               EXACTTRIGGER_TOUCH;
+               EXACTTRIGGER_TOUCH(this, trigger);
        }
 
-       FOREACH_ENTITY_ENT(enemy, actor,
+       IL_EACH(g_items, it.enemy == actor && it.classname == "droppedweapon",
        {
-               if(it.classname == "droppedweapon")
-                       remove(it);
+               delete(it);
        });
 
        if(GiveItems(actor, 0, tokenize_console(this.netname)))
@@ -1451,7 +1478,7 @@ void target_items_use(entity this, entity actor, entity trigger)
 
 spawnfunc(target_items)
 {
-       float n, i;
+       int n, j;
        string s;
 
        this.use = target_items_use;
@@ -1469,20 +1496,20 @@ spawnfunc(target_items)
        }
        else
        {
-               for(i = 0; i < n; ++i)
+               for(j = 0; j < n; ++j)
                {
-                       if     (argv(i) == "unlimited_ammo")         this.items |= IT_UNLIMITED_AMMO;
-                       else if(argv(i) == "unlimited_weapon_ammo")  this.items |= IT_UNLIMITED_WEAPON_AMMO;
-                       else if(argv(i) == "unlimited_superweapons") this.items |= IT_UNLIMITED_SUPERWEAPONS;
-                       else if(argv(i) == "strength")               this.items |= ITEM_Strength.m_itemid;
-                       else if(argv(i) == "invincible")             this.items |= ITEM_Shield.m_itemid;
-                       else if(argv(i) == "superweapons")           this.items |= IT_SUPERWEAPON;
-                       else if(argv(i) == "jetpack")                this.items |= ITEM_Jetpack.m_itemid;
-                       else if(argv(i) == "fuel_regen")             this.items |= ITEM_JetpackRegen.m_itemid;
+                       if     (argv(j) == "unlimited_ammo")         this.items |= IT_UNLIMITED_AMMO;
+                       else if(argv(j) == "unlimited_weapon_ammo")  this.items |= IT_UNLIMITED_WEAPON_AMMO;
+                       else if(argv(j) == "unlimited_superweapons") this.items |= IT_UNLIMITED_SUPERWEAPONS;
+                       else if(argv(j) == "strength")               this.items |= ITEM_Strength.m_itemid;
+                       else if(argv(j) == "invincible")             this.items |= ITEM_Shield.m_itemid;
+                       else if(argv(j) == "superweapons")           this.items |= IT_SUPERWEAPON;
+                       else if(argv(j) == "jetpack")                this.items |= ITEM_Jetpack.m_itemid;
+                       else if(argv(j) == "fuel_regen")             this.items |= ITEM_JetpackRegen.m_itemid;
                        else
                        {
                                FOREACH(Weapons, it != WEP_Null, {
-                                       s = W_UndeprecateName(argv(i));
+                                       s = W_UndeprecateName(argv(j));
                                        if(s == it.netname)
                                        {
                                                this.weapons |= (it.m_wepset);
@@ -1543,9 +1570,9 @@ spawnfunc(target_items)
        //print(this.netname, "\n");
 
        n = tokenize_console(this.netname);
-       for(i = 0; i < n; ++i)
+       for(j = 0; j < n; ++j)
        {
-               FOREACH(Weapons, it != WEP_Null && argv(i) == it.netname, {
+               FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(argv(j)) == it.netname, {
             it.wr_init(it);
             break;
                });
@@ -1768,7 +1795,7 @@ float GiveItems(entity e, float beginarg, float endarg)
                                got += GiveValue(e, ammo_fuel, op, val);
                                break;
                        default:
-                               FOREACH(Weapons, it != WEP_Null && cmd == it.netname, {
+                               FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(cmd) == it.netname, {
                     got += GiveWeapon(e, it.m_id, op, val);
                     break;
                                });
@@ -1790,6 +1817,7 @@ float GiveItems(entity e, float beginarg, float endarg)
        });
        POSTGIVE_VALUE(e, strength_finished, 1, SND_POWERUP, SND_POWEROFF);
        POSTGIVE_VALUE(e, invincible_finished, 1, SND_Shield, SND_POWEROFF);
+       //POSTGIVE_VALUE(e, superweapons_finished, 1, SND_Null, SND_Null);
        POSTGIVE_VALUE(e, ammo_nails, 0, SND_ITEMPICKUP, SND_Null);
        POSTGIVE_VALUE(e, ammo_cells, 0, SND_ITEMPICKUP, SND_Null);
        POSTGIVE_VALUE(e, ammo_plasma, 0, SND_ITEMPICKUP, SND_Null);
index 5ebc9d7e6234f4e8bd06d1233f0584a91946a254..418ce391767f4ff170a27f315ffbe953bab91d1a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_ITEMS_H
-#define T_ITEMS_H
+#pragma once
 
 const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
 
@@ -13,7 +12,7 @@ const int ISF_STATUS                  = BIT(3);
     const int ITS_AVAILABLE        = BIT(3);
     const int ITS_ALLOWFB                  = BIT(4);
     const int ITS_ALLOWSI                  = BIT(5);
-    const int ITS_POWERUP                  = BIT(6);
+    const int ITS_GLOW                     = BIT(6);
 const int ISF_COLORMAP                         = BIT(4);
 const int ISF_DROP                             = BIT(5);
 const int ISF_ANGLES                   = BIT(6);
@@ -21,6 +20,7 @@ const int ISF_SIZE                            = BIT(7);
 
 .int ItemStatus;
 
+.float onground_time;
 .float fade_start;
 .float fade_end;
 
@@ -30,6 +30,7 @@ void StartItem(entity this, entity a);
 
 #ifdef CSQC
 
+bool   autocvar_cl_items_nofade;
 float  autocvar_cl_animate_items = 1;
 float  autocvar_cl_ghost_items = 0.45;
 vector autocvar_cl_ghost_items_color = '-1 -1 -1';
@@ -88,7 +89,7 @@ float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax
 
 float Item_GiveTo(entity item, entity player);
 
-void Item_Touch(entity this);
+void Item_Touch(entity this, entity toucher);
 
 void Item_Reset(entity this);
 
@@ -133,4 +134,3 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa
 
 float GiveItems(entity e, float beginarg, float endarg);
 #endif
-#endif
index aeaf078dce4b564b6c2a77df146821ff5a77c935..1f8a603dd01744b1dde6a4f13f98b5b32bb61d03 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TEAMS_H
-#define TEAMS_H
+#pragma once
 
 #ifdef TEAMNUMBERS_THAT_ARENT_STUPID
 const int NUM_TEAM_1 = 1;  // red
@@ -177,5 +176,3 @@ float Team_TeamToNumber(float teamid)
 // safe team comparisons
 #define SAME_TEAM(a,b) (teamplay ? (a.team == b.team) : (a == b))
 #define DIFF_TEAM(a,b) (teamplay ? (a.team != b.team) : (a != b))
-
-#endif
index c049b10828d94f28669e578f2e2d53ce082f307f..9b327de55d23fc0f59fbc9e8b7a34e267ccbcaf1 100644 (file)
@@ -4,3 +4,8 @@
 #include <common/triggers/subs.qc>
 #include <common/triggers/teleporters.qc>
 #include <common/triggers/triggers.qc>
+
+#include <common/triggers/func/_mod.inc>
+#include <common/triggers/misc/_mod.inc>
+#include <common/triggers/target/_mod.inc>
+#include <common/triggers/trigger/_mod.inc>
index 2fba6046172541db1c2324c936fd81f8d57eecaa..d3bb2dc3addb2764d6a6cd71e6f8ee115b305160 100644 (file)
@@ -4,3 +4,8 @@
 #include <common/triggers/subs.qh>
 #include <common/triggers/teleporters.qh>
 #include <common/triggers/triggers.qh>
+
+#include <common/triggers/func/_mod.qh>
+#include <common/triggers/misc/_mod.qh>
+#include <common/triggers/target/_mod.qh>
+#include <common/triggers/trigger/_mod.qh>
index 094673b5bb4612f0b2dcb25f9b0afb7840ca42b0..b7034939ae8863806fc49fb2d326488e70d2a84f 100644 (file)
@@ -1,3 +1,4 @@
+#include "bobbing.qh"
 #ifdef SVQC
 .float height;
 void func_bobbing_controller_think(entity this)
diff --git a/qcsrc/common/triggers/func/bobbing.qh b/qcsrc/common/triggers/func/bobbing.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 1a65710a98346fcd1115b60ca06887e6282b7f87..dc2e6f7dc0a062a2a19e8c2a611ee12b8cb14ea2 100644 (file)
@@ -1,8 +1,9 @@
+#include "breakable.qh"
 #ifdef SVQC
 
 #include <server/g_subs.qh>
 #include <server/g_damage.qh>
-#include <server/bot/bot.qh>
+#include <server/bot/api.qh>
 #include <common/csqcmodel_settings.qh>
 #include <lib/csqcmodel/sv_model.qh>
 #include <server/weapons/common.qh>
@@ -59,7 +60,7 @@ void LaunchDebris (entity this, string debrisname, vector force)
        dbr.skin = this.debrisskin;
        dbr.colormap = this.colormap; // inherit team colors
        dbr.owner = this; // do not be affected by our own explosion
-       dbr.movetype = this.debrismovetype;
+       set_movetype(dbr, this.debrismovetype);
        dbr.solid = this.debrissolid;
        if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
                setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
@@ -88,8 +89,6 @@ void func_breakable_colormod(entity this)
                this.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
        else
                this.colormod = '1 1 1';
-
-       CSQCMODEL_AUTOUPDATE(this);
 }
 
 void func_breakable_look_destroyed(entity this)
@@ -108,24 +107,22 @@ void func_breakable_look_destroyed(entity this)
                        this.origin_z = floorZ;
                }
                _setmodel(this, this.mdl_dead);
+               ApplyMinMaxScaleAngles(this);
                this.effects &= ~EF_NODRAW;
        }
 
-       CSQCMODEL_AUTOUPDATE(this);
-
        this.solid = SOLID_NOT;
 }
 
 void func_breakable_look_restore(entity this)
 {
        _setmodel(this, this.mdl);
+       ApplyMinMaxScaleAngles(this);
        this.effects &= ~EF_NODRAW;
 
        if(this.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
                setorigin(this, this.dropped_origin);
 
-       CSQCMODEL_AUTOUPDATE(this);
-
        this.solid = SOLID_BSP;
 }
 
@@ -133,6 +130,8 @@ void func_breakable_behave_destroyed(entity this)
 {
        this.health = this.max_health;
        this.takedamage = DAMAGE_NO;
+       if(this.bot_attack)
+               IL_REMOVE(g_bot_targets, this);
        this.bot_attack = false;
        this.event_damage = func_null;
        this.state = 1;
@@ -143,6 +142,13 @@ void func_breakable_behave_destroyed(entity this)
                stopsound (this, CH_TRIGGER_SINGLE);
 }
 
+void func_breakable_think(entity this)
+{
+       this.nextthink = time;
+       CSQCMODEL_AUTOUPDATE(this);
+}
+
+void func_breakable_destroy(entity this, entity actor, entity trigger);
 void func_breakable_behave_restore(entity this)
 {
        this.health = this.max_health;
@@ -154,11 +160,17 @@ void func_breakable_behave_restore(entity this)
        if(!(this.spawnflags & 4))
        {
                this.takedamage = DAMAGE_AIM;
+               if(!this.bot_attack)
+                       IL_PUSH(g_bot_targets, this);
                this.bot_attack = true;
                this.event_damage = func_breakable_damage;
        }
+       if(this.spawnflags & 4)
+               this.use = func_breakable_destroy; // don't need to set it usually, as .use isn't reset
        this.state = 0;
-       this.nextthink = 0; // cancel auto respawn
+       //this.nextthink = 0; // cancel auto respawn
+       setthink(this, func_breakable_think);
+       this.nextthink = time + 0.1;
        func_breakable_colormod(this);
        if (this.noise1)
                _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
@@ -177,16 +189,12 @@ void func_breakable_destroyed(entity this)
 {
        func_breakable_look_destroyed(this);
        func_breakable_behave_destroyed(this);
-
-       CSQCMODEL_AUTOUPDATE(this);
 }
 
 void func_breakable_restore(entity this, entity actor, entity trigger)
 {
        func_breakable_look_restore(this);
        func_breakable_behave_restore(this);
-
-       CSQCMODEL_AUTOUPDATE(this);
 }
 
 void func_breakable_restore_self(entity this)
@@ -221,6 +229,7 @@ void func_breakable_destroy(entity this, entity actor, entity trigger)
 
        if(this.respawntime)
        {
+               CSQCMODEL_AUTOUPDATE(this);
                setthink(this, func_breakable_restore_self);
                this.nextthink = time + this.respawntime + crandom() * this.respawntimejitter;
        }
@@ -271,6 +280,7 @@ void func_breakable_damage(entity this, entity inflictor, entity attacker, float
                // do not explode NOW but in the NEXT FRAME!
                // because recursive calls to RadiusDamage are not allowed
                this.nextthink = time;
+               CSQCMODEL_AUTOUPDATE(this);
                setthink(this, func_breakable_destroy_self);
        }
 }
@@ -283,8 +293,6 @@ void func_breakable_reset(entity this)
                func_breakable_behave_destroyed(this);
        else
                func_breakable_behave_restore(this);
-
-       CSQCMODEL_AUTOUPDATE(this);
 }
 
 // destructible walls that can be used to trigger target_objective_decrease
@@ -350,7 +358,7 @@ spawnfunc(func_breakable)
        this.reset = func_breakable_reset;
        this.reset(this);
 
-       this.init_for_player_needed = 1;
+       IL_PUSH(g_initforplayer, this);
        this.init_for_player = func_breakable_init_for_player;
 
        CSQCMODEL_AUTOINIT(this);
index 75cfb94c690cf09d51c5ad8eaa1211bf5012043c..761a2c7a98f350fe53629bc13f74e3874eb19673 100644 (file)
@@ -1,8 +1,5 @@
-#ifndef TRIGGERS_FUNC_BREAKABLE_H
-#define TRIGGERS_FUNC_BREAKABLE_H
+#pragma once
 
 #ifdef SVQC
 spawnfunc(func_breakable);
 #endif
-
-#endif
index a8ec50a86540a7be10fed5418c2bcd1b2b1786a3..916ff8ca1f1434c4f5053f995484df4bcb43cec2 100644 (file)
@@ -1,3 +1,4 @@
+#include "button.qh"
 #ifdef SVQC
 // button and multiple button
 
@@ -28,7 +29,7 @@ void button_return(entity this)
 }
 
 
-void button_blocked(entity this)
+void button_blocked(entity this, entity blocker)
 {
        // do nothing, just don't come all the way back out
 }
@@ -68,17 +69,17 @@ void button_use(entity this, entity actor, entity trigger)
        button_fire(this);
 }
 
-void button_touch(entity this)
+void button_touch(entity this, entity toucher)
 {
-       if (!other)
+       if (!toucher)
                return;
-       if (!other.iscreature)
+       if (!toucher.iscreature)
                return;
-       if(other.velocity * this.movedir < 0)
+       if(toucher.velocity * this.movedir < 0)
                return;
-       this.enemy = other;
-       if (other.owner)
-               this.enemy = other.owner;
+       this.enemy = toucher;
+       if (toucher.owner)
+               this.enemy = toucher.owner;
        button_fire (this);
 }
 
diff --git a/qcsrc/common/triggers/func/button.qh b/qcsrc/common/triggers/func/button.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index e2335b88314fef3d6470f5854860df0b09573cad..90328da23002dd61a1a8cc37fb9b6c93705fb7d9 100644 (file)
@@ -1,3 +1,4 @@
+#include "conveyor.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR)
 
 void conveyor_think(entity this)
@@ -8,47 +9,51 @@ void conveyor_think(entity this)
        this.move_time = time;
        if(dt <= 0) { return; }
 #endif
-       entity e;
 
        // set mythis as current conveyor where possible
-       for(e = NULL; (e = findentity(e, conveyor, this)); )
-               e.conveyor = NULL;
+       IL_EACH(g_conveyed, it.conveyor == this,
+       {
+               it.conveyor = NULL;
+               IL_REMOVE(g_conveyed, it);
+       });
 
        if(this.state)
        {
-               for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain)
-                       if(!e.conveyor.state)
-                               if(isPushable(e))
+               FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, !it.conveyor.state && isPushable(it),
+               {
+                       vector emin = it.absmin;
+                       vector emax = it.absmax;
+                       if(this.solid == SOLID_BSP)
+                       {
+                               emin -= '1 1 1';
+                               emax += '1 1 1';
+                       }
+                       if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
+                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
                                {
-                                       vector emin = e.absmin;
-                                       vector emax = e.absmax;
-                                       if(this.solid == SOLID_BSP)
-                                       {
-                                               emin -= '1 1 1';
-                                               emax += '1 1 1';
-                                       }
-                                       if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
-                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate
-                                                       e.conveyor = this;
+                                       if(!it.conveyor)
+                                               IL_PUSH(g_conveyed, it);
+                                       it.conveyor = this;
                                }
+               });
 
-               for(e = NULL; (e = findentity(e, conveyor, this)); )
+               IL_EACH(g_conveyed, it.conveyor == this,
                {
-                       if(IS_CLIENT(e)) // doing it via velocity has quite some advantages
+                       if(IS_CLIENT(it)) // doing it via velocity has quite some advantages
                                continue; // done in SV_PlayerPhysics   continue;
 
-                       setorigin(e, e.origin + this.movedir * PHYS_INPUT_FRAMETIME);
-                       move_out_of_solid(e);
+                       setorigin(it, it.origin + this.movedir * PHYS_INPUT_FRAMETIME);
+                       move_out_of_solid(it);
 #ifdef SVQC
-                       UpdateCSQCProjectile(e);
+                       UpdateCSQCProjectile(it);
 #endif
                        /*
                        // stupid conveyor code
-                       tracebox(e.origin, e.mins, e.maxs, e.origin + this.movedir * sys_frametime, MOVE_NORMAL, e);
+                       tracebox(it.origin, it.mins, it.maxs, it.origin + this.movedir * sys_frametime, MOVE_NORMAL, it);
                        if(trace_fraction > 0)
-                               setorigin(e, trace_endpos);
+                               setorigin(it, trace_endpos);
                        */
-               }
+               });
        }
 
 #ifdef SVQC
@@ -141,7 +146,7 @@ spawnfunc(func_conveyor)
 {
        SetMovedir(this);
        InitMovingBrushTrigger(this);
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        conveyor_init(this);
 }
 
@@ -152,12 +157,12 @@ void conveyor_draw(entity this) { conveyor_think(this); }
 void conveyor_init(entity this)
 {
        this.draw = conveyor_draw;
+       IL_PUSH(g_drawables, this);
        this.drawmask = MASK_NORMAL;
 
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.model = "";
        this.solid = SOLID_TRIGGER;
-       this.move_origin = this.origin;
        this.move_time = time;
 }
 
diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh
new file mode 100644 (file)
index 0000000..c12b52d
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+
+IntrusiveList g_conveyed;
+STATIC_INIT(g_conveyed) { g_conveyed = IL_NEW(); }
index f32a8d75ded3ac456e217e5af07c07a22c1c27e1..8a82802607bdcdb09f505d11248419d5e4d20977 100644 (file)
@@ -1,3 +1,4 @@
+#include "door.qh"
 /*
 
 Doors are similar to buttons, but can spawn a fat trigger field around them
@@ -23,33 +24,33 @@ THINK FUNCTIONS
 void door_go_down(entity this);
 void door_go_up(entity this);
 void door_rotating_go_down(entity this);
-void door_rotating_go_up(entity this);
+void door_rotating_go_up(entity this, entity oth);
 
-void door_blocked(entity this)
+void door_blocked(entity this, entity blocker)
 {
        if((this.spawnflags & 8)
 #ifdef SVQC
-               && (other.takedamage != DAMAGE_NO)
+               && (blocker.takedamage != DAMAGE_NO)
 #elif defined(CSQC)
-               && !IS_DEAD(other)
+               && !IS_DEAD(blocker)
 #endif
        )
        { // KIll Kill Kill!!
 #ifdef SVQC
-               Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+               Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
 #endif
        }
        else
        {
 #ifdef SVQC
-               if((this.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
-                       Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+               if((this.dmg) && (blocker.takedamage == DAMAGE_YES))    // Shall we bite?
+                       Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
 #endif
 
                 // don't change direction for dead or dying stuff
-               if(IS_DEAD(other)
+               if(IS_DEAD(blocker)
 #ifdef SVQC
-                       && (other.takedamage == DAMAGE_NO)
+                       && (blocker.takedamage == DAMAGE_NO)
 #endif
                )
                {
@@ -61,7 +62,7 @@ void door_blocked(entity this)
                                door_go_up (this);
                        } else
                        {
-                               door_rotating_go_up (this);
+                               door_rotating_go_up(this, blocker);
                        }
                                else
                        if (this.classname == "door")
@@ -77,8 +78,8 @@ void door_blocked(entity this)
                else
                {
                        //gib dying stuff just to make sure
-                       if((this.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
-                               Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       if((this.dmg) && (blocker.takedamage != DAMAGE_NO))    // Shall we bite?
+                               Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
                }
 #endif
        }
@@ -235,15 +236,15 @@ void door_fire(entity this, entity actor, entity trigger)
                        door_go_up(e);
                } else {
                        // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
-                       if ((e.spawnflags & 2) && other.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) {
+                       if ((e.spawnflags & 2) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) {
                                e.lip = 666; // e.lip is used to remember reverse opening direction for door_rotating
                                e.pos2 = '0 0 0' - e.pos2;
                        }
                        // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
                        if (!((e.spawnflags & 2) &&  (e.spawnflags & 8) && e.state == STATE_DOWN
-                               && (((e.lip == 666) && (other.trigger_reverse == 0)) || ((e.lip != 666) && (other.trigger_reverse != 0)))))
+                               && (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0)))))
                        {
-                               door_rotating_go_up(e);
+                               door_rotating_go_up(e, trigger);
                        }
                }
                e = e.enemy;
@@ -289,9 +290,9 @@ Prints messages
 ================
 */
 
-void door_touch(entity this)
+void door_touch(entity this, entity toucher)
 {
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
        if (this.owner.door_finished > time)
                return;
@@ -301,36 +302,35 @@ void door_touch(entity this)
 #ifdef SVQC
        if (!(this.owner.dmg) && (this.owner.message != ""))
        {
-               if (IS_CLIENT(other))
-                       centerprint(other, this.owner.message);
-               play2(other, this.owner.noise);
+               if (IS_CLIENT(toucher))
+                       centerprint(toucher, this.owner.message);
+               play2(toucher, this.owner.noise);
        }
 #endif
 }
 
-void door_generic_plat_blocked(entity this)
+void door_generic_plat_blocked(entity this, entity blocker)
 {
-
-       if((this.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
+       if((this.spawnflags & 8) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!!
 #ifdef SVQC
-               Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+               Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
 #endif
        }
        else
        {
 
 #ifdef SVQC
-               if((this.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
-                       Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+               if((this.dmg) && (blocker.takedamage == DAMAGE_YES))    // Shall we bite?
+                       Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
 #endif
 
                 //Dont chamge direction for dead or dying stuff
-               if(IS_DEAD(other) && (other.takedamage == DAMAGE_NO))
+               if(IS_DEAD(blocker) && (blocker.takedamage == DAMAGE_NO))
                {
                        if (this.wait >= 0)
                        {
                                if (this.state == STATE_DOWN)
-                                       door_rotating_go_up (this);
+                                       door_rotating_go_up (this, blocker);
                                else
                                        door_rotating_go_down (this);
                        }
@@ -339,8 +339,8 @@ void door_generic_plat_blocked(entity this)
                else
                {
                        //gib dying stuff just to make sure
-                       if((this.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
-                               Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       if((this.dmg) && (blocker.takedamage != DAMAGE_NO))    // Shall we bite?
+                               Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
                }
 #endif
        }
@@ -383,7 +383,7 @@ void door_rotating_go_down(entity this)
        SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom);
 }
 
-void door_rotating_go_up(entity this)
+void door_rotating_go_up(entity this, entity oth)
 {
        if (this.state == STATE_UP)
                return;         // already going up
@@ -401,7 +401,7 @@ void door_rotating_go_up(entity this)
        string oldmessage;
        oldmessage = this.message;
        this.message = "";
-       SUB_UseTargets(this, NULL, other); // TODO: is other needed here?
+       SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here?
        this.message = oldmessage;
 }
 
@@ -414,13 +414,13 @@ Spawned if a door lacks a real activator
 =========================================
 */
 
-void door_trigger_touch(entity this)
+void door_trigger_touch(entity this, entity toucher)
 {
-       if (other.health < 1)
+       if (toucher.health < 1)
 #ifdef SVQC
-               if (!((other.iscreature || (other.flags & FL_PROJECTILE)) && !IS_DEAD(other)))
+               if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher)))
 #elif defined(CSQC)
-               if(!((IS_CLIENT(other) || other.classname == "csqcprojectile") && !IS_DEAD(other)))
+               if(!((IS_CLIENT(toucher) || toucher.classname == "csqcprojectile") && !IS_DEAD(toucher)))
 #endif
                        return;
 
@@ -428,12 +428,12 @@ void door_trigger_touch(entity this)
                return;
 
        // check if door is locked
-       if (!door_check_keys(this, other))
+       if (!door_check_keys(this, toucher))
                return;
 
        this.door_finished = time + 1;
 
-       door_use(this.owner, other, NULL);
+       door_use(this.owner, toucher, NULL);
 }
 
 void door_spawnfield(entity this, vector fmins, vector fmaxs)
@@ -442,7 +442,7 @@ void door_spawnfield(entity this, vector fmins, vector fmaxs)
        vector  t1 = fmins, t2 = fmaxs;
 
        trigger = new(doortriggerfield);
-       trigger.movetype = MOVETYPE_NONE;
+       set_movetype(trigger, MOVETYPE_NONE);
        trigger.solid = SOLID_TRIGGER;
        trigger.owner = this;
 #ifdef SVQC
@@ -526,7 +526,7 @@ void LinkDoors(entity this)
                        break;
                }
        }
-       LOG_TRACE("\n");
+       LOG_TRACE("");
 
        // collect health, targetname, message, size
        cmins = this.absmin;
@@ -816,7 +816,7 @@ NET_HANDLE(ENT_CLIENT_DOOR, bool isnew)
                this.SUB_LTIME = ReadCoord();
 
                this.solid = SOLID_BSP;
-               this.movetype = MOVETYPE_PUSH;
+               set_movetype(this, MOVETYPE_PUSH);
                this.use = door_use;
 
                LinkDoors(this);
@@ -825,10 +825,7 @@ NET_HANDLE(ENT_CLIENT_DOOR, bool isnew)
                        door_init_startopen(this);
 
                this.move_time = time;
-               this.move_origin = this.origin;
-               this.move_movetype = MOVETYPE_PUSH;
-               this.move_angles = this.angles;
-               this.move_blocked = door_blocked;
+               set_movetype(this, MOVETYPE_PUSH);
        }
 
        if(sf & SF_TRIGGER_RESET)
@@ -842,7 +839,6 @@ NET_HANDLE(ENT_CLIENT_DOOR, bool isnew)
                this.origin_y = ReadCoord();
                this.origin_z = ReadCoord();
                setorigin(this, this.origin);
-               this.move_origin = this.origin;
 
                this.pos1_x = ReadCoord();
                this.pos1_y = ReadCoord();
index 31171899fc691a889e204ff06cee003b68b21bea..2c72dc9cf07c1b249d91648683f0e3f4396017a9 100644 (file)
@@ -1,3 +1,4 @@
+#include "door_rotating.qh"
 #ifdef SVQC
 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
 if two doors touch, they are assumed to be connected and operate as a unit.
diff --git a/qcsrc/common/triggers/func/door_rotating.qh b/qcsrc/common/triggers/func/door_rotating.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index f02fc61f646219626c5cf270df04e4763fb4d0a6..0b8930aec5a494aca16a3a889a5b02fdb2df8bf9 100644 (file)
@@ -1,3 +1,4 @@
+#include "door_secret.qh"
 #ifdef SVQC
 void fd_secret_move1(entity this);
 void fd_secret_move2(entity this);
@@ -19,6 +20,8 @@ void fd_secret_use(entity this, entity actor, entity trigger)
        string message_save;
 
        this.health = 10000;
+       if(!this.bot_attack)
+               IL_PUSH(g_bot_targets, this);
        this.bot_attack = true;
 
        // exit if still moving around...
@@ -135,7 +138,7 @@ void fd_secret_done(entity this)
 
 .float door_finished;
 
-void secret_blocked(entity this)
+void secret_blocked(entity this, entity blocker)
 {
        if (time < this.door_finished)
                return;
@@ -150,9 +153,9 @@ secret_touch
 Prints messages
 ================
 */
-void secret_touch(entity this)
+void secret_touch(entity this, entity toucher)
 {
-       if (!other.iscreature)
+       if (!toucher.iscreature)
                return;
        if (this.door_finished > time)
                return;
@@ -161,9 +164,9 @@ void secret_touch(entity this)
 
        if (this.message)
        {
-               if (IS_CLIENT(other))
-                       centerprint(other, this.message);
-               play2(other, this.noise);
+               if (IS_CLIENT(toucher))
+                       centerprint(toucher, this.message);
+               play2(toucher, this.noise);
        }
 }
 
diff --git a/qcsrc/common/triggers/func/door_secret.qh b/qcsrc/common/triggers/func/door_secret.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 52eab115a0655790538c6c43471dabaafbe32be8..600f3937301fdbd6695e1272e1623bfbd76ea41b 100644 (file)
@@ -1,3 +1,4 @@
+#include "fourier.qh"
 #ifdef SVQC
 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
diff --git a/qcsrc/common/triggers/func/fourier.qh b/qcsrc/common/triggers/func/fourier.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 628355cb79aa0280f2173848d03c0bca5387e419..4e8b94cd1c88779b56d0ec81fc99b429bc0bcf55 100644 (file)
@@ -1,8 +1,5 @@
-#ifndef TRIGGERS_FUNC_INCLUDE_H
-#define TRIGGERS_FUNC_INCLUDE_H
+#pragma once
 
 #include "door.qh"
 #include "ladder.qh"
 #include "train.qh"
-
-#endif
index af5065643bf9a6957fd0de37d50cba53a7f58d9c..5ff2bdcc247133ac99b9c7a95f1ae4da3fe38a82 100644 (file)
@@ -1,21 +1,22 @@
+#include "ladder.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_LADDER)
 
-void func_ladder_touch(entity this)
+void func_ladder_touch(entity this, entity toucher)
 {
 #ifdef SVQC
-       if (!other.iscreature)
+       if (!toucher.iscreature)
                return;
-       if(IS_VEHICLE(other))
+       if(IS_VEHICLE(toucher))
                return;
 #elif defined(CSQC)
-       if(!other.isplayermodel)
+       if(!toucher.isplayermodel)
                return;
 #endif
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
-       other.ladder_time = time + 0.1;
-       other.ladder_entity = this;
+       toucher.ladder_time = time + 0.1;
+       toucher.ladder_entity = this;
 }
 
 #ifdef SVQC
index 05f7b75a67602ead968210b1bcf94343d4c32d3c..946712509b0fbea3b1a5752cdcf6da94531a3e3e 100644 (file)
@@ -1,3 +1,4 @@
+#include "pendulum.qh"
 #ifdef SVQC
 .float freq;
 void func_pendulum_controller_think(entity this)
diff --git a/qcsrc/common/triggers/func/pendulum.qh b/qcsrc/common/triggers/func/pendulum.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 6f7ebaad31522130f7b69bf3509346c2e1e5098c..5d90924daa248b0e079f8ff4bc99bf5b6c4e125e 100644 (file)
@@ -1,3 +1,4 @@
+#include "plat.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_PLAT)
 
 #ifdef SVQC
@@ -168,17 +169,16 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew)
 
                this.classname = "plat";
                this.solid = SOLID_BSP;
-               this.movetype = MOVETYPE_PUSH;
+               set_movetype(this, MOVETYPE_PUSH);
                this.drawmask = MASK_NORMAL;
                this.draw = plat_draw;
+               if (isnew) IL_PUSH(g_drawables, this);
                this.use = plat_use;
                this.entremove = trigger_remove_generic;
 
                plat_reset(this); // also called here
 
-               this.move_movetype = MOVETYPE_PUSH;
-               this.move_origin = this.origin;
-               this.move_angles = this.angles;
+               set_movetype(this, MOVETYPE_PUSH);
                this.move_time = time;
 
                plat_spawn_inside_trigger(this);
@@ -188,8 +188,6 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew)
        {
                plat_reset(this);
 
-               this.move_origin = this.origin;
-               this.move_angles = this.angles;
                this.move_time = time;
        }
        return true;
diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 1705d4081ead4bafe1a874992dbe379843e5f5b8..a0773f249a82ccfe6bc52f0fac17cb063381cbd6 100644 (file)
@@ -1,3 +1,4 @@
+#include "pointparticles.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES)
 
 #ifdef SVQC
@@ -378,6 +379,7 @@ NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
        setsize(this, this.mins, this.maxs);
        this.solid = SOLID_NOT;
        this.draw = Draw_PointParticles;
+       if (isnew) IL_PUSH(g_drawables, this);
        this.entremove = Ent_PointParticles_Remove;
 }
 #endif
diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index dc569f69a1a738a1f8dbecefa82d6d69ee12fbc9..c8b4e2924376fb510b27ba4ffb2c6d4dc1cba511 100644 (file)
@@ -1,3 +1,4 @@
+#include "rainsnow.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW)
 
 #ifdef SVQC
@@ -35,7 +36,7 @@ spawnfunc(func_rain)
        if (!this.dest)
                this.dest = '0 0 -700';
        this.angles = '0 0 0';
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.solid = SOLID_NOT;
        SetBrushEntityModel(this);
        if (!this.cnt)
@@ -73,7 +74,7 @@ spawnfunc(func_snow)
        if (!this.dest)
                this.dest = '0 0 -300';
        this.angles = '0 0 0';
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.solid = SOLID_NOT;
        SetBrushEntityModel(this);
        if (!this.cnt)
@@ -92,14 +93,24 @@ spawnfunc(func_snow)
        Net_LinkEntity(this, false, 0, rainsnow_SendEntity);
 }
 #elif defined(CSQC)
+float autocvar_cl_rainsnow_maxdrawdist = 2048;
+
 void Draw_Rain(entity this)
 {
-    te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color);
+       vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist;
+       maxdist.z = 5;
+       if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5'))
+       //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist))
+       te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color);
 }
 
 void Draw_Snow(entity this)
 {
-    te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color);
+       vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist;
+       maxdist.z = 5;
+       if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5'))
+       //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist))
+       te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color);
 }
 
 NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew)
@@ -124,6 +135,7 @@ NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew)
        setorigin(this, this.origin);
        setsize(this, this.mins, this.maxs);
        this.solid = SOLID_NOT;
+       if (isnew) IL_PUSH(g_drawables, this);
        if(this.impulse)
                this.draw = Draw_Rain;
        else
diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 22f3dedea282926edb76fd55ec172d740aad02af..1adaea91d354db13b259a0109fb86da46efd3352 100644 (file)
@@ -1,3 +1,4 @@
+#include "rotating.qh"
 #ifdef SVQC
 void func_rotating_setactive(entity this, int astate)
 {
diff --git a/qcsrc/common/triggers/func/rotating.qh b/qcsrc/common/triggers/func/rotating.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index db5081b61b474659b09a7f98f0cd58742d98f86f..9c2fba8ada8ed27ca928ba17218beadffcd70111 100644 (file)
@@ -1,3 +1,4 @@
+#include "stardust.qh"
 #ifdef SVQC
 spawnfunc(func_stardust)
 {
diff --git a/qcsrc/common/triggers/func/stardust.qh b/qcsrc/common/triggers/func/stardust.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 3f6af7d00185dd256bc1c77253ae66d66bd67f05..2e4356bab3214d5f124150e20975ee14056e0126 100644 (file)
@@ -1,4 +1,6 @@
+#include "train.qh"
 .float train_wait_turning;
+.entity future_target;
 void train_next(entity this);
 #ifdef SVQC
 void train_use(entity this, entity actor, entity trigger);
@@ -13,7 +15,7 @@ void train_wait(entity this)
        {
                entity targ, cp;
                vector ang;
-               targ = find(NULL, targetname, this.target);
+               targ = this.future_target;
                if((this.spawnflags & 1) && targ.curvetarget)
                        cp = find(NULL, targetname, targ.curvetarget);
                else
@@ -40,7 +42,7 @@ void train_wait(entity this)
 #endif
 
 #ifdef SVQC
-       entity tg = find(NULL, targetname, this.target);
+       entity tg = this.future_target;
        if(tg.spawnflags & 4)
        {
                this.use = train_use;
@@ -61,13 +63,34 @@ void train_wait(entity this)
        }
 }
 
+entity train_next_find(entity this)
+{
+       if(this.target_random)
+       {
+               RandomSelection_Init();
+               for(entity t = NULL; (t = find(t, targetname, this.target));)
+               {
+                       RandomSelection_AddEnt(t, 1, 0);
+               }
+               return RandomSelection_chosen_ent;
+       }
+       else
+       {
+               return find(NULL, targetname, this.target);
+       }
+}
+
 void train_next(entity this)
 {
-       entity targ, cp = NULL;
+       entity targ = NULL, cp = NULL;
        vector cp_org = '0 0 0';
 
-       targ = find(NULL, targetname, this.target);
+       targ = this.future_target;
+
        this.target = targ.target;
+       this.target_random = targ.target_random;
+       this.future_target = train_next_find(targ);
+
        if (this.spawnflags & 1)
        {
                if(targ.curvetarget)
@@ -185,9 +208,11 @@ void train_use(entity this, entity actor, entity trigger)
 
 void func_train_find(entity this)
 {
-       entity targ;
-       targ = find(NULL, targetname, this.target);
+       entity targ = train_next_find(this);
        this.target = targ.target;
+       this.target_random = targ.target_random;
+       // save the future target for later
+       this.future_target = train_next_find(targ);
        if (this.target == "")
                objerror(this, "func_train_find: no next target");
        SUB_SETORIGIN(this, targ.origin - this.view_ofs);
@@ -307,9 +332,10 @@ NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew)
 
                this.classname = "func_train";
                this.solid = SOLID_BSP;
-               this.movetype = MOVETYPE_PUSH;
+               set_movetype(this, MOVETYPE_PUSH);
                this.drawmask = MASK_NORMAL;
                this.draw = train_draw;
+               if (isnew) IL_PUSH(g_drawables, this);
                this.entremove = trigger_remove_generic;
 
                if(set_platmovetype(this, this.platmovetype))
@@ -322,13 +348,9 @@ NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew)
                //func_train_find();
 
                // but we will need these
-               //this.move_nextthink = this.move_ltime + 0.1;
-               //this.move_think = train_next;
                train_next(this);
 
-               this.move_movetype = MOVETYPE_PUSH;
-               this.move_origin = this.origin;
-               this.move_angles = this.angles;
+               set_movetype(this, MOVETYPE_PUSH);
                this.move_time = time;
        }
 
index accdc998350a556af775d72eac796ef9675abfb2..18d58d649177124543cec55d6834644e99ec58f9 100644 (file)
@@ -1,3 +1,4 @@
+#include "vectormamamam.qh"
 #ifdef SVQC
 // reusing some fields havocbots declared
 .entity wp00, wp01, wp02, wp03;
diff --git a/qcsrc/common/triggers/func/vectormamamam.qh b/qcsrc/common/triggers/func/vectormamamam.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 25688e3ecb692e7114c6ec739d5dbf83236d7aa9..87c07c14df58db888f78d5cd293de5830bd37676 100644 (file)
@@ -1,8 +1,7 @@
-#ifndef TRIGGERS_INCLUDE_H
-#define TRIGGERS_INCLUDE_H
+#pragma once
 
 // some required common stuff
-#ifdef CSQC
+#ifdef SVQC
        #include <server/item_key.qh>
 #endif
 #include "triggers.qh"
@@ -18,5 +17,3 @@
 
 // trigger
 #include "trigger/include.qh"
-
-#endif
index 38772a295562e09ec2138f05f7e54dec1a445e79..dcc44710fc4e2fe6f746d3c8fd85b0d8da06f7de 100644 (file)
@@ -1,3 +1,4 @@
+#include "corner.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_CORNER)
 
 #ifdef SVQC
diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 69222bf2d81a2ecf03113904c6c97c8053e4a0ac..63db2c18fa83ac172f62dce5a7b187fccc0c63c4 100644 (file)
@@ -1,3 +1,4 @@
+#include "follow.qh"
 // the way this entity works makes it no use to CSQC, as it removes itself instantly
 
 #ifdef SVQC
@@ -41,13 +42,13 @@ void follow_init(entity this)
                }
 
                dst.solid = SOLID_NOT; // solid doesn't work with attachment
-               remove(this);
+               delete(this);
        }
        else
        {
                if(this.spawnflags & 2)
                {
-                       dst.movetype = MOVETYPE_FOLLOW;
+                       set_movetype(dst, MOVETYPE_FOLLOW);
                        dst.aiment = src;
                        // dst.punchangle = '0 0 0'; // keep unchanged
                        dst.view_ofs = dst.origin;
@@ -58,7 +59,7 @@ void follow_init(entity this)
                        follow_sameorigin(dst, src);
                }
 
-               remove(this);
+               delete(this);
        }
 }
 
diff --git a/qcsrc/common/triggers/misc/follow.qh b/qcsrc/common/triggers/misc/follow.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 8965c62906e01e349fb2b5f85867746414907878..bbe5ce03d6d18fca6344507904a0c22dac1d081e 100644 (file)
@@ -1,3 +1,4 @@
+#include "include.qh"
 #include "corner.qc"
 #include "follow.qc"
 #include "laser.qc"
diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 399ba5baf5b9f134476ae64fcba2147ccbbd3b62..2059a8126d53101672aec3410e6560e8812d2891 100644 (file)
@@ -1,3 +1,4 @@
+#include "laser.qh"
 #if defined(CSQC)
        #include <lib/csqcmodel/interpolate.qh>
        #include <client/main.qh>
@@ -377,5 +378,6 @@ NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
 
        InterpolateOrigin_Note(this);
        this.draw = Draw_Laser;
+       if (isnew) IL_PUSH(g_drawables, this);
 }
 #endif
diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index ab15a689192c50ac21e2c639b54c3d26f951d8ac..fc3cec863a26920d0547df1143e75af283631f88 100644 (file)
@@ -1,3 +1,4 @@
+#include "teleport_dest.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST)
 
 #ifdef SVQC
diff --git a/qcsrc/common/triggers/misc/teleport_dest.qh b/qcsrc/common/triggers/misc/teleport_dest.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c089039a60bccf1549b1602fb643380c7b64129c..10af3e1944316a7f1b6299297cfbd6a3686cb3d3 100644 (file)
@@ -1,17 +1,18 @@
-void generic_plat_blocked(entity this)
+#include "platforms.qh"
+void generic_plat_blocked(entity this, entity blocker)
 {
 #ifdef SVQC
-       if(this.dmg && other.takedamage != DAMAGE_NO)
+       if(this.dmg && blocker.takedamage != DAMAGE_NO)
        {
                if(this.dmgtime2 < time)
                {
-                       Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
                        this.dmgtime2 = time + this.dmgtime;
                }
 
                // Gib dead/dying stuff
-               if(IS_DEAD(other))
-                       Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+               if(IS_DEAD(blocker))
+                       Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
        }
 #endif
 }
@@ -23,7 +24,7 @@ void plat_spawn_inside_trigger(entity this)
 
        trigger = spawn();
        settouch(trigger, plat_center_touch);
-       trigger.movetype = MOVETYPE_NONE;
+       set_movetype(trigger, MOVETYPE_NONE);
        trigger.solid = SOLID_TRIGGER;
        trigger.enemy = this;
 
@@ -53,7 +54,7 @@ void plat_spawn_inside_trigger(entity this)
                        }
 
        // otherwise, something is fishy...
-       remove(trigger);
+       delete(trigger);
        objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
 }
 
@@ -86,18 +87,18 @@ void plat_go_up(entity this)
        SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top);
 }
 
-void plat_center_touch(entity this)
+void plat_center_touch(entity this, entity toucher)
 {
 #ifdef SVQC
-       if (!other.iscreature)
+       if (!toucher.iscreature)
                return;
 
-       if (other.health <= 0)
+       if (toucher.health <= 0)
                return;
 #elif defined(CSQC)
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
-       if(IS_DEAD(other))
+       if(IS_DEAD(toucher))
                return;
 #endif
 
@@ -107,16 +108,16 @@ void plat_center_touch(entity this)
                this.enemy.SUB_NEXTTHINK = this.enemy.SUB_LTIME + 1;
 }
 
-void plat_outside_touch(entity this)
+void plat_outside_touch(entity this, entity toucher)
 {
 #ifdef SVQC
-       if (!other.iscreature)
+       if (!toucher.iscreature)
                return;
 
-       if (other.health <= 0)
+       if (toucher.health <= 0)
                return;
 #elif defined(CSQC)
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
 #endif
 
@@ -128,34 +129,29 @@ void plat_outside_touch(entity this)
 
 void plat_trigger_use(entity this, entity actor, entity trigger)
 {
-#ifdef SVQC
        if (getthink(this))
                return;         // already activated
-#elif defined(CSQC)
-       if(this.move_think)
-               return;
-#endif
        plat_go_down(this);
 }
 
 
-void plat_crush(entity this)
+void plat_crush(entity this, entity blocker)
 {
-       if((this.spawnflags & 4) && (other.takedamage != DAMAGE_NO))
+       if((this.spawnflags & 4) && (blocker.takedamage != DAMAGE_NO))
        { // KIll Kill Kill!!
 #ifdef SVQC
-               Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+               Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
 #endif
        }
        else
        {
 #ifdef SVQC
-               if((this.dmg) && (other.takedamage != DAMAGE_NO))
+               if((this.dmg) && (blocker.takedamage != DAMAGE_NO))
                {   // Shall we bite?
-                       Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
                        // Gib dead/dying stuff
-                       if(IS_DEAD(other))
-                               Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       if(IS_DEAD(blocker))
+                               Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
                }
 #endif
 
index 6bdfb23d68bf0ced7ea0ee836e6cbcb4bb6d0368..c40467494f367adf9c68af7390dbd55ee6ec120c 100644 (file)
@@ -1,16 +1,13 @@
-#ifndef PLATFORMS_H
-#define PLATFORMS_H
+#pragma once
 
 .float dmgtime2;
 
-void plat_center_touch(entity this);
-void plat_outside_touch(entity this);
+void plat_center_touch(entity this, entity toucher);
+void plat_outside_touch(entity this, entity toucher);
 void plat_trigger_use(entity this, entity actor, entity trigger);
 void plat_go_up(entity this);
 void plat_go_down(entity this);
-void plat_crush(entity this);
+void plat_crush(entity this, entity blocker);
 const float PLAT_LOW_TRIGGER = 1;
 
 .float dmg;
-
-#endif
index 51ef002736a32201d391cc9831b9a4f84f06389f..67eb18a6782b84f54abedd8813e258c51b846fb4 100644 (file)
@@ -1,3 +1,4 @@
+#include "subs.qh"
 void SUB_NullThink(entity this) { }
 
 void SUB_CalcMoveDone(entity this);
@@ -14,7 +15,7 @@ Applies some friction to this
 void SUB_Friction (entity this)
 {
        this.SUB_NEXTTHINK = time;
-       if(this.SUB_FLAGS & FL_ONGROUND)
+       if(IS_ONGROUND(this))
                this.SUB_VELOCITY = this.SUB_VELOCITY * (1 - frametime * this.friction);
 }
 
@@ -40,7 +41,7 @@ void SUB_VanishOrRemove (entity ent)
        else
        {
                // remove
-               remove (ent);
+               delete (ent);
        }
 }
 
@@ -144,7 +145,7 @@ void SUB_CalcMove_controller_think (entity this)
                // derivative: delta + 2 * delta2 (e.g. for angle positioning)
                entity own = this.owner;
                SUB_THINK(own, this.think1);
-               remove(this);
+               delete(this);
                SUB_THUNK(own)(own);
        }
 }
index 1ce3ea2e9e842864e7315270fab82622a784af81..4f98d5ae15cd92335c4b76282814566358ba1fa7 100644 (file)
@@ -1,42 +1,14 @@
-#ifndef SUBS_H
-#define SUBS_H
-
-#ifdef SVQC
-
-       #define SUB_ANGLES(s)           (s).angles
-       #define SUB_VELOCITY            velocity
-       #define SUB_AVELOCITY           avelocity
-       #define SUB_ORIGIN                      origin
-       #define SUB_SETORIGIN(s,v)      setorigin((s), (v))
-       #define SUB_NEXTTHINK           nextthink
-       #define SUB_THINK(e, f)     setthink(e, f)
-       #define SUB_THUNK(e)        getthink(e)
-       #define SUB_LTIME                       ltime
-       #define SUB_FLAGS                       flags
-
-#elif defined(CSQC)
-
-       void _Movetype_LinkEdict(entity this, float touch_triggers);
-
-       #define SUB_ANGLES(s)   (s).move_angles
-       #define SUB_VELOCITY    move_velocity
-       #define SUB_AVELOCITY   move_avelocity
-       #define SUB_ORIGIN              move_origin
-       #define SUB_NEXTTHINK   move_nextthink
-       #define SUB_THINK(e, f) ((e).move_think = (f))
-       #define SUB_THUNK(e)    ((e).move_think)
-       #define SUB_LTIME               move_ltime
-       #define SUB_FLAGS               move_flags
-
-.vector move_origin;
-
-       void SUB_SETORIGIN(entity s, vector v)
-       {
-               s.move_origin = v;
-               _Movetype_LinkEdict(s, true);
-       }
+#pragma once
 
-#endif
+#define SUB_ANGLES(s)          (s).angles
+#define SUB_VELOCITY           velocity
+#define SUB_AVELOCITY          avelocity
+#define SUB_ORIGIN                     origin
+#define SUB_SETORIGIN(s,v)     setorigin((s), (v))
+#define SUB_NEXTTHINK          nextthink
+#define SUB_THINK(e, f)     setthink(e, f)
+#define SUB_THUNK(e)        getthink(e)
+#define SUB_LTIME                      ltime
 
 
 void SUB_SetFade (entity ent, float when, float fading_time);
@@ -96,5 +68,3 @@ float STATE_DOWN              = 3;
 
 .float         max_health;             // players maximum health is stored here
 #endif
-
-#endif
index d4bc850de11eef0766ec3aeeefca3aca59ac75e6..6c006d42a91610e70cd3984bc076cdc61312885c 100644 (file)
@@ -1,3 +1,4 @@
+#include "changelevel.qh"
 #ifdef SVQC
 .string chmap, gametype;
 .entity chlevel_targ;
diff --git a/qcsrc/common/triggers/target/changelevel.qh b/qcsrc/common/triggers/target/changelevel.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 4e44b9762b901a762436b82ba5f507ebb840cfb4..c0f7cad443a0fd62f5cfad147ba3f207e0b662b5 100644 (file)
@@ -1,6 +1,3 @@
-#ifndef TRIGGERS_TARGET_INCLUDE_H
-#define TRIGGERS_TARGET_INCLUDE_H
+#pragma once
 
 #include "music.qh"
-
-#endif
index 5a24d2c5ed6e4e709ba4d2b5d445b1905e59ab23..5259276e43c0f1bb7c47ae87ea9365577e89bbc8 100644 (file)
@@ -1,3 +1,4 @@
+#include "location.qh"
 #ifdef SVQC
 void target_push_init(entity this);
 
diff --git a/qcsrc/common/triggers/target/location.qh b/qcsrc/common/triggers/target/location.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d43cbec769c542774e5c54ea4e8f575a6e186dcf..1f8cb00cb553a95623dec5d0eb10f3d0ca3c2e9f 100644 (file)
@@ -1,7 +1,9 @@
+#include "music.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include <common/constants.qh>
+    #include <common/net_linked.qh>
     #include <server/constants.qh>
     #include <server/defs.qh>
 #endif
@@ -11,6 +13,9 @@ REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC)
 
 #ifdef SVQC
 
+IntrusiveList g_targetmusic_list;
+STATIC_INIT(g_targetmusic_list) { g_targetmusic_list = IL_NEW(); }
+
 // values:
 //   volume
 //   noise
@@ -36,7 +41,8 @@ void target_music_reset(entity this)
 }
 void target_music_kill()
 {
-       FOREACH_ENTITY_CLASS("target_music", true, {
+       IL_EACH(g_targetmusic_list, true,
+       {
                it.volume = 0;
         if (it.targetname == "")
             target_music_sendto(it, MSG_ALL, 1);
@@ -64,6 +70,7 @@ spawnfunc(target_music)
        this.reset = target_music_reset;
        if(!this.volume)
                this.volume = 1;
+       IL_PUSH(g_targetmusic_list, this);
        if(this.targetname == "")
                target_music_sendto(this, MSG_INIT, 1);
        else
@@ -71,13 +78,13 @@ spawnfunc(target_music)
 }
 void TargetMusic_RestoreGame()
 {
-       for(entity e = NULL; (e = find(e, classname, "target_music")); )
+       IL_EACH(g_targetmusic_list, true,
        {
-               if(e.targetname == "")
-                       target_music_sendto(e, MSG_INIT, 1);
+               if(it.targetname == "")
+                       target_music_sendto(it, MSG_INIT, 1);
                else
-                       target_music_sendto(e, MSG_INIT, 0);
-       }
+                       target_music_sendto(it, MSG_INIT, 0);
+       });
 }
 // values:
 //   volume
@@ -223,7 +230,7 @@ void Net_TargetMusic()
                _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
                if(getsoundtime(e, CH_BGM_SINGLE) < 0)
                {
-                       LOG_TRACEF("Cannot initialize sound %s\n", e.noise);
+                       LOG_TRACEF("Cannot initialize sound %s", e.noise);
                        strunzone(e.noise);
                        e.noise = string_null;
                }
@@ -310,7 +317,7 @@ NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew)
                        _sound(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE);
                        if(getsoundtime(this, CH_BGM_SINGLE) < 0)
                        {
-                               LOG_TRACEF("Cannot initialize sound %s\n", this.noise);
+                               LOG_TRACEF("Cannot initialize sound %s", this.noise);
                                strunzone(this.noise);
                                this.noise = string_null;
                        }
index a9232107ebbbed9407716c1e112acfab553a35b8..80b3684a4e9a768b8ef17e4ea596f463d9cc886d 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TARGET_MUSIC_H
-#define TARGET_MUSIC_H
+#pragma once
 
 .float lifetime;
 
@@ -25,5 +24,3 @@ void Ent_TriggerMusic_Remove(entity this);
 #elif defined(SVQC)
 void target_music_kill();
 #endif
-
-#endif
index eced856d7ecfcf331d18a49a225b28083a95c626..4eed8ef345872f3726bcb03fd4904fdcb63a871e 100644 (file)
@@ -1,3 +1,4 @@
+#include "spawn.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
@@ -287,8 +288,10 @@ void target_spawn_use(entity this, entity actor, entity trigger)
        else
        {
                // edit entity
-               for(entity e = NULL; (e = find(e, targetname, this.target)); )
-                       target_spawn_useon(e, this, actor, trigger);
+               FOREACH_ENTITY_STRING(targetname, this.target,
+               {
+                       target_spawn_useon(it, this, actor, trigger);
+               });
        }
 }
 
diff --git a/qcsrc/common/triggers/target/spawn.qh b/qcsrc/common/triggers/target/spawn.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 7db93a3d1e46c665227b15b5d7223e6003651376..af327b443bc21febb1c8494829bfd10e1fb4543a 100644 (file)
@@ -1,3 +1,4 @@
+#include "speaker.qh"
 #ifdef SVQC
 // TODO add a way to do looped sounds with sound(); then complete this entity
 void target_speaker_use_off(entity this, entity actor, entity trigger);
@@ -115,7 +116,7 @@ spawnfunc(target_speaker)
        else if(this.spawnflags & 1) // LOOPED_ON
        {
                ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten);
-               remove(this);
+               delete(this);
        }
        else if(this.spawnflags & 2) // LOOPED_OFF
        {
@@ -125,7 +126,7 @@ spawnfunc(target_speaker)
        {
                // Quake/Nexuiz fallback
                ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten);
-               remove(this);
+               delete(this);
        }
 }
 #endif
diff --git a/qcsrc/common/triggers/target/speaker.qh b/qcsrc/common/triggers/target/speaker.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index bdf1e0f1fb40e856f89823b0d3e347aae9a3be03..fe7155cc546ff60349f4ed22b164a5ca313ca73c 100644 (file)
@@ -1,3 +1,4 @@
+#include "voicescript.qh"
 #ifdef SVQC
 .entity voicescript; // attached voice script
 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
diff --git a/qcsrc/common/triggers/target/voicescript.qh b/qcsrc/common/triggers/target/voicescript.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index ba472ac55732e7f479c57fc6c97ccf028a06f20e..949f478fdd3c714e9ba3402416be4b182c348b7a 100644 (file)
@@ -92,7 +92,7 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle
                                        RandomSelection_Init();
                                        FOREACH_WORD(teleporter.noise, true,
                                        {
-                                               RandomSelection_Add(NULL, 0, it, 1, 1);
+                                               RandomSelection_AddString(it, 1, 1);
                                        });
                                        thesound = RandomSelection_chosen_string;
                                }
@@ -124,22 +124,20 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle
        UpdateCSQCProjectileAfterTeleport(player);
        UpdateItemAfterTeleport(player);
 #elif defined(CSQC)
-       from = player.move_origin;
-       player.move_origin = to;
-       player.move_angles = to_angles;
-       player.move_velocity = to_velocity;
-       player.move_flags &= ~FL_ONGROUND;
+       from = player.origin;
+       setorigin(player, to);
+       player.angles = to_angles;
+       player.velocity = to_velocity;
+       UNSET_ONGROUND(player);
        player.iflags |= IFLAG_TELEPORTED | IFLAG_V_ANGLE | IFLAG_ANGLES;
        player.csqcmodel_teleported = 1;
        player.v_angle = to_angles;
 
-       if(player.isplayermodel) // not for anything but the main player
+       if(player == csqcplayer) // not for anything but the main player
        {
-               setproperty(VF_ANGLES, player.move_angles);
-               setproperty(VF_CL_VIEWANGLES, player.move_angles);
+               setproperty(VF_ANGLES, player.angles);
+               setproperty(VF_CL_VIEWANGLES, player.angles);
        }
-
-       makevectors(player.move_angles);
 #endif
 
 #ifdef SVQC
@@ -187,21 +185,21 @@ entity Simple_TeleportPlayer(entity teleporter, entity player)
        else
        {
                RandomSelection_Init();
-               for(e = NULL; (e = find(e, targetname, teleporter.target)); )
+               FOREACH_ENTITY_STRING(targetname, teleporter.target,
                {
                        p = 1;
                        if(STAT(TELEPORT_TELEFRAG_AVOID, player))
                        {
                        #ifdef SVQC
-                               locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
+                               locout = it.origin + '0 0 1' * (1 - player.mins.z - 24);
                        #elif defined(CSQC)
-                               locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
+                               locout = it.origin + '0 0 1' * (1 - player.mins.z - 24);
                        #endif
                                if(check_tdeath(player, locout, '0 0 0', '0 0 0'))
                                        p = 0;
                        }
-                       RandomSelection_Add(e, 0, string_null, (e.cnt ? e.cnt : 1), p);
-               }
+                       RandomSelection_AddEnt(it, (it.cnt ? it.cnt : 1), p);
+               });
                e = RandomSelection_chosen_ent;
        }
 
@@ -213,35 +211,17 @@ entity Simple_TeleportPlayer(entity teleporter, entity player)
 
        makevectors(e.mangle);
 
-#ifdef SVQC
        if(e.speed)
                if(vdist(player.velocity, >, e.speed))
                        player.velocity = normalize(player.velocity) * max(0, e.speed);
-#elif defined(CSQC)
-       if(e.speed)
-               if(vdist(player.move_velocity, >, e.speed))
-                       player.move_velocity = normalize(player.move_velocity) * max(0, e.speed);
-#endif
 
-#ifdef SVQC
        if(STAT(TELEPORT_MAXSPEED, player))
                if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player)))
                        player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player));
-#elif defined(CSQC)
-       if(STAT(TELEPORT_MAXSPEED, player))
-               if(vdist(player.move_velocity, >, STAT(TELEPORT_MAXSPEED, player)))
-                       player.move_velocity = normalize(player.move_velocity) * max(0, STAT(TELEPORT_MAXSPEED, player));
-#endif
 
-#ifdef SVQC
        locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
 
        TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
-#elif defined(CSQC)
-       locout = e.origin + '0 0 1' * (1 - player.mins.z - 24);
-
-       TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.move_velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
-#endif
 
        return e;
 }
@@ -254,7 +234,7 @@ void teleport_findtarget(entity this)
        {
                ++n;
 #ifdef SVQC
-               if(e.movetype == MOVETYPE_NONE)
+               if(e.move_movetype == MOVETYPE_NONE)
                        waypoint_spawnforteleporter(this, e.origin, 0);
                if(e.classname != "info_teleport_destination")
                        LOG_INFO("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work.\n");
@@ -287,10 +267,10 @@ void teleport_findtarget(entity this)
 
 entity Teleport_Find(vector mi, vector ma)
 {
-       entity e;
-       for(e = NULL; (e = find(e, classname, "trigger_teleport")); )
-               if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, NULL))
-                       return e;
+       IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL),
+       {
+               return it;
+       });
        return NULL;
 }
 
@@ -310,7 +290,7 @@ void WarpZone_PostTeleportPlayer_Callback(entity pl)
        #ifdef SVQC
                if(!(pl.flags & FL_PROJECTILE))
        #elif defined(CSQC)
-               if(!(pl.move_flags & BIT(15))) // FL_PROJECTILE
+               if(!(pl.flags & BIT(15))) // FL_PROJECTILE
        #endif
                        LOG_INFO("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, ".\n");
                pl.owner = NULL;
index ef47b0c6c8d00f180394f9f882d6bac61950d80d..6f5f8cb76e21bec6f941b85e2aebccd767b5ba21 100644 (file)
@@ -1,5 +1,7 @@
-#ifndef T_TELEPORTERS_H
-#define T_TELEPORTERS_H
+#pragma once
+
+IntrusiveList g_teleporters;
+STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); }
 
 .entity pusher;
 const float TELEPORT_FLAG_SOUND = 1;
@@ -17,7 +19,7 @@ const float TELEPORT_SIMPLE = 2; // only do teleport, nothing special
 
 entity Simple_TeleportPlayer(entity teleporter, entity player);
 
-void Teleport_Touch (entity this);
+void Teleport_Touch(entity this, entity toucher);
 
 void teleport_findtarget(entity this);
 
@@ -92,5 +94,3 @@ void WarpZone_PostTeleportPlayer_Callback(entity pl);
                        if(head.isplayermodel) \
                                if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
 #endif
-
-#endif
index a4f850ba9ad548d9b76e14f67f64fd6f4851054a..8246aed7c329a67c8eaa10cf3f4822186ba62cfc 100644 (file)
@@ -1,3 +1,4 @@
+#include "counter.qh"
 #ifdef SVQC
 void counter_use(entity this, entity actor, entity trigger)
 {
diff --git a/qcsrc/common/triggers/trigger/counter.qh b/qcsrc/common/triggers/trigger/counter.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d6742fed3298c03f70882ee07871ed566a54beb5..dc1a781f81a0c81933fae9d47f63baf53506555a 100644 (file)
@@ -1,8 +1,9 @@
+#include "delay.qh"
 #ifdef SVQC
 void delay_use(entity this, entity actor, entity trigger)
 {
    setthink(this, SUB_UseTargets_self);
-   this.nextthink = this.wait;
+   this.nextthink = time + this.wait;
 }
 
 void delay_reset(entity this)
diff --git a/qcsrc/common/triggers/trigger/delay.qh b/qcsrc/common/triggers/trigger/delay.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 1d30db0e08f919a703a1d11da5d5b35bb0369802..6154f6bf0aa073e67c3aa522bb7a96501c541f6a 100644 (file)
@@ -1,3 +1,4 @@
+#include "disablerelay.qh"
 #ifdef SVQC
 void trigger_disablerelay_use(entity this, entity actor, entity trigger)
 {
diff --git a/qcsrc/common/triggers/trigger/disablerelay.qh b/qcsrc/common/triggers/trigger/disablerelay.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index e4923bdf545bdf8cb43740858ffc384071191539..af212ff5a42b7449bea067a2c755d4c86374782f 100644 (file)
@@ -1,3 +1,4 @@
+#include "flipflop.qh"
 #ifdef SVQC
 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
 "Flip-flop" trigger gate... lets only every second trigger event through
diff --git a/qcsrc/common/triggers/trigger/flipflop.qh b/qcsrc/common/triggers/trigger/flipflop.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index efddf8c77f89eb99787af0075e448e1035ad4082..72d76d183326437bdd4f5bcd7a38393d0a578268 100644 (file)
@@ -1,8 +1,9 @@
+#include "gamestart.qh"
 #ifdef SVQC
 void gamestart_use(entity this, entity actor, entity trigger)
 {
        SUB_UseTargets(this, this, trigger);
-       remove(this);
+       delete(this);
 }
 
 void gamestart_use_this(entity this)
diff --git a/qcsrc/common/triggers/trigger/gamestart.qh b/qcsrc/common/triggers/trigger/gamestart.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 9dcc710f0356b2102c0f20248c9a3e2e6739184b..3ea1562f084a93e4c73b7fdb0c9a26ac64dad90e 100644 (file)
@@ -1,3 +1,4 @@
+#include "gravity.qh"
 #ifdef SVQC
 .entity trigger_gravity_check;
 void trigger_gravity_remove(entity own)
@@ -6,7 +7,7 @@ void trigger_gravity_remove(entity own)
        {
                UpdateCSQCProjectile(own);
                own.gravity = own.trigger_gravity_check.gravity;
-               remove(own.trigger_gravity_check);
+               delete(own.trigger_gravity_check);
        }
        else
                backtrace("Removing a trigger_gravity_check with no valid owner");
@@ -22,7 +23,7 @@ void trigger_gravity_check_think(entity this)
                if(this.owner.trigger_gravity_check == this)
                        trigger_gravity_remove(this.owner);
                else
-                       remove(this);
+                       delete(this);
                return;
        }
        else
@@ -37,50 +38,50 @@ void trigger_gravity_use(entity this, entity actor, entity trigger)
        this.state = !this.state;
 }
 
-void trigger_gravity_touch(entity this)
+void trigger_gravity_touch(entity this, entity toucher)
 {
        float g;
 
        if(this.state != true)
                return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
        g = this.gravity;
 
        if (!(this.spawnflags & 1))
        {
-               if(other.trigger_gravity_check)
+               if(toucher.trigger_gravity_check)
                {
-                       if(this == other.trigger_gravity_check.enemy)
+                       if(this == toucher.trigger_gravity_check.enemy)
                        {
                                // same?
-                               other.trigger_gravity_check.count = 2; // gravity one more frame...
+                               toucher.trigger_gravity_check.count = 2; // gravity one more frame...
                                return;
                        }
 
                        // compare prio
-                       if(this.cnt > other.trigger_gravity_check.enemy.cnt)
-                               trigger_gravity_remove(other);
+                       if(this.cnt > toucher.trigger_gravity_check.enemy.cnt)
+                               trigger_gravity_remove(toucher);
                        else
                                return;
                }
-               other.trigger_gravity_check = spawn();
-               other.trigger_gravity_check.enemy = this;
-               other.trigger_gravity_check.owner = other;
-               other.trigger_gravity_check.gravity = other.gravity;
-               setthink(other.trigger_gravity_check, trigger_gravity_check_think);
-               other.trigger_gravity_check.nextthink = time;
-               other.trigger_gravity_check.count = 2;
-               if(other.gravity)
-                       g *= other.gravity;
+               toucher.trigger_gravity_check = spawn();
+               toucher.trigger_gravity_check.enemy = this;
+               toucher.trigger_gravity_check.owner = toucher;
+               toucher.trigger_gravity_check.gravity = toucher.gravity;
+               setthink(toucher.trigger_gravity_check, trigger_gravity_check_think);
+               toucher.trigger_gravity_check.nextthink = time;
+               toucher.trigger_gravity_check.count = 2;
+               if(toucher.gravity)
+                       g *= toucher.gravity;
        }
 
-       if (other.gravity != g)
+       if (toucher.gravity != g)
        {
-               other.gravity = g;
+               toucher.gravity = g;
                if(this.noise != "")
-                       _sound (other, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
+                       _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
                UpdateCSQCProjectile(this.owner);
        }
 }
diff --git a/qcsrc/common/triggers/trigger/gravity.qh b/qcsrc/common/triggers/trigger/gravity.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 41519e20cc622c4547bae54d7c226cd147f17e0c..e7b309062848d0fe673b8d797f4039512f292372 100644 (file)
@@ -1,25 +1,26 @@
+#include "heal.qh"
 #ifdef SVQC
 .float triggerhealtime;
-void trigger_heal_touch(entity this)
+void trigger_heal_touch(entity this, entity toucher)
 {
        if (this.active != ACTIVE_ACTIVE)
                return;
 
        // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
-       if (other.iscreature)
+       if (toucher.iscreature)
        {
-               if (other.takedamage)
-               if (!IS_DEAD(other))
-               if (other.triggerhealtime < time)
+               if (toucher.takedamage)
+               if (!IS_DEAD(toucher))
+               if (toucher.triggerhealtime < time)
                {
-                       EXACTTRIGGER_TOUCH;
-                       other.triggerhealtime = time + 1;
+                       EXACTTRIGGER_TOUCH(this, toucher);
+                       toucher.triggerhealtime = time + 1;
 
-                       if (other.health < this.max_health)
+                       if (toucher.health < this.max_health)
                        {
-                               other.health = min(other.health + this.health, this.max_health);
-                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
-                               _sound (other, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
+                               toucher.health = min(toucher.health + this.health, this.max_health);
+                               toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+                               _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
                        }
                }
        }
diff --git a/qcsrc/common/triggers/trigger/heal.qh b/qcsrc/common/triggers/trigger/heal.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 92a4dd0299e66d50607e4a1e3cf7675c673d2a09..d0ba4ebd19b252e925e425bb6cd2089475cffeff 100644 (file)
@@ -1,3 +1,4 @@
+#include "hurt.qh"
 #ifdef SVQC
 void trigger_hurt_use(entity this, entity actor, entity trigger)
 {
@@ -8,23 +9,23 @@ void trigger_hurt_use(entity this, entity actor, entity trigger)
 }
 
 .float triggerhurttime;
-void trigger_hurt_touch(entity this)
+void trigger_hurt_touch(entity this, entity toucher)
 {
        if (this.active != ACTIVE_ACTIVE)
                return;
 
        if(this.team)
-               if(((this.spawnflags & 4) == 0) == (this.team != other.team))
+               if(((this.spawnflags & 4) == 0) == (this.team != toucher.team))
                        return;
 
        // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
-       if (other.iscreature)
+       if (toucher.iscreature)
        {
-               if (other.takedamage)
-               if (other.triggerhurttime < time)
+               if (toucher.takedamage)
+               if (toucher.triggerhurttime < time)
                {
-                       EXACTTRIGGER_TOUCH;
-                       other.triggerhurttime = time + 1;
+                       EXACTTRIGGER_TOUCH(this, toucher);
+                       toucher.triggerhurttime = time + 1;
 
                        entity own;
                        own = this.enemy;
@@ -34,15 +35,15 @@ void trigger_hurt_touch(entity this)
                                this.enemy = NULL; // I still hate you all
                        }
 
-                       Damage (other, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       Damage (toucher, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, toucher.origin, '0 0 0');
                }
        }
-       else if(other.damagedbytriggers)
+       else if(toucher.damagedbytriggers)
        {
-               if(other.takedamage)
+               if(toucher.takedamage)
                {
-                       EXACTTRIGGER_TOUCH;
-                       Damage(other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0');
+                       EXACTTRIGGER_TOUCH(this, toucher);
+                       Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, toucher.origin, '0 0 0');
                }
        }
 
diff --git a/qcsrc/common/triggers/trigger/hurt.qh b/qcsrc/common/triggers/trigger/hurt.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 91381f4877c9954ccc54ca618640503cd32bd058..cb9c2d293558fad6829c7e888a08c9cfc63c0f40 100644 (file)
@@ -1,5 +1,6 @@
+#include "impulse.qh"
 // targeted (directional) mode
-void trigger_impulse_touch1(entity this)
+void trigger_impulse_touch1(entity this, entity toucher)
 {
        entity targ;
        float pushdeltatime;
@@ -8,24 +9,20 @@ void trigger_impulse_touch1(entity this)
        if (this.active != ACTIVE_ACTIVE)
                return;
 
-       if (!isPushable(other))
+       if (!isPushable(toucher))
                return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
        targ = find(NULL, targetname, this.target);
        if(!targ)
        {
                objerror(this, "trigger_force without a (valid) .target!\n");
-               remove(this);
+               delete(this);
                return;
        }
 
-#ifdef SVQC
-       str = min(this.radius, vlen(this.origin - other.origin));
-#elif defined(CSQC)
-       str = min(this.radius, vlen(this.origin - other.move_origin));
-#endif
+       str = min(this.radius, vlen(this.origin - toucher.origin));
 
        if(this.falloff == 1)
                str = (str / this.radius) * this.strength;
@@ -34,75 +31,58 @@ void trigger_impulse_touch1(entity this)
        else
                str = this.strength;
 
-       pushdeltatime = time - other.lastpushtime;
+       pushdeltatime = time - toucher.lastpushtime;
        if (pushdeltatime > 0.15) pushdeltatime = 0;
-       other.lastpushtime = time;
+       toucher.lastpushtime = time;
        if(!pushdeltatime) return;
 
        if(this.spawnflags & 64)
        {
-#ifdef SVQC
-               float addspeed = str - other.velocity * normalize(targ.origin - this.origin);
+               float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin);
                if (addspeed > 0)
                {
                        float accelspeed = min(8 * pushdeltatime * str, addspeed);
-                       other.velocity += accelspeed * normalize(targ.origin - this.origin);
+                       toucher.velocity += accelspeed * normalize(targ.origin - this.origin);
                }
-#elif defined(CSQC)
-               float addspeed = str - other.move_velocity * normalize(targ.origin - this.origin);
-               if (addspeed > 0)
-               {
-                       float accelspeed = min(8 * pushdeltatime * str, addspeed);
-                       other.move_velocity += accelspeed * normalize(targ.origin - this.origin);
-               }
-#endif
        }
        else
-#ifdef SVQC
-               other.velocity = other.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime;
-#elif defined(CSQC)
-               other.move_velocity = other.move_velocity + normalize(targ.origin - this.origin) * str * pushdeltatime;
-#endif
+               toucher.velocity = toucher.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime;
 
-#ifdef SVQC
-       UNSET_ONGROUND(other);
+       UNSET_ONGROUND(toucher);
 
-       UpdateCSQCProjectile(other);
-#elif defined(CSQC)
-       other.move_flags &= ~FL_ONGROUND;
+#ifdef SVQC
+       UpdateCSQCProjectile(toucher);
 #endif
 }
 
 // Directionless (accelerator/decelerator) mode
-void trigger_impulse_touch2(entity this)
+void trigger_impulse_touch2(entity this, entity toucher)
 {
        float pushdeltatime;
 
        if (this.active != ACTIVE_ACTIVE)
                return;
 
-       if (!isPushable(other))
+       if (!isPushable(toucher))
                return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
-       pushdeltatime = time - other.lastpushtime;
+       pushdeltatime = time - toucher.lastpushtime;
        if (pushdeltatime > 0.15) pushdeltatime = 0;
-       other.lastpushtime = time;
+       toucher.lastpushtime = time;
        if(!pushdeltatime) return;
 
        // div0: ticrate independent, 1 = identity (not 20)
-#ifdef SVQC
-       other.velocity = other.velocity * pow(this.strength, pushdeltatime);
+       toucher.velocity = toucher.velocity * pow(this.strength, pushdeltatime);
 
-       UpdateCSQCProjectile(other);
-#elif defined(CSQC)
-       other.move_velocity = other.move_velocity * pow(this.strength, pushdeltatime);
+#ifdef SVQC
+       UpdateCSQCProjectile(toucher);
 #endif
 }
 
 // Spherical (gravity/repulsor) mode
-void trigger_impulse_touch3(entity this)
+void trigger_impulse_touch3(entity this, entity toucher)
 {
        float pushdeltatime;
        float str;
@@ -110,23 +90,19 @@ void trigger_impulse_touch3(entity this)
        if (this.active != ACTIVE_ACTIVE)
                return;
 
-       if (!isPushable(other))
+       if (!isPushable(toucher))
                return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
-       pushdeltatime = time - other.lastpushtime;
+       pushdeltatime = time - toucher.lastpushtime;
        if (pushdeltatime > 0.15) pushdeltatime = 0;
-       other.lastpushtime = time;
+       toucher.lastpushtime = time;
        if(!pushdeltatime) return;
 
        setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius);
 
-#ifdef SVQC
-       str = min(this.radius, vlen(this.origin - other.origin));
-#elif defined(CSQC)
-       str = min(this.radius, vlen(this.origin - other.move_origin));
-#endif
+       str = min(this.radius, vlen(this.origin - toucher.origin));
 
        if(this.falloff == 1)
                str = (1 - str / this.radius) * this.strength; // 1 in the inside
@@ -135,12 +111,10 @@ void trigger_impulse_touch3(entity this)
        else
                str = this.strength;
 
-#ifdef SVQC
-       other.velocity = other.velocity + normalize(other.origin - this.origin) * str * pushdeltatime;
+       toucher.velocity = toucher.velocity + normalize(toucher.origin - this.origin) * str * pushdeltatime;
 
-       UpdateCSQCProjectile(other);
-#elif defined(CSQC)
-       other.move_velocity = other.move_velocity + normalize(other.move_origin - this.origin) * str * pushdeltatime;
+#ifdef SVQC
+       UpdateCSQCProjectile(toucher);
 #endif
 }
 
index 67d6361fbed2ea61c76c4bafb8e2952d6f06d485..a6961f5d2e7693920f56dac39df1d78011b88f91 100644 (file)
@@ -1,10 +1,7 @@
-#ifndef TRIGGER_IMPULSE_H
-#define TRIGGER_IMPULSE_H
+#pragma once
 
 // tZorks trigger impulse / gravity
 .float radius;
 .float falloff;
 .float strength;
 .float lastpushtime;
-
-#endif
index 63cdb019017254c0fdeba69e347dc8bc21d7256c..8aa6b2b1729244d8f548102d7d8ebd8b6c3a4a34 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGERS_TRIGGER_INCLUDE_H
-#define TRIGGERS_TRIGGER_INCLUDE_H
+#pragma once
 
 #include "multi.qh"
 #include "jumppads.qh"
@@ -8,5 +7,3 @@
 #include "keylock.qh"
 #include "impulse.qh"
 #include "viewloc.qh"
-
-#endif
index 8100f6b7972e0e7d01c1a3a59b1e1f0934c7c3f1..5a279561d374c5c6b0d975e3a4105b5e2f322787 100644 (file)
@@ -1,3 +1,4 @@
+#include "jumppads.qh"
 // TODO: split target_push and put it in the target folder
 #ifdef SVQC
 #include "jumppads.qh"
@@ -129,24 +130,14 @@ vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
        return sdir * vs + '0 0 1' * vz;
 }
 
-void trigger_push_touch(entity this)
+bool jumppad_push(entity this, entity targ)
 {
-       if (this.active == ACTIVE_NOT)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       if(this.team)
-               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, other)))
-                       return;
-
-       EXACTTRIGGER_TOUCH;
+       if (!isPushable(targ))
+               return false;
 
        if(this.enemy)
        {
-               other.velocity = trigger_push_calculatevelocity(other.origin, this.enemy, this.height);
-               other.move_velocity = other.velocity;
+               targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height);
        }
        else if(this.target && this.target != "")
        {
@@ -155,112 +146,121 @@ void trigger_push_touch(entity this)
                for(e = NULL; (e = find(e, targetname, this.target)); )
                {
                        if(e.cnt)
-                               RandomSelection_Add(e, 0, string_null, e.cnt, 1);
+                               RandomSelection_AddEnt(e, e.cnt, 1);
                        else
-                               RandomSelection_Add(e, 0, string_null, 1, 1);
+                               RandomSelection_AddEnt(e, 1, 1);
                }
-               other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, this.height);
-               other.move_velocity = other.velocity;
+               targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height);
        }
        else
        {
-               other.velocity = this.movedir;
-               other.move_velocity = other.velocity;
+               targ.velocity = this.movedir;
        }
 
-#ifdef SVQC
-       UNSET_ONGROUND(other);
-#elif defined(CSQC)
-       other.move_flags &= ~FL_ONGROUND;
+       UNSET_ONGROUND(targ);
 
-       if (other.flags & FL_PROJECTILE)
+#ifdef CSQC
+       if (targ.flags & FL_PROJECTILE)
        {
-               other.move_angles = vectoangles (other.move_velocity);
-               switch(other.move_movetype)
+               targ.angles = vectoangles (targ.velocity);
+               switch(targ.move_movetype)
                {
                        case MOVETYPE_FLY:
-                               other.move_movetype = MOVETYPE_TOSS;
-                               other.gravity = 1;
+                               set_movetype(targ, MOVETYPE_TOSS);
+                               targ.gravity = 1;
                                break;
                        case MOVETYPE_BOUNCEMISSILE:
-                               other.move_movetype = MOVETYPE_BOUNCE;
-                               other.gravity = 1;
+                               set_movetype(targ, MOVETYPE_BOUNCE);
+                               targ.gravity = 1;
                                break;
                }
        }
 #endif
 
 #ifdef SVQC
-       if (IS_PLAYER(other))
+       if (IS_PLAYER(targ))
        {
                // reset tracking of oldvelocity for impact damage (sudden velocity changes)
-               other.oldvelocity = other.velocity;
+               targ.oldvelocity = targ.velocity;
 
                if(this.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
                {
                        // flash when activated
-                       Send_Effect(EFFECT_JUMPPAD, other.origin, other.velocity, 1);
-                       _sound (other, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
+                       Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1);
+                       _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
                        this.pushltime = time + 0.2;
                }
-               if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other))
+               if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ))
                {
                        bool found = false;
-                       for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
-                               if(other.(jumppadsused[i]) == this)
+                       for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
+                               if(targ.(jumppadsused[i]) == this)
                                        found = true;
                        if(!found)
                        {
-                               other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = this;
-                               other.jumppadcount = other.jumppadcount + 1;
+                               targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this;
+                               targ.jumppadcount = targ.jumppadcount + 1;
                        }
 
-                       if(IS_REAL_CLIENT(other))
+                       if(IS_REAL_CLIENT(targ))
                        {
                                if(this.message)
-                                       centerprint(other, this.message);
+                                       centerprint(targ, this.message);
                        }
                        else
-                               other.lastteleporttime = time;
+                               targ.lastteleporttime = time;
 
-                       if (!IS_DEAD(other))
-                               animdecide_setaction(other, ANIMACTION_JUMP, true);
+                       if (!IS_DEAD(targ))
+                               animdecide_setaction(targ, ANIMACTION_JUMP, true);
                }
                else
-                       other.jumppadcount = true;
+                       targ.jumppadcount = true;
 
                // reset tracking of who pushed you into a hazard (for kill credit)
-               other.pushltime = 0;
-               other.istypefrag = 0;
+               targ.pushltime = 0;
+               targ.istypefrag = 0;
        }
 
        if(this.enemy.target)
-               SUB_UseTargets(this.enemy, other, other); // TODO: do we need other as trigger too?
+               SUB_UseTargets(this.enemy, targ, targ); // TODO: do we need targ as trigger too?
 
-       if (other.flags & FL_PROJECTILE)
+       if (targ.flags & FL_PROJECTILE)
        {
-               other.angles = vectoangles (other.velocity);
-               switch(other.movetype)
+               targ.angles = vectoangles (targ.velocity);
+               targ.com_phys_gravity_factor = 1;
+               switch(targ.move_movetype)
                {
                        case MOVETYPE_FLY:
-                               other.movetype = MOVETYPE_TOSS;
-                               other.gravity = 1;
+                               set_movetype(targ, MOVETYPE_TOSS);
+                               targ.gravity = 1;
                                break;
                        case MOVETYPE_BOUNCEMISSILE:
-                               other.movetype = MOVETYPE_BOUNCE;
-                               other.gravity = 1;
+                               set_movetype(targ, MOVETYPE_BOUNCE);
+                               targ.gravity = 1;
                                break;
                }
-               UpdateCSQCProjectile(other);
+               UpdateCSQCProjectile(targ);
        }
+#endif
 
-       /*if (other.flags & FL_ITEM)
-       {
-               ItemUpdate(other);
-               other.SendFlags |= ISF_DROP;
-       }*/
+       return true;
+}
+
+void trigger_push_touch(entity this, entity toucher)
+{
+       if (this.active == ACTIVE_NOT)
+               return;
+
+       if(this.team)
+               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
+                       return;
+
+       EXACTTRIGGER_TOUCH(this, toucher);
 
-       if (this.spawnflags & PUSH_ONCE)
+       noref bool success = jumppad_push(this, toucher);
+
+#ifdef SVQC
+       if (success && (this.spawnflags & PUSH_ONCE))
        {
                settouch(this, func_null);
                setthink(this, SUB_Remove);
@@ -275,28 +275,25 @@ void trigger_push_updatelink(entity this);
 #endif
 void trigger_push_findtarget(entity this)
 {
-       entity t;
-       vector org;
-
        // first calculate a typical start point for the jump
-       org = (this.absmin + this.absmax) * 0.5;
-       org_z = this.absmax.z - STAT(PL_MIN, NULL).z;
+       vector org = (this.absmin + this.absmax) * 0.5;
+       org.z = this.absmax.z - PL_MIN_CONST.z;
 
        if (this.target)
        {
-               float n = 0;
-               for(t = NULL; (t = find(t, targetname, this.target)); )
+               int n = 0;
+               for(entity t = NULL; (t = find(t, targetname, this.target)); )
                {
                        ++n;
 #ifdef SVQC
                        entity e = spawn();
                        setorigin(e, org);
-                       setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+                       setsize(e, PL_MIN_CONST, PL_MAX_CONST);
                        e.velocity = trigger_push_calculatevelocity(org, t, this.height);
                        tracetoss(e, e);
-                       if(e.movetype == MOVETYPE_NONE)
+                       if(e.move_movetype == MOVETYPE_NONE)
                                waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
-                       remove(e);
+                       delete(e);
 #endif
                }
 
@@ -324,14 +321,13 @@ void trigger_push_findtarget(entity this)
        {
                entity e = spawn();
                setorigin(e, org);
-               setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+               setsize(e, PL_MIN_CONST, PL_MAX_CONST);
                e.velocity = this.movedir;
                tracetoss(e, e);
                waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
-               remove(e);
+               delete(e);
        }
 
-       trigger_push_link(this);
        defer(this, 0.1, trigger_push_updatelink);
 #endif
 }
@@ -396,6 +392,8 @@ spawnfunc(trigger_push)
                this.noise = "misc/jumppad.wav";
        precache_sound (this.noise);
 
+       trigger_push_link(this); // link it now
+
        // this must be called to spawn the teleport waypoints for bots
        InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET);
 }
@@ -418,6 +416,11 @@ bool target_push_send(entity this, entity to, float sf)
        return true;
 }
 
+void target_push_use(entity this, entity actor, entity trigger)
+{
+       jumppad_push(this, actor);
+}
+
 void target_push_link(entity this)
 {
        BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
@@ -432,7 +435,18 @@ void target_push_init(entity this)
        target_push_link(this);
 }
 
-spawnfunc(target_push) { target_push_init(this); }
+void target_push_init2(entity this)
+{
+       if(this.target && this.target != "") // we have an old style pusher!
+       {
+               InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET);
+               this.use = target_push_use;
+       }
+
+       target_push_init(this); // normal push target behaviour can be combined with a legacy pusher?
+}
+
+spawnfunc(target_push) { target_push_init2(this); }
 spawnfunc(info_notnull) { target_push_init(this); }
 spawnfunc(target_position) { target_push_init(this); }
 
index 4e8bf1809fd24840637abeb5a3aa27e31109bd41..76d244da55b2d0b8e814b1d3574b53f293c01ff6 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_JUMPPADS_H
-#define T_JUMPPADS_H
+#pragma once
 
 const float PUSH_ONCE          = 1;
 const float PUSH_SILENT                = 2;
@@ -35,7 +34,7 @@ void trigger_push_use(entity this, entity actor, entity trigger);
 
 vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
 
-void trigger_push_touch(entity this);
+void trigger_push_touch(entity this, entity toucher);
 
 .vector dest;
 void trigger_push_findtarget(entity this);
@@ -59,4 +58,3 @@ spawnfunc(target_push);
 spawnfunc(info_notnull);
 spawnfunc(target_position);
 #endif
-#endif
index 133397876857999c1eecad47467b8a09fa86e8e1..bf20d1e9732e4dfe133ed4fc5473ae50b5021bd9 100644 (file)
@@ -1,3 +1,4 @@
+#include "keylock.qh"
 /**
  * trigger given targets
  */
@@ -15,21 +16,21 @@ void trigger_keylock_kill(string s)
 {
        entity t;
        for(t = NULL; (t = find(t, targetname, s)); )
-               remove(t);
+               delete(t);
 }
 
-void trigger_keylock_touch(entity this)
+void trigger_keylock_touch(entity this, entity toucher)
 {
        bool key_used = false;
        bool started_delay = false;
 
        // only player may trigger the lock
-       if(!IS_PLAYER(other))
+       if(!IS_PLAYER(toucher))
                return;
 
        // check silver key
        if(this.itemkeys)
-               key_used = item_keys_usekey(this, other);
+               key_used = item_keys_usekey(this, toucher);
 
        if(this.itemkeys)
        {
@@ -38,16 +39,16 @@ void trigger_keylock_touch(entity this)
                if(key_used)
                {
                        // one or more keys were given, but others are still missing!
-                       play2(other, this.noise1);
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys));
-                       other.key_door_messagetime = time + 2;
+                       play2(toucher, this.noise1);
+                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys));
+                       toucher.key_door_messagetime = time + 2;
                }
-               else if(other.key_door_messagetime <= time)
+               else if(toucher.key_door_messagetime <= time)
                {
                        // no keys were given
-                       play2(other, this.noise2);
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys));
-                       other.key_door_messagetime = time + 2;
+                       play2(toucher, this.noise2);
+                       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys));
+                       toucher.key_door_messagetime = time + 2;
                }
 #endif
 
@@ -55,7 +56,7 @@ void trigger_keylock_touch(entity this)
                if(this.delay <= time || started_delay == true)
                if(this.target2)
                {
-                       trigger_keylock_trigger(this, other, this.target2);
+                       trigger_keylock_trigger(this, toucher, this.target2);
                        started_delay = true;
                        this.delay = time + this.wait;
                }
@@ -64,17 +65,17 @@ void trigger_keylock_touch(entity this)
        {
 #ifdef SVQC
                // all keys were given!
-               play2(other, this.noise);
-               centerprint(other, this.message);
+               play2(toucher, this.noise);
+               centerprint(toucher, this.message);
 #endif
 
                if(this.target)
-                       trigger_keylock_trigger(this, other, this.target);
+                       trigger_keylock_trigger(this, toucher, this.target);
 
                if(this.killtarget)
                        trigger_keylock_kill(this.killtarget);
 
-               remove(this);
+               delete(this);
        }
 
 }
@@ -121,7 +122,7 @@ message2 and noise2 will be resent to the player every 2 seconds while he is in
 */
 spawnfunc(trigger_keylock)
 {
-       if(!this.itemkeys) { remove(this); return; }
+       if(!this.itemkeys) { delete(this); return; }
 
        // set unlocked message
        if(this.message == "")
index 53fda30e4285bfb27d74113dbe343288a40248da..065d8c932ac1ffdd04c2cf0b1054cf231877b6d4 100644 (file)
@@ -1,3 +1,4 @@
+#include "magicear.qh"
 #ifdef SVQC
 float magicear_matched;
 float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo);
diff --git a/qcsrc/common/triggers/trigger/magicear.qh b/qcsrc/common/triggers/trigger/magicear.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 018e20884b25938e7d17fba6fc0764948c2aad1d..a67baca16a2f152d69877823eee144bfd076724a 100644 (file)
@@ -1,3 +1,4 @@
+#include "monoflop.qh"
 #ifdef SVQC
 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
diff --git a/qcsrc/common/triggers/trigger/monoflop.qh b/qcsrc/common/triggers/trigger/monoflop.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 14d7fda04b7fbcd4420eee464cda96613497239a..7aa13c13ebfc2aec22b8d7a7382607877cd0d5e2 100644 (file)
@@ -1,3 +1,4 @@
+#include "multi.qh"
 // NOTE: also contains trigger_once at bottom
 
 #ifdef SVQC
@@ -66,34 +67,34 @@ void multi_use(entity this, entity actor, entity trigger)
        multi_trigger(this);
 }
 
-void multi_touch(entity this)
+void multi_touch(entity this, entity toucher)
 {
        if(!(this.spawnflags & 2))
-       if(!other.iscreature)
+       if(!toucher.iscreature)
                        return;
 
        if(this.team)
-               if(((this.spawnflags & 4) == 0) == (this.team != other.team))
+               if(((this.spawnflags & 4) == 0) == (this.team != toucher.team))
                        return;
 
 // if the trigger has an angles field, check player's facing direction
        if (this.movedir != '0 0 0')
        {
-               makevectors (other.angles);
+               makevectors (toucher.angles);
                if (v_forward * this.movedir < 0)
                        return;         // not facing the right way
        }
 
        // if the trigger has pressed keys, check that the player is pressing those keys
        if(this.pressedkeys)
-       if(IS_PLAYER(other)) // only for players
-       if(!(other.pressedkeys & this.pressedkeys))
+       if(IS_PLAYER(toucher)) // only for players
+       if(!(toucher.pressedkeys & this.pressedkeys))
                return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
-       this.enemy = other;
-       this.goalentity = other;
+       this.enemy = toucher;
+       this.goalentity = toucher;
        multi_trigger(this);
 }
 
index 1a1850537d4f29cd7bdde118395ea007e1d1716d..d946efe5f17cc533c2b104b52fb1dc2c2e37ba82 100644 (file)
@@ -1,3 +1,4 @@
+#include "multivibrator.qh"
 #ifdef SVQC
 void multivibrator_send(entity this)
 {
diff --git a/qcsrc/common/triggers/trigger/multivibrator.qh b/qcsrc/common/triggers/trigger/multivibrator.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 1df446ba32ee4345a936fc0512c98eb5aa191a65..794f4dc112d017a4e9759dbe84095d628662602e 100644 (file)
@@ -1,3 +1,4 @@
+#include "relay.qh"
 #ifdef SVQC
 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
 This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
diff --git a/qcsrc/common/triggers/trigger/relay.qh b/qcsrc/common/triggers/trigger/relay.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index bbb49d0e5c29a3aafe79595510a684666fd25a27..d713a05837728245f18a604f96647b5a300d40ca 100644 (file)
@@ -1,3 +1,4 @@
+#include "relay_activators.qh"
 #ifdef SVQC
 void relay_activators_use(entity this, entity actor, entity trigger)
 {
diff --git a/qcsrc/common/triggers/trigger/relay_activators.qh b/qcsrc/common/triggers/trigger/relay_activators.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index ea90a06cb70d19865b24365bf582ebfbcadcfaee..728252c70400972a4259b3f49eea02c5100027d2 100644 (file)
@@ -1,3 +1,4 @@
+#include "relay_if.qh"
 #ifdef SVQC
 void trigger_relay_if_use(entity this, entity actor, entity trigger)
 {
diff --git a/qcsrc/common/triggers/trigger/relay_if.qh b/qcsrc/common/triggers/trigger/relay_if.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 2972d32bbf79b3e23fa6da66e4e9569e982a6824..4f9dab7fd1e500a4228297b82ba9e6dab1be6dc6 100644 (file)
@@ -1,3 +1,4 @@
+#include "relay_teamcheck.qh"
 #ifdef SVQC
 void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger)
 {
diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qh b/qcsrc/common/triggers/trigger/relay_teamcheck.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index bf4178beae33396b3742a6bedabe5f950cc0d21d..e6e35c295a9f8d723285699de7db0972e850ea9c 100644 (file)
@@ -1,9 +1,9 @@
+#include "secret.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include <common/util.qh>
     #include <server/defs.qh>
-    #include "secret.qh"
 #endif
 
 #ifdef SVQC
@@ -17,10 +17,10 @@ void secrets_setstatus(entity this)
 /**
  * A secret has been found (maybe :P)
  */
-void trigger_secret_touch(entity this)
+void trigger_secret_touch(entity this, entity toucher)
 {
        // only a player can trigger this
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
 
        // update secrets found counter
@@ -29,12 +29,12 @@ void trigger_secret_touch(entity this)
        //print(ftos(secret_counter.count), "\n");
 
        // centerprint message (multi_touch() doesn't always call centerprint())
-       centerprint(other, this.message);
+       centerprint(toucher, this.message);
        this.message = "";
 
        // handle normal trigger features
-       multi_touch(this);
-       remove(this);
+       multi_touch(this, toucher);
+       delete(this);
 }
 
 /*QUAKED trigger_secret (.5 .5 .5) ?
index c2a155ce72e0114e790e092d56dceacca0f25c74..e483316a4086da58721cea32794b1d5f5de3453c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SECRET_H
-#define SECRET_H
+#pragma once
 #ifdef SVQC
 
 /**
@@ -21,4 +20,3 @@ float secrets_found;
  */
 void secrets_setstatus(entity this);
 #endif
-#endif
index fb792b94bb926bdec1cd410746dd62fe94837471..71c5247c7446c5332d12e62b7f9084e105e47bd9 100644 (file)
@@ -1,8 +1,9 @@
+#include "swamp.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include <lib/warpzone/util_server.qh>
-    #include <common/weapons/all.qh>
+    #include <common/weapons/_all.qh>
     #include <server/defs.qh>
     #include <common/deathtypes/all.qh>
 #endif
@@ -22,7 +23,7 @@
 #ifdef SVQC
 spawnfunc(trigger_swamp);
 #endif
-void swamp_touch(entity this);
+void swamp_touch(entity this, entity toucher);
 void swampslug_think(entity this);
 
 
@@ -45,7 +46,7 @@ void swampslug_think(entity this)
        if(this.health <= 0)
        {
                this.owner.in_swamp = 0;
-               remove(this);
+               delete(this);
                //centerprint(this.owner,"Killing slug...\n");
                return;
        }
@@ -54,42 +55,42 @@ void swampslug_think(entity this)
        // Or we have exited it very recently.
        // Do the damage and renew the timer.
 #ifdef SVQC
-       Damage (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, other.origin, '0 0 0');
+       Damage (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, this.owner.origin, '0 0 0');
 #endif
 
        this.nextthink = time + this.swamp_interval;
 }
 
-void swamp_touch(entity this)
+void swamp_touch(entity this, entity toucher)
 {
        // If whatever thats touching the swamp is not a player
        // or if its a dead player, just dont care abt it.
-       if(!IS_PLAYER(other) || IS_DEAD(other))
+       if(!IS_PLAYER(toucher) || IS_DEAD(toucher))
                return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
        // Chech if player alredy got a swampslug.
-       if(other.in_swamp != 1)
+       if(toucher.in_swamp != 1)
        {
                // If not attach one.
-               //centerprint(other,"Entering swamp!\n");
-               other.swampslug = spawn();
-               other.swampslug.health = 2;
-               setthink(other.swampslug, swampslug_think);
-               other.swampslug.nextthink = time;
-               other.swampslug.owner = other;
-               other.swampslug.dmg = this.dmg;
-               other.swampslug.swamp_interval = this.swamp_interval;
-               other.swamp_slowdown = this.swamp_slowdown;
-               other.in_swamp = 1;
+               //centerprint(toucher,"Entering swamp!\n");
+               toucher.swampslug = spawn();
+               toucher.swampslug.health = 2;
+               setthink(toucher.swampslug, swampslug_think);
+               toucher.swampslug.nextthink = time;
+               toucher.swampslug.owner = toucher;
+               toucher.swampslug.dmg = this.dmg;
+               toucher.swampslug.swamp_interval = this.swamp_interval;
+               toucher.swamp_slowdown = this.swamp_slowdown;
+               toucher.in_swamp = 1;
                return;
        }
 
-       //other.in_swamp = 1;
+       //toucher.in_swamp = 1;
 
        //Revitalize players swampslug
-       other.swampslug.health = 2;
+       toucher.swampslug.health = 2;
 }
 
 REGISTER_NET_LINKED(ENT_CLIENT_SWAMP)
index 7ae50bac42a9b7d4bbc7fc6ba5582b6fd2fd4993..f4df98378d544659b1c22f0c663919fb95f90c17 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGER_SWAMP_H
-#define TRIGGER_SWAMP_H
+#pragma once
 
 .float swamp_interval; //Hurt players in swamp with this interval
 .float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
@@ -7,5 +6,3 @@
 
 .float in_swamp;              // bool
 .entity swampslug;            // Uses this to release from swamp ("untouch" fix)
-
-#endif
index 8ee6b7a69727e091f5a83459b4309b6e9172686f..c3de654609ff5e0db36ad1bf01e4b314b3283023 100644 (file)
@@ -1,3 +1,4 @@
+#include "teleport.qh"
 REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT)
 
 #ifdef SVQC
@@ -11,49 +12,49 @@ void trigger_teleport_use(entity this, entity actor, entity trigger)
 }
 #endif
 
-void Teleport_Touch (entity this)
+void Teleport_Touch(entity this, entity toucher)
 {
        if (this.active != ACTIVE_ACTIVE)
                return;
 
 #ifdef SVQC
-       if (!other.teleportable)
+       if (!toucher.teleportable)
                return;
 
-       if(other.vehicle)
-       if(!other.vehicle.teleportable)
+       if(toucher.vehicle)
+       if(!toucher.vehicle.teleportable)
                return;
 
-       if(IS_TURRET(other))
+       if(IS_TURRET(toucher))
                return;
 #elif defined(CSQC)
-       if(!IS_PLAYER(other))
+       if(!IS_PLAYER(toucher))
                return;
 #endif
 
-       if(IS_DEAD(other))
+       if(IS_DEAD(toucher))
                return;
 
        if(this.team)
-               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, other)))
+               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
                        return;
 
-       EXACTTRIGGER_TOUCH;
+       EXACTTRIGGER_TOUCH(this, toucher);
 
 #ifdef SVQC
-       if(IS_PLAYER(other))
-               RemoveGrapplingHook(other);
+       if(IS_PLAYER(toucher))
+               RemoveGrapplingHook(toucher);
 #endif
 
        entity e;
-       e = Simple_TeleportPlayer(this, other);
+       e = Simple_TeleportPlayer(this, toucher);
 
 #ifdef SVQC
        string s = this.target; this.target = string_null;
-       SUB_UseTargets(this, other, other); // TODO: should we be using other for trigger too?
+       SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for trigger too?
        if (!this.target) this.target = s;
 
-       SUB_UseTargets(e, other, other);
+       SUB_UseTargets(e, toucher, toucher);
 #endif
 }
 
@@ -98,6 +99,8 @@ spawnfunc(trigger_teleport)
                return;
        }
 
+       IL_PUSH(g_teleporters, this);
+
        this.teleport_next = teleport_first;
        teleport_first = this;
 }
@@ -105,6 +108,8 @@ spawnfunc(trigger_teleport)
 NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew)
 {
        this.classname = "trigger_teleport";
+       if(isnew)
+               IL_PUSH(g_teleporters, this);
        int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; }
        this.spawnflags = ReadInt24_t();
        this.active = ReadByte();
@@ -114,7 +119,7 @@ NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew)
 
        this.entremove = trigger_remove_generic;
        this.solid = SOLID_TRIGGER;
-       //this.move_touch = trigger_push_touch;
+       //settouch(this, trigger_push_touch);
        this.move_time = time;
        defer(this, 0.25, teleport_findtarget);
 
diff --git a/qcsrc/common/triggers/trigger/teleport.qh b/qcsrc/common/triggers/trigger/teleport.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 9d675bbc734dbba6ad08ea4a549b005297b779cc..a600263542afab2266461d91a2f350489b4344b9 100644 (file)
@@ -1,3 +1,4 @@
+#include "viewloc.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
@@ -71,7 +72,7 @@ void viewloc_init(entity this)
                        break;
                }
 
-       if(!this.enemy) { LOG_INFO("^1FAIL!\n"); remove(this); return; }
+       if(!this.enemy) { LOG_INFO("^1FAIL!\n"); delete(this); return; }
 
        if(!this.goalentity)
                this.goalentity = this.enemy; // make them match so CSQC knows what to do
@@ -85,7 +86,7 @@ void viewloc_init(entity this)
 spawnfunc(trigger_viewlocation)
 {
        // we won't check target2 here yet, as it may not even need to exist
-       if(this.target == "") { LOG_INFO("^1FAIL!\n"); remove(this); return; }
+       if(this.target == "") { LOG_INFO("^1FAIL!\n"); delete(this); return; }
 
        EXACTTRIGGER_INIT;
        InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET);
@@ -101,9 +102,9 @@ bool viewloc_send(entity this, entity to, int sf)
        WriteCoord(MSG_ENTITY, this.origin_y);
        WriteCoord(MSG_ENTITY, this.origin_z);
 
-       WriteCoord(MSG_ENTITY, this.angles_x);
-       WriteCoord(MSG_ENTITY, this.angles_y);
-       WriteCoord(MSG_ENTITY, this.angles_z);
+       WriteAngle(MSG_ENTITY, this.angles_x);
+       WriteAngle(MSG_ENTITY, this.angles_y);
+       WriteAngle(MSG_ENTITY, this.angles_z);
 
        return true;
 }
@@ -175,9 +176,9 @@ NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew)
        this.origin_z = ReadCoord();
        setorigin(this, this.origin);
 
-       this.movedir_x = ReadCoord();
-       this.movedir_y = ReadCoord();
-       this.movedir_z = ReadCoord();
+       this.movedir_x = ReadAngle();
+       this.movedir_y = ReadAngle();
+       this.movedir_z = ReadAngle();
 
        return = true;
 
index 881197a3b82fb57b7c96bc4f16ed3c14fd527b04..167fc218f7707c5f00b2f70d612d654f21ee1999 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef T_VIEWLOC_H
-#define T_VIEWLOC_H
+#pragma once
 
 .entity viewloc;
 
@@ -8,5 +7,3 @@
 .entity enemy;
 .vector movedir;
 #endif
-
-#endif
index a9050357f6e97b5bf7851968239bb266404b5f57..1f5139896d19eea55fa89b5c5d8085a9f2b37e9f 100644 (file)
@@ -1,3 +1,4 @@
+#include "triggers.qh"
 void SUB_DontUseTargets(entity this, entity actor, entity trigger) { }
 
 void SUB_UseTargets(entity this, entity actor, entity trigger);
@@ -5,7 +6,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger);
 void DelayThink(entity this)
 {
        SUB_UseTargets (this, this.enemy, NULL);
-       remove(this);
+       delete(this);
 }
 
 void FixSize(entity e)
@@ -163,6 +164,7 @@ void trigger_remove_generic(entity this)
 }
 #endif
 
+
 /*
 ==============================
 SUB_UseTargets
@@ -182,7 +184,8 @@ match (string)this.target and call their .use function
 
 ==============================
 */
-void SUB_UseTargets(entity this, entity actor, entity trigger)
+
+void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse)
 {
 //
 // check for a delay
@@ -200,6 +203,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger)
                t.target2 = this.target2;
                t.target3 = this.target3;
                t.target4 = this.target4;
+               t.antiwall_flag = this.antiwall_flag;
                return;
        }
 
@@ -225,7 +229,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger)
        if (s != "")
        {
                for(entity t = NULL; (t = find(t, targetname, s)); )
-                       remove(t);
+                       delete(t);
        }
 #endif
 
@@ -253,11 +257,11 @@ void SUB_UseTargets(entity this, entity actor, entity trigger)
                        int aw_flag = this.antiwall_flag;
                        for(entity t = NULL; (t = find(t, targetname, s)); )
                        {
-                               if(t.use)
+                               if(t.use && (t.sub_target_used != time || !preventReuse))
                                {
                                        if(this.target_random)
                                        {
-                                               RandomSelection_Add(t, 0, string_null, 1, 0);
+                                               RandomSelection_AddEnt(t, 1, 0);
                                        }
                                        else
                                        {
@@ -265,6 +269,8 @@ void SUB_UseTargets(entity this, entity actor, entity trigger)
                                                        t.antiwall_flag = aw_flag;
 
                                                t.use(t, actor, this);
+                                               if(preventReuse)
+                                                       t.sub_target_used = time;
                                        }
                                }
                        }
@@ -272,9 +278,16 @@ void SUB_UseTargets(entity this, entity actor, entity trigger)
        }
 
        if(this.target_random && RandomSelection_chosen_ent)
+       {
                RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
+               if(preventReuse)
+                       RandomSelection_chosen_ent.sub_target_used = time;
+       }
 }
 
+void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); }
+void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); }
+
 void SUB_UseTargets_self(entity this)
 {
        SUB_UseTargets(this, NULL, NULL);
index 82c0916c19228c2dd6605b24a6f503d6b3c07061..2b8274f4b890aa473b5432de2415bb1719e925a1 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TRIGGERS_H
-#define TRIGGERS_H
+#pragma once
 
 const float SF_TRIGGER_INIT = 1;
 const float SF_TRIGGER_UPDATE = 2;
@@ -28,8 +27,12 @@ string trigger_magicear_processmessage_forallears(entity source, float teamsay,
 
 void target_voicescript_next(entity pl);
 void target_voicescript_clear(entity pl);
+
+void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger);
 #endif
 
+.float sub_target_used;
+
 .float volume, atten;
 
 .vector dest;
@@ -50,5 +53,3 @@ const int ACTIVE_IDLE                 = 2;
 const int ACTIVE_BUSY          = 2;
 const int ACTIVE_TOGGLE                = 3;
 #endif
-
-#endif
diff --git a/qcsrc/common/turrets/_all.inc b/qcsrc/common/turrets/_all.inc
new file mode 100644 (file)
index 0000000..8bc63f7
--- /dev/null
@@ -0,0 +1,2 @@
+#include "_all.qh"
+#include "_mod.inc"
diff --git a/qcsrc/common/turrets/_all.qh b/qcsrc/common/turrets/_all.qh
new file mode 100644 (file)
index 0000000..947026d
--- /dev/null
@@ -0,0 +1,2 @@
+#pragma once
+#include "_mod.qh"
index 40c3114bab5983451be09892c943a8c4ba83170d..8fff9535c5ce40ca345d9175a859b509dd053218 100644 (file)
@@ -1,8 +1,13 @@
 // generated file; do not modify
 #include <common/turrets/all.qc>
 #include <common/turrets/checkpoint.qc>
-#include <common/turrets/cl_turrets.qc>
 #include <common/turrets/config.qc>
-#include <common/turrets/sv_turrets.qc>
 #include <common/turrets/targettrigger.qc>
+#include <common/turrets/turrets.qc>
+#ifdef CSQC
+    #include <common/turrets/cl_turrets.qc>
+#endif
+#ifdef SVQC
+    #include <common/turrets/sv_turrets.qc>
+#endif
 #include <common/turrets/util.qc>
index 6da539e8522ac3ac979eb28aafc8861747cc4780..06978f1d41e23fa375a94ab19463287553d0736c 100644 (file)
@@ -1,8 +1,13 @@
 // generated file; do not modify
 #include <common/turrets/all.qh>
 #include <common/turrets/checkpoint.qh>
-#include <common/turrets/cl_turrets.qh>
 #include <common/turrets/config.qh>
-#include <common/turrets/sv_turrets.qh>
 #include <common/turrets/targettrigger.qh>
+#include <common/turrets/turrets.qh>
+#ifdef CSQC
+    #include <common/turrets/cl_turrets.qh>
+#endif
+#ifdef SVQC
+    #include <common/turrets/sv_turrets.qh>
+#endif
 #include <common/turrets/util.qh>
index a993b9218a22cf77ae2df4f80870d2115b3b9fde..1a77e989121b0cade1ab49e584f93e6e575b29d4 100644 (file)
@@ -1,7 +1,6 @@
-#ifndef TURRETS_ALL_H
-#define TURRETS_ALL_H
+#pragma once
 
-#include <common/command/all.qh>
+#include <common/command/_mod.qh>
 #include "config.qh"
 
 #include "turret.qh"
@@ -75,5 +74,3 @@ const int TUR_FIRST = 1;
 REGISTER_TURRET(Null, NEW(Turret));
 
 #include "turret/_mod.inc"
-
-#endif
index dc48fd1156bacf3c114c23dd04740ee5726d4921..6c246a75b83a5c8b6229f475c32c82bd173f1556 100644 (file)
@@ -1,3 +1,4 @@
+#include "checkpoint.qh"
 /**
     turret_checkpoint
 **/
@@ -56,7 +57,7 @@ void turret_checkpoint_init(entity this)
     {
         this.enemy = find(NULL, targetname, this.target);
         if(this.enemy == NULL)
-            LOG_TRACE("A turret_checkpoint faild to find its target!\n");
+            LOG_TRACE("A turret_checkpoint faild to find its target!");
     }
     //setthink(this, turret_checkpoint_think);
     //this.nextthink = time + tc_acum + 0.25;
diff --git a/qcsrc/common/turrets/checkpoint.qh b/qcsrc/common/turrets/checkpoint.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 55719cb3f59eb38fe30ef856de28f90dfe568b8f..c21e327d4470655336e6033d1823587c65f6e458 100644 (file)
@@ -1,6 +1,7 @@
+#include "cl_turrets.qh"
 void turret_remove(entity this)
 {
-       remove(this.tur_head);
+       delete(this.tur_head);
        //remove(this.enemy);
        this.tur_head = NULL;
 }
@@ -34,7 +35,7 @@ void turret_draw(entity this)
        if(dt <= 0)
                return;
 
-       this.tur_head.angles += dt * this.tur_head.move_avelocity;
+       this.tur_head.angles += dt * this.tur_head.avelocity;
 
        if (this.health < 127)
        {
@@ -155,17 +156,14 @@ void turret_draw2d(entity this)
 
        o_z = 0;
 
-       float edgedistance_min, crosshairdistance;
-               edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
+       float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
        (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
        (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
        (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
 
-       float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
+       float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
 
-       crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
-
-       t = waypointsprite_scale * vidscale;
+       t = waypointsprite_scale;
        a *= waypointsprite_alpha;
 
        {
@@ -202,7 +200,7 @@ void turret_draw2d(entity this)
                        );
 }
 
-void turret_construct(entity this)
+void turret_construct(entity this, bool isnew)
 {
        entity tur = get_turretinfo(this.m_id);
 
@@ -224,14 +222,14 @@ void turret_construct(entity this)
 
        this.tur_head.classname                 = "turret_head";
        this.tur_head.owner                             = this;
-       this.tur_head.move_movetype             = MOVETYPE_NOCLIP;
-       this.move_movetype                              = MOVETYPE_NOCLIP;
+       set_movetype(this.tur_head, MOVETYPE_NOCLIP);
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.tur_head.angles                    = this.angles;
        this.health                                             = 255;
        this.solid                                              = SOLID_BBOX;
        this.tur_head.solid                             = SOLID_NOT;
-       this.movetype                                   = MOVETYPE_NOCLIP;
-       this.tur_head.movetype                  = MOVETYPE_NOCLIP;
+       set_movetype(this, MOVETYPE_NOCLIP);
+       set_movetype(this.tur_head, MOVETYPE_NOCLIP);
        this.draw                                               = turret_draw;
        this.entremove                                  = turret_remove;
        this.drawmask                                   = MASK_NORMAL;
@@ -242,6 +240,12 @@ void turret_construct(entity this)
        this.teamradar_color = '1 0 0';
        this.alpha = 1;
 
+       if(isnew)
+       {
+               IL_PUSH(g_drawables, this);
+               IL_PUSH(g_drawables_2d, this);
+       }
+
        tur.tr_setup(tur, this);
 }
 
@@ -258,14 +262,14 @@ void turret_gib_draw(entity this)
                if(time >= this.nextthink)
                {
                        turret_gibboom(this);
-                       remove(this);
+                       delete(this);
                }
        }
        else
        {
                this.alpha = bound(0, this.nextthink - time, 1);
                if(this.alpha < ALPHA_MIN_VISIBLE)
-                       remove(this);
+                       delete(this);
        }
 }
 
@@ -303,11 +307,10 @@ entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, flo
                gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
 
        gib.gravity              = 1;
-       gib.move_movetype   = MOVETYPE_BOUNCE;
-       gib.move_origin  = _from;
+       set_movetype(gib, MOVETYPE_BOUNCE);
        setorigin(gib,          _from);
-       gib.move_velocity   = _to;
-       gib.move_avelocity  = prandomvec() * 32;
+       gib.velocity   = _to;
+       gib.avelocity  = prandomvec() * 32;
        gib.move_time      = time;
        gib.damageforcescale = 1;
 
@@ -341,9 +344,9 @@ void turret_die(entity this)
                        entity headgib = turret_gibtoss((get_turretinfo(this.m_id)).head_model, this.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true);
                        if(headgib)
                        {
-                               headgib.angles = headgib.move_angles = this.tur_head.angles;
-                               headgib.avelocity = headgib.move_avelocity = this.tur_head.move_avelocity + randomvec() * 45;
-                               headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5;
+                               headgib.angles = this.tur_head.angles;
+                               headgib.avelocity = this.tur_head.avelocity + randomvec() * 45;
+                               headgib.avelocity_y = headgib.avelocity_y * 5;
                                headgib.gravity = 0.5;
                        }
                }
@@ -370,7 +373,7 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
                this.angles_x = ReadAngle();
                this.angles_y = ReadAngle();
 
-               turret_construct(this);
+               turret_construct(this, isnew);
                this.colormap = 1024;
                this.glowmod = '0 1 1';
                this.tur_head.colormap = this.colormap;
@@ -382,10 +385,9 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
                if(this.tur_head == NULL) // aparenly this can happpen before TNSF_SETUP. great.
                        this.tur_head = spawn();
 
-               this.tur_head.move_angles_x = ReadShort();
-               this.tur_head.move_angles_y = ReadShort();
-               //this.tur_head.angles = this.angles + this.tur_head.move_angles;
-               this.tur_head.angles = this.tur_head.move_angles;
+               this.tur_head.angles_x = ReadShort();
+               this.tur_head.angles_y = ReadShort();
+               //this.tur_head.angles = this.angles + this.tur_head.angles;
        }
 
        if(sf & TNSF_AVEL)
@@ -393,8 +395,8 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
                if(this.tur_head == NULL) // aparenly this can happpen before TNSF_SETUP. great.
                        this.tur_head = spawn();
 
-               this.tur_head.move_avelocity_x = ReadShort();
-               this.tur_head.move_avelocity_y = ReadShort();
+               this.tur_head.avelocity_x = ReadShort();
+               this.tur_head.avelocity_y = ReadShort();
        }
 
        if(sf & TNSF_MOVE)
@@ -408,11 +410,9 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
                this.velocity_y = ReadShort();
                this.velocity_z = ReadShort();
 
-               this.move_angles_y = ReadShort();
+               this.angles_y = ReadShort();
 
                this.move_time   = time;
-               this.move_velocity = this.velocity;
-               this.move_origin   = this.origin;
        }
 
        if(sf & TNSF_ANIM)
diff --git a/qcsrc/common/turrets/cl_turrets.qh b/qcsrc/common/turrets/cl_turrets.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index f5989b48d157221c0e1fea3b629e3ee8911b6c4f..68cac084649dba3baa49ae258a438d25313d5444 100644 (file)
@@ -1,3 +1,4 @@
+#include "config.qh"
 // ==========================
 //  Turret Config Generator
 // ==========================
@@ -27,12 +28,12 @@ float T_Config_Queue_Compare(float root, float child, entity pass)
 
 void Dump_Turret_Settings()
 {
-       float x, totalsettings = 0;
+       int totalsettings = 0;
        FOREACH(Turrets, it != TUR_Null, {
                // step 1: clear the queue
                TUR_CONFIG_COUNT = 0;
-               for(x = 0; x <= MAX_TUR_CONFIG; ++x)
-                       { tur_config_queue[x] = string_null; }
+               for(int j = 0; j <= MAX_TUR_CONFIG; ++j)
+                       { tur_config_queue[j] = string_null; }
 
                // step 2: build new queue
                it.tr_config(it);
@@ -42,8 +43,8 @@ void Dump_Turret_Settings()
 
                // step 4: write queue
                TUR_CONFIG_WRITETOFILE(sprintf("// {{{ #%d: %s\n", i, it.turret_name))
-               for(x = 0; x <= TUR_CONFIG_COUNT; ++x)
-                       { TUR_CONFIG_WRITETOFILE(tur_config_queue[x]) }
+               for(int j = 0; j <= TUR_CONFIG_COUNT; ++j)
+                       { TUR_CONFIG_WRITETOFILE(tur_config_queue[j]) }
                TUR_CONFIG_WRITETOFILE("// }}}\n")
 
                // step 5: debug info
@@ -53,8 +54,8 @@ void Dump_Turret_Settings()
 
        // clear queue now that we're finished
        TUR_CONFIG_COUNT = 0;
-       for(x = 0; x <= MAX_TUR_CONFIG; ++x)
-               { tur_config_queue[x] = string_null; }
+       for(int j = 0; j <= MAX_TUR_CONFIG; ++j)
+               { tur_config_queue[j] = string_null; }
 
        // extra information
        LOG_INFO(sprintf("Totals: %d turrets, %d settings\n", (Turrets_COUNT - 1), totalsettings));
index bb2a81b847eb740251b5ba126335dfd8295e5e49..caa68a86487dcd3780eb9c277f93aab8eacaa635 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TURRETS_CONFIG_H
-#define TURRETS_CONFIG_H
+#pragma once
 
 #ifdef SVQC
 
@@ -17,5 +16,3 @@ string tur_config_queue[MAX_TUR_CONFIG];
 
 
 #endif
-
-#endif
index 57bb008fcdb133516ec9e0ca0c1ebbdf57a54678..c90eaff1fe98fdb22bc7918fefedd1f28c703738 100644 (file)
@@ -1,3 +1,4 @@
+#include "sv_turrets.qh"
 #ifdef SVQC
 #include <server/autocvars.qh>
 
@@ -32,7 +33,7 @@ vector turret_aim_generic(entity this)
 
                        if(this.aim_flags & TFL_AIM_ZPREDICT)
                        if(!IS_ONGROUND(this.enemy))
-                       if(this.enemy.movetype == MOVETYPE_WALK || this.enemy.movetype == MOVETYPE_TOSS || this.enemy.movetype == MOVETYPE_BOUNCE)
+                       if(this.enemy.move_movetype == MOVETYPE_WALK || this.enemy.move_movetype == MOVETYPE_TOSS || this.enemy.move_movetype == MOVETYPE_BOUNCE)
                        {
                                float vz;
                                prep_z = pre_pos_z;
@@ -193,8 +194,8 @@ void turret_die(entity this)
        {
                tur.tr_death(tur, this);
 
-               remove(this.tur_head);
-               remove(this);
+               delete(this.tur_head);
+               delete(this);
        }
        else
        {
@@ -440,12 +441,12 @@ void turret_projectile_explode(entity this)
 #else
        RadiusDamage (this, this.realowner, this.owner.shot_dmg, 0, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.totalfrags, NULL);
 #endif
-       remove(this);
+       delete(this);
 }
 
-void turret_projectile_touch(entity this)
+void turret_projectile_touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
        turret_projectile_explode(this);
 }
 
@@ -474,9 +475,11 @@ entity turret_projectile(entity actor, Sound _snd, float _size, float _health, f
        setthink(proj, turret_projectile_explode);
        settouch(proj, turret_projectile_touch);
        proj.nextthink    = time + 9;
-       proj.movetype           = MOVETYPE_FLYMISSILE;
+       set_movetype(proj, MOVETYPE_FLYMISSILE);
        proj.velocity           = normalize(actor.tur_shotdir_updated + randomvec() * actor.shot_spread) * actor.shot_speed;
-       proj.flags                = FL_PROJECTILE;
+       proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.enemy                = actor.enemy;
        proj.totalfrags  = _death;
        PROJECTILE_MAKETRIGGER(proj);
@@ -824,7 +827,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl
        */
 
 #ifdef TURRET_DEBUG_TARGETSELECT
-       LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n");
+       LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname);
 #endif
 
        return 1;
@@ -1174,7 +1177,7 @@ void turret_think(entity this)
 */
 void turret_use(entity this, entity actor, entity trigger)
 {
-       LOG_TRACE("Turret ",this.netname, " used by ", actor.classname, "\n");
+       LOG_TRACE("Turret ",this.netname, " used by ", actor.classname);
 
        this.team = actor.team;
 
@@ -1199,7 +1202,8 @@ void turrets_manager_think(entity this)
 
        if (autocvar_g_turrets_reloadcvars == 1)
        {
-               FOREACH_ENTITY(IS_TURRET(it), {
+               IL_EACH(g_turrets, true,
+               {
                        load_unit_settings(it, true);
                        Turret tur = get_turretinfo(it.m_id);
                        tur.tr_think(tur, it);
@@ -1248,6 +1252,8 @@ bool turret_initialize(entity this, Turret tur)
        // if tur_head exists, we can assume this turret re-spawned
        if(!this.tur_head) {
                tur.tr_precache(tur);
+               IL_PUSH(g_turrets, this);
+               IL_PUSH(g_bot_targets, this);
        }
 
        entity e = find(NULL, classname, "turret_manager");
@@ -1329,7 +1335,7 @@ bool turret_initialize(entity this, Turret tur)
        this.ammo_recharge                 *= this.ticrate;
        this.solid                                      = SOLID_BBOX;
        this.takedamage                         = DAMAGE_AIM;
-       this.movetype                           = MOVETYPE_NOCLIP;
+       set_movetype(this, MOVETYPE_NOCLIP);
        this.view_ofs                           = '0 0 0';
        this.turret_firecheckfunc       = turret_firecheck;
        this.event_damage                       = turret_damage;
@@ -1349,7 +1355,7 @@ bool turret_initialize(entity this, Turret tur)
        this.tur_head.owner                     = this;
        this.tur_head.takedamage        = DAMAGE_NO;
        this.tur_head.solid                     = SOLID_NOT;
-       this.tur_head.movetype          = this.movetype;
+       set_movetype(this.tur_head, this.move_movetype);
 
        if(!this.tur_defend)
        if(this.target != "")
@@ -1358,7 +1364,7 @@ bool turret_initialize(entity this, Turret tur)
                if (this.tur_defend == NULL)
                {
                        this.target = "";
-                       LOG_TRACE("Turret has invalid defendpoint!\n");
+                       LOG_TRACE("Turret has invalid defendpoint!");
                }
        }
 
index 29d08c6290fd6a0699204197e2484283b591c669..62759c058f3df63b37713919baf4f55a9c8314ee 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef SV_TURRETS_H
-#define SV_TURRETS_H
+#pragma once
 
 entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim);
 void turret_projectile_explode(entity this);
@@ -9,7 +8,6 @@ entity turret_select_target(entity this);
 
 // turret fields
 .float ticrate; // interal ai think rate
-.vector aim_idle; // where to aim while idle
 .entity tur_head; // top part of the turret
 .entity tur_defend; // defend this entity
 .vector tur_shotorg; // shot origin
@@ -119,5 +117,3 @@ vector tvt_tadv; // turret angle diff vector, updated by a successful call to tu
 float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target
 float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target
 float tvt_dist; // turret distance, updated by a successful call to turret_validate_target
-
-#endif
index 15dbaec4f009b0c8d87fba54e6c0269c72f41709..152a7d6a2715e362fde40fca3be8f20497ccc511 100644 (file)
@@ -1,13 +1,15 @@
+#include "targettrigger.qh"
 spawnfunc(turret_targettrigger);
-void turret_targettrigger_touch(entity this);
+void turret_targettrigger_touch(entity this, entity toucher);
 
-void turret_targettrigger_touch(entity this)
+void turret_targettrigger_touch(entity this, entity toucher)
 {
     if (this.cnt > time) return;
-    FOREACH_ENTITY_STRING_ORDERED(targetname, this.target, {
+    IL_EACH(g_turrets, it.targetname == this.target,
+    {
         if (!(it.turret_flags & TUR_FLAG_RECIEVETARGETS)) continue;
         if (!it.turret_addtarget) continue;
-        it.turret_addtarget(it, other, this);
+        it.turret_addtarget(it, toucher, this);
     });
     this.cnt = time + 0.5;
 }
@@ -16,7 +18,7 @@ void turret_targettrigger_touch(entity this)
 */
 spawnfunc(turret_targettrigger)
 {
-    if(!autocvar_g_turrets) { remove(this); return; }
+    if(!autocvar_g_turrets) { delete(this); return; }
 
     InitTrigger(this);
 
diff --git a/qcsrc/common/turrets/targettrigger.qh b/qcsrc/common/turrets/targettrigger.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index a66952833a10fe877c7ba2a806cc474da50be5ba..8e5239702866c4c6fe4519a7c34d67f2aa00b7fd 100644 (file)
@@ -1,27 +1,26 @@
-#ifndef TURRET_H
-#define TURRET_H
+#pragma once
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 CLASS(Turret, Object)
-    ATTRIB(Turret, m_id, int, 0)
+    ATTRIB(Turret, m_id, int, 0);
 
     /** short name */
-    ATTRIB(Turret, netname, string, string_null)
+    ATTRIB(Turret, netname, string);
     /** human readable name */
-    ATTRIB(Turret, turret_name, string, _("Turret"))
+    ATTRIB(Turret, turret_name, string, _("Turret"));
     /** currently a copy of the model */
-    ATTRIB(Turret, mdl, string, string_null)
+    ATTRIB(Turret, mdl, string);
     /** full name of model */
-    ATTRIB(Turret, model, string, string_null)
+    ATTRIB(Turret, model, string);
     /** full name of tur_head model */
-    ATTRIB(Turret, head_model, string, string_null)
+    ATTRIB(Turret, head_model, string);
 
-    ATTRIB(Turret, spawnflags, int, 0)
+    ATTRIB(Turret, spawnflags, int, 0);
     /** turret hitbox size */
-    ATTRIB(Turret, mins, vector, '-0 -0 -0')
+    ATTRIB(Turret, mins, vector, '-0 -0 -0');
     /** turret hitbox size */
-    ATTRIB(Turret, maxs, vector, '0 0 0')
+    ATTRIB(Turret, maxs, vector, '0 0 0');
 
     METHOD(Turret, display, void(Turret this, void(string name, string icon) returns)) {
         returns(this.turret_name, string_null);
@@ -42,7 +41,7 @@ CLASS(Turret, Object)
     METHOD(Turret, tr_precache, void(Turret this)) {
 
     }
-    ATTRIB(Turret, m_weapon, Weapon, WEP_Null)
+    ATTRIB(Turret, m_weapon, Weapon);
 #ifdef SVQC
     /** (SERVER) called when turret attacks */
     METHOD(Turret, tr_attack, void(Turret this, entity it)) {
@@ -176,5 +175,3 @@ const int TNSF_MOVE         = 64;
 const int TNSF_ANIM         = 128;
 
 const int TNSF_FULL_UPDATE  = 16777215;
-
-#endif
index a72a9f016ca35f111b661d813dde11213bcc8354..0a633c78446431fdb0f0c841d67c5d70e49da369 100644 (file)
@@ -1,24 +1,4 @@
-#ifndef TURRET_EWHEEL_H
-#define TURRET_EWHEEL_H
-
-//#define EWHEEL_FANCYPATH
-
-#include "ewheel_weapon.qh"
-
-CLASS(EWheel, Turret)
-/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM);
-/* mins       */ ATTRIB(EWheel, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(EWheel, maxs, vector, '32 32 48');
-/* modelname  */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3");
-/* model      */ ATTRIB_STRZONE(EWheel, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(EWheel, head_model, string, strcat("models/turrets/", "ewheel-gun1.md3"));
-/* netname    */ ATTRIB(EWheel, netname, string, "ewheel");
-/* fullname   */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret"));
-    ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL);
-ENDCLASS(EWheel)
-REGISTER_TURRET(EWHEEL, NEW(EWheel));
-
-#endif
+#include "ewheel.qh"
 
 #ifdef IMPLEMENTATION
 
@@ -133,7 +113,7 @@ void ewheel_move_idle(entity this)
         movelib_brake_simple(this, (autocvar_g_turrets_unit_ewheel_speed_stop));
 }
 
-spawnfunc(turret_ewheel) { if(!turret_initialize(this, TUR_EWHEEL)) remove(this); }
+spawnfunc(turret_ewheel) { if(!turret_initialize(this, TUR_EWHEEL)) delete(this); }
 
 METHOD(EWheel, tr_think, void(EWheel thistur, entity it))
 {
@@ -184,7 +164,7 @@ METHOD(EWheel, tr_setup, void(EWheel this, entity it))
 {
     entity e;
 
-    if(it.movetype == MOVETYPE_WALK)
+    if(it.move_movetype == MOVETYPE_WALK)
     {
         it.velocity = '0 0 0';
         it.enemy = NULL;
@@ -196,12 +176,12 @@ METHOD(EWheel, tr_setup, void(EWheel this, entity it))
             e = find(NULL, targetname, it.target);
             if (!e)
             {
-                LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!\n");
+                LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!");
                 it.target = "";
             }
 
             if (e.classname != "turret_checkpoint")
-                LOG_TRACE("Warning: not a turrret path\n");
+                LOG_TRACE("Warning: not a turrret path");
             else
             {
 
@@ -218,7 +198,8 @@ METHOD(EWheel, tr_setup, void(EWheel this, entity it))
     it.iscreature                              = true;
     it.teleportable                    = TELEPORT_NORMAL;
     it.damagedbycontents               = true;
-    it.movetype                                = MOVETYPE_WALK;
+    IL_PUSH(g_damagedbycontents, it);
+    set_movetype(it, MOVETYPE_WALK);
     it.solid                                   = SOLID_SLIDEBOX;
     it.takedamage                              = DAMAGE_AIM;
     it.idle_aim                                = '0 0 0';
@@ -247,8 +228,7 @@ void ewheel_draw(entity this)
 
     fixedmakevectors(this.angles);
     setorigin(this, this.origin + this.velocity * dt);
-    this.tur_head.angles += dt * this.tur_head.move_avelocity;
-    this.angles_y = this.move_angles_y;
+    this.tur_head.angles += dt * this.tur_head.avelocity;
 
     if (this.health < 127)
     if(random() < 0.05)
@@ -258,9 +238,7 @@ void ewheel_draw(entity this)
         METHOD(EWheel, tr_setup, void(EWheel this, entity it))
         {
             it.gravity         = 1;
-            it.movetype                = MOVETYPE_BOUNCE;
-            it.move_movetype   = MOVETYPE_BOUNCE;
-            it.move_origin     = it.origin;
+            set_movetype(it, MOVETYPE_BOUNCE);
             it.move_time               = time;
             it.draw                    = ewheel_draw;
         }
diff --git a/qcsrc/common/turrets/turret/ewheel.qh b/qcsrc/common/turrets/turret/ewheel.qh
new file mode 100644 (file)
index 0000000..b34adb2
--- /dev/null
@@ -0,0 +1,18 @@
+#pragma once
+
+//#define EWHEEL_FANCYPATH
+
+#include "ewheel_weapon.qh"
+
+CLASS(EWheel, Turret)
+/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM);
+/* mins       */ ATTRIB(EWheel, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(EWheel, maxs, vector, '32 32 48');
+/* modelname  */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3");
+/* model      */ ATTRIB_STRZONE(EWheel, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(EWheel, head_model, string, strcat("models/turrets/", "ewheel-gun1.md3"));
+/* netname    */ ATTRIB(EWheel, netname, string, "ewheel");
+/* fullname   */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret"));
+    ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL);
+ENDCLASS(EWheel)
+REGISTER_TURRET(EWHEEL, NEW(EWheel));
index 17df7dac04ad81c8e7fc742b87a203d67cccdc97..e77b534022423e2ae72de04f2de5e4822c2de027 100644 (file)
@@ -12,7 +12,7 @@ METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, .entity weapon
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_EWheelAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_EWheelAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
index 64b32d4a7c53f6cbd34210cf958ff18f024ef6c0..ab6e5f5cb67afe9bec1f43fb0adf6a88df0a309f 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_FLAC_H
-#define TURRET_FLAC_H
-
-#include "flac_weapon.qh"
-
-CLASS(Flac, Turret)
-/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE);
-/* mins       */ ATTRIB(Flac, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(Flac, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(Flac, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(Flac, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(Flac, head_model, string, strcat("models/turrets/", "flac.md3"));
-/* netname    */ ATTRIB(Flac, netname, string, "flac");
-/* fullname   */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon"));
-    ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC);
-ENDCLASS(Flac)
-REGISTER_TURRET(FLAC, NEW(Flac));
-
-#endif
+#include "flac.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_flac) { if (!turret_initialize(this, TUR_FLAC)) remove(this); }
+spawnfunc(turret_flac) { if (!turret_initialize(this, TUR_FLAC)) delete(this); }
 
 METHOD(Flac, tr_setup, void(Flac this, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/flac.qh b/qcsrc/common/turrets/turret/flac.qh
new file mode 100644 (file)
index 0000000..d53422c
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "flac_weapon.qh"
+
+CLASS(Flac, Turret)
+/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE);
+/* mins       */ ATTRIB(Flac, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(Flac, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(Flac, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(Flac, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(Flac, head_model, string, strcat("models/turrets/", "flac.md3"));
+/* netname    */ ATTRIB(Flac, netname, string, "flac");
+/* fullname   */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon"));
+    ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC);
+ENDCLASS(Flac)
+REGISTER_TURRET(FLAC, NEW(Flac));
index 3037f65d0d3c9554abb200494c193abfa9657f15..357bba975d853cfcd6cb50af66692cb16165a099 100644 (file)
@@ -12,7 +12,7 @@ METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, .entity weaponen
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_FlacAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_FlacAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
@@ -49,7 +49,7 @@ void turret_flac_projectile_think_explode(entity this)
 #else
     RadiusDamage (this, this.realowner, this.owner.shot_dmg, this.owner.shot_dmg, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.totalfrags, NULL);
 #endif
-    remove(this);
+    delete(this);
 }
 
 #endif
index 945b35dd0a4af2839772a953bfb7d7ac2c348385..163bffb9d70ebd8988f9f595b75b622c450bd605 100644 (file)
@@ -1,19 +1,4 @@
-#ifndef TURRET_FUSIONREACTOR_H
-#define TURRET_FUSIONREACTOR_H
-
-CLASS(FusionReactor, Turret)
-/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE);
-/* mins       */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0');
-/* maxs       */ ATTRIB(FusionReactor, maxs, vector, '34 34 90');
-/* modelname  */ ATTRIB(FusionReactor, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(FusionReactor, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(FusionReactor, head_model, string, strcat("models/turrets/", "reactor.md3"));
-/* netname    */ ATTRIB(FusionReactor, netname, string, "fusionreactor");
-/* fullname   */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor"));
-ENDCLASS(FusionReactor)
-REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor));
-
-#endif
+#include "fusionreactor.qh"
 
 #ifdef IMPLEMENTATION
 #ifdef SVQC
@@ -46,7 +31,7 @@ bool turret_fusionreactor_firecheck(entity this)
     return true;
 }
 
-spawnfunc(turret_fusionreactor) { if (!turret_initialize(this, TUR_FUSIONREACTOR)) remove(this); }
+spawnfunc(turret_fusionreactor) { if (!turret_initialize(this, TUR_FUSIONREACTOR)) delete(this); }
 
 METHOD(FusionReactor, tr_attack, void(FusionReactor this, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/fusionreactor.qh b/qcsrc/common/turrets/turret/fusionreactor.qh
new file mode 100644 (file)
index 0000000..134b805
--- /dev/null
@@ -0,0 +1,13 @@
+#pragma once
+
+CLASS(FusionReactor, Turret)
+/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE);
+/* mins       */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0');
+/* maxs       */ ATTRIB(FusionReactor, maxs, vector, '34 34 90');
+/* modelname  */ ATTRIB(FusionReactor, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(FusionReactor, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(FusionReactor, head_model, string, strcat("models/turrets/", "reactor.md3"));
+/* netname    */ ATTRIB(FusionReactor, netname, string, "fusreac");
+/* fullname   */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor"));
+ENDCLASS(FusionReactor)
+REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor));
index fde81bfea02e8dd5b8acac71edb090efa8efd902..88a0170ea67d4d556f7dd8a72c483615d0af8016 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_HELLION_H
-#define TURRET_HELLION_H
-
-#include "hellion_weapon.qh"
-
-CLASS(Hellion, Turret)
-/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
-/* mins       */ ATTRIB(Hellion, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(Hellion, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(Hellion, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(Hellion, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(Hellion, head_model, string, strcat("models/turrets/", "hellion.md3"));
-/* netname    */ ATTRIB(Hellion, netname, string, "hellion");
-/* fullname   */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret"));
-    ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION);
-ENDCLASS(Hellion)
-REGISTER_TURRET(HELLION, NEW(Hellion));
-
-#endif
+#include "hellion.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_hellion) { if (!turret_initialize(this, TUR_HELLION)) remove(this); }
+spawnfunc(turret_hellion) { if (!turret_initialize(this, TUR_HELLION)) delete(this); }
 
 METHOD(Hellion, tr_think, void(Hellion thistur, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/hellion.qh b/qcsrc/common/turrets/turret/hellion.qh
new file mode 100644 (file)
index 0000000..642645b
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "hellion_weapon.qh"
+
+CLASS(Hellion, Turret)
+/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
+/* mins       */ ATTRIB(Hellion, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(Hellion, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(Hellion, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(Hellion, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(Hellion, head_model, string, strcat("models/turrets/", "hellion.md3"));
+/* netname    */ ATTRIB(Hellion, netname, string, "hellion");
+/* fullname   */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret"));
+    ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION);
+ENDCLASS(Hellion)
+REGISTER_TURRET(HELLION, NEW(Hellion));
index 9e737ea6cf12e921fb2e48828b739ef382a5f0e3..ea392ec4ed9540f94fde30c999c5ae8aed3c1841 100644 (file)
@@ -15,7 +15,7 @@ METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, .entity weapo
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_HellionAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_HellionAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
@@ -33,7 +33,6 @@ METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, .entity weapo
         te_explosion (missile.origin);
         setthink(missile, turret_hellion_missile_think);
         missile.nextthink      = time;
-        missile.flags          = FL_PROJECTILE;
         missile.max_health   = time + 9;
         missile.tur_aimpos   = randomvec() * 128;
         missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
index da107922f19e5c9cc46cf0a6d3d71e93f9b9ef55..255821e1c368209aa8d229703f6c5dedcc8ec7ba 100644 (file)
@@ -1,24 +1,4 @@
-#ifndef TURRET_HK_H
-#define TURRET_HK_H
-
-//#define TURRET_DEBUG_HK
-
-#include "hk_weapon.qh"
-
-CLASS(HunterKiller, Turret)
-/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS);
-/* mins       */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(HunterKiller, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(HunterKiller, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(HunterKiller, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(HunterKiller, head_model, string, strcat("models/turrets/", "hk.md3"));
-/* netname    */ ATTRIB(HunterKiller, netname, string, "hk");
-/* fullname   */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret"));
-    ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK);
-ENDCLASS(HunterKiller)
-REGISTER_TURRET(HK, NEW(HunterKiller));
-
-#endif
+#include "hk.qh"
 
 #ifdef IMPLEMENTATION
 
@@ -28,7 +8,7 @@ REGISTER_TURRET(HK, NEW(HunterKiller));
 .float atime;
 #endif
 
-spawnfunc(turret_hk) { if(!turret_initialize(this, TUR_HK)) remove(this); }
+spawnfunc(turret_hk) { if(!turret_initialize(this, TUR_HK)) delete(this); }
 
 METHOD(HunterKiller, tr_think, void(HunterKiller thistur, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/hk.qh b/qcsrc/common/turrets/turret/hk.qh
new file mode 100644 (file)
index 0000000..d7c9cfb
--- /dev/null
@@ -0,0 +1,18 @@
+#pragma once
+
+//#define TURRET_DEBUG_HK
+
+#include "hk_weapon.qh"
+
+CLASS(HunterKiller, Turret)
+/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS);
+/* mins       */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(HunterKiller, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(HunterKiller, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(HunterKiller, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(HunterKiller, head_model, string, strcat("models/turrets/", "hk.md3"));
+/* netname    */ ATTRIB(HunterKiller, netname, string, "hk");
+/* fullname   */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret"));
+    ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK);
+ENDCLASS(HunterKiller)
+REGISTER_TURRET(HK, NEW(HunterKiller));
index dc6f49cab35dcf3a4b5bfc49d1dbd1e83783206d..5a2f05a4832219fdc3ed9c51a3238c79de625f49 100644 (file)
@@ -20,7 +20,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity
        if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
                if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_HunterKillerAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_HunterKillerAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
@@ -31,7 +31,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity
 
         setthink(missile, turret_hk_missile_think);
         missile.nextthink = time + 0.25;
-        missile.movetype = MOVETYPE_BOUNCEMISSILE;
+        set_movetype(missile, MOVETYPE_BOUNCEMISSILE);
         missile.velocity = actor.tur_shotdir_updated * (actor.shot_speed * 0.75);
         missile.angles = vectoangles(missile.velocity);
         missile.cnt = time + 30;
@@ -44,7 +44,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity
        }
 }
 
-bool hk_is_valid_target(entity this, entity e_target);
+bool hk_is_valid_target(entity this, entity proj, entity targ);
 void turret_hk_missile_think(entity this)
 {
     vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
@@ -53,10 +53,7 @@ void turret_hk_missile_think(entity this)
     float lt_for;   // Length of Trace FORwrad
     float lt_seek;  // Length of Trace SEEK (left, right, up down)
     float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
-    vector pre_pos;
     float myspeed;
-    entity e;
-    float ad,edist;
 
     this.nextthink = time + this.ticrate;
 
@@ -69,19 +66,17 @@ void turret_hk_missile_think(entity this)
     // Pick the closest valid target.
     if (!this.enemy)
     {
-        e = findradius(this.origin, 5000);
-        while (e)
+        // in this case, the lighter check is to validate it first, and check distance if it is valid
+        IL_EACH(g_damagedbycontents, hk_is_valid_target(this.owner, this, it),
         {
-            if (hk_is_valid_target(this, e))
-            {
-                if (!this.enemy)
-                    this.enemy = e;
-                else
-                    if (vlen2(this.origin - e.origin) < vlen2(this.origin - this.enemy.origin))
-                        this.enemy = e;
-            }
-            e = e.chain;
-        }
+            if(vdist(it.origin, >, 5000))
+                continue;
+
+            if(!this.enemy)
+                this.enemy = it;
+            else if(vlen2(this.origin - it.origin) < vlen2(this.origin - this.enemy.origin))
+                this.enemy = it;
+        });
     }
 
     this.angles = vectoangles(this.velocity);
@@ -91,16 +86,15 @@ void turret_hk_missile_think(entity this)
 
     if (this.enemy)
     {
-        edist = vlen(this.origin - this.enemy.origin);
         // Close enougth to do decent damage?
-        if ( edist <= (this.owner.shot_radius * 0.25) )
+        if(vdist(this.origin - this.enemy.origin, <=, (this.owner.shot_radius * 0.25)))
         {
             turret_projectile_explode(this);
             return;
         }
 
         // Get data on enemy position
-        pre_pos = this.enemy.origin +
+        vector pre_pos = this.enemy.origin +
                   this.enemy.velocity *
                   min((vlen(this.enemy.origin - this.origin) / vlen(this.velocity)),0.5);
 
@@ -111,12 +105,11 @@ void turret_hk_missile_think(entity this)
     }
     else
     {
-    edist = 0;
-    ve = '0 0 0';
+        ve = '0 0 0';
         fe = 0;
     }
 
-    if ((fe != 1) || (this.enemy == NULL) || (edist > 1000))
+    if ((fe != 1) || (this.enemy == NULL) || vdist(this.origin - this.enemy.origin, >, 1000))
     {
         myspeed = vlen(this.velocity);
 
@@ -129,7 +122,7 @@ void turret_hk_missile_think(entity this)
         ff = trace_fraction;
 
         // Find angular offset
-        ad = vlen(vectoangles(normalize(this.enemy.origin - this.origin)) - this.angles);
+        float ad = vlen(vectoangles(normalize(this.enemy.origin - this.origin)) - this.angles);
 
         // To close to something, Slow down!
         if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
@@ -207,7 +200,7 @@ void turret_hk_missile_think(entity this)
     {
         this.cnt = time + 0.25;
         this.nextthink = 0;
-        this.movetype           = MOVETYPE_BOUNCE;
+        set_movetype(this, MOVETYPE_BOUNCE);
         return;
     }
 
@@ -224,7 +217,7 @@ void turret_hk_missile_think(entity this)
 
 #ifdef TURRET_DEBUG_HK
     //if(this.atime < time) {
-    if ((fe <= 0.99)||(edist > 1000))
+    if ((fe <= 0.99)||vdist(this.origin - this.enemy.origin, >, 1000))
     {
         te_lightning2(NULL,this.origin, this.origin + vr * lt_seek);
         te_lightning2(NULL,this.origin, this.origin + vl * lt_seek);
@@ -246,35 +239,39 @@ void turret_hk_missile_think(entity this)
     UpdateCSQCProjectile(this);
 }
 
-bool hk_is_valid_target(entity this, entity e_target)
+bool hk_is_valid_target(entity this, entity proj, entity targ)
 {
-    if (e_target == NULL)
+    if (!targ)
+        return false;
+
+    // we know for sure pure entities are bad targets
+    if(is_pure(targ))
         return false;
 
     // If only this was used more..
-    if (e_target.flags & FL_NOTARGET)
+    if (targ.flags & FL_NOTARGET)
         return false;
 
     // Cant touch this
-    if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
+    if ((targ.takedamage == DAMAGE_NO) || (targ.health < 0))
         return false;
 
     // player
-    if (IS_CLIENT(e_target))
+    if (IS_PLAYER(targ))
     {
-        if (this.owner.target_select_playerbias < 0)
+        if (this.target_select_playerbias < 0)
             return false;
 
-        if (IS_DEAD(e_target))
+        if (IS_DEAD(targ))
             return false;
     }
 
     // Missile
-    if ((e_target.flags & FL_PROJECTILE) && (this.owner.target_select_missilebias < 0))
+    if ((targ.flags & FL_PROJECTILE) && (this.target_select_missilebias < 0))
         return false;
 
     // Team check
-    if ((e_target.team == this.owner.team) || (this.owner.team == e_target.owner.team))
+    if ((targ.team == this.team) || (this.team == targ.owner.team))
         return false;
 
     return true;
index 5dac49efc785a3d76f8236659f39bd4ae22f9f9c..db3cb47bf2fbb6cc3d173ccce2edba39f3ea6072 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_MACHINEGUN_H
-#define TURRET_MACHINEGUN_H
-
-#include "machinegun_weapon.qh"
-
-CLASS(MachineGunTurret, Turret)
-/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER);
-/* mins       */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(MachineGunTurret, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(MachineGunTurret, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(MachineGunTurret, head_model, string, strcat("models/turrets/", "machinegun.md3"));
-/* netname    */ ATTRIB(MachineGunTurret, netname, string, "machinegun");
-/* fullname   */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret"));
-    ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN);
-ENDCLASS(MachineGunTurret)
-REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret));
-
-#endif
+#include "machinegun.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_machinegun) { if (!turret_initialize(this, TUR_MACHINEGUN)) remove(this); }
+spawnfunc(turret_machinegun) { if (!turret_initialize(this, TUR_MACHINEGUN)) delete(this); }
 
 METHOD(MachineGunTurret, tr_setup, void(MachineGunTurret this, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/machinegun.qh b/qcsrc/common/turrets/turret/machinegun.qh
new file mode 100644 (file)
index 0000000..92a8fba
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "machinegun_weapon.qh"
+
+CLASS(MachineGunTurret, Turret)
+/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(MachineGunTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(MachineGunTurret, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(MachineGunTurret, head_model, string, strcat("models/turrets/", "machinegun.md3"));
+/* netname    */ ATTRIB(MachineGunTurret, netname, string, "machinegun");
+/* fullname   */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret"));
+    ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN);
+ENDCLASS(MachineGunTurret)
+REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret));
index 34cd32d74279c07a8ebda0744d5cab5e422a1810..44c8d645990152caf90e8b7b67e70e79e559f089 100644 (file)
@@ -4,7 +4,7 @@
 
 #ifdef SVQC
 
-void W_MachineGun_MuzzleFlash(entity actor);
+void W_MachineGun_MuzzleFlash(entity actor, .entity weaponentity);
 SOUND(MachineGunTurretAttack_FIRE, W_Sound("electro_fire"));
 METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
 {
@@ -13,15 +13,15 @@ METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .ent
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_MachineGunTurretAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MachineGunTurretAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
         }
         fireBullet (actor, actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN.m_id, 0);
-        W_MachineGun_MuzzleFlash(actor);
-        setattachment(actor.muzzle_flash, actor.tur_head, "tag_fire");
+        W_MachineGun_MuzzleFlash(actor, weaponentity);
+        setattachment(actor.(weaponentity).muzzle_flash, actor.tur_head, "tag_fire");
     }
 }
 
index bce27118a2ce34dc9ba605aa47d1adf78ea416c2..472a0cb09a5c410ad5ae5a7060e410799189ccd4 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_MLRS_H
-#define TURRET_MLRS_H
-
-#include "mlrs_weapon.qh"
-
-CLASS(MLRSTurret, Turret)
-/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
-/* mins       */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(MLRSTurret, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(MLRSTurret, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(MLRSTurret, head_model, string, strcat("models/turrets/", "mlrs.md3"));
-/* netname    */ ATTRIB(MLRSTurret, netname, string, "mlrs");
-/* fullname   */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret"));
-    ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS);
-ENDCLASS(MLRSTurret)
-REGISTER_TURRET(MLRS, NEW(MLRSTurret));
-
-#endif
+#include "mlrs.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_mlrs) { if (!turret_initialize(this, TUR_MLRS)) remove(this); }
+spawnfunc(turret_mlrs) { if (!turret_initialize(this, TUR_MLRS)) delete(this); }
 
 METHOD(MLRSTurret, tr_think, void(MLRSTurret thistur, entity it))
 {
@@ -30,8 +12,8 @@ METHOD(MLRSTurret, tr_think, void(MLRSTurret thistur, entity it))
     it.tur_head.frame = bound(0, 6 - floor(0.1 + it.ammo / it.shot_dmg), 6);
     if(it.tur_head.frame < 0)
     {
-        LOG_TRACE("ammo:",ftos(it.ammo),"\n");
-        LOG_TRACE("shot_dmg:",ftos(it.shot_dmg),"\n");
+        LOG_TRACE("ammo:",ftos(it.ammo));
+        LOG_TRACE("shot_dmg:",ftos(it.shot_dmg));
     }
 }
 METHOD(MLRSTurret, tr_setup, void(MLRSTurret this, entity it))
diff --git a/qcsrc/common/turrets/turret/mlrs.qh b/qcsrc/common/turrets/turret/mlrs.qh
new file mode 100644 (file)
index 0000000..b2a6a5c
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "mlrs_weapon.qh"
+
+CLASS(MLRSTurret, Turret)
+/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(MLRSTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(MLRSTurret, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(MLRSTurret, head_model, string, strcat("models/turrets/", "mlrs.md3"));
+/* netname    */ ATTRIB(MLRSTurret, netname, string, "mlrs");
+/* fullname   */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret"));
+    ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS);
+ENDCLASS(MLRSTurret)
+REGISTER_TURRET(MLRS, NEW(MLRSTurret));
index 305392cd5ad03a65020d33fb0e8aca7300ab616f..cfd51c732076b2686fc9ae12e96adae33db462ea 100644 (file)
@@ -11,7 +11,7 @@ METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, .entity we
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_MLRSTurretAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MLRSTurretAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
index f604b0466953707f98a59082ae7b0258cc06e665..31ece9cb2b8b86d7946d9c0eaf45634910e09036 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_PHASER_H
-#define TURRET_PHASER_H
-
-#include "phaser_weapon.qh"
-
-CLASS(PhaserTurret, Turret)
-/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER);
-/* mins       */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(PhaserTurret, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(PhaserTurret, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(PhaserTurret, head_model, string, strcat("models/turrets/", "phaser.md3"));
-/* netname    */ ATTRIB(PhaserTurret, netname, string, "phaser");
-/* fullname   */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon"));
-    ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER);
-ENDCLASS(PhaserTurret)
-REGISTER_TURRET(PHASER, NEW(PhaserTurret));
-
-#endif
+#include "phaser.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_phaser) { if (!turret_initialize(this, TUR_PHASER)) remove(this); }
+spawnfunc(turret_phaser) { if (!turret_initialize(this, TUR_PHASER)) delete(this); }
 
 .int fireflag;
 
diff --git a/qcsrc/common/turrets/turret/phaser.qh b/qcsrc/common/turrets/turret/phaser.qh
new file mode 100644 (file)
index 0000000..fedbe66
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "phaser_weapon.qh"
+
+CLASS(PhaserTurret, Turret)
+/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(PhaserTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(PhaserTurret, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(PhaserTurret, head_model, string, strcat("models/turrets/", "phaser.md3"));
+/* netname    */ ATTRIB(PhaserTurret, netname, string, "phaser");
+/* fullname   */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon"));
+    ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER);
+ENDCLASS(PhaserTurret)
+REGISTER_TURRET(PHASER, NEW(PhaserTurret));
index ea48a6479024734fd4a425f044c37548d40118e9..bf901d886a77e407292d8d208d20f37819f5b8cc 100644 (file)
@@ -14,7 +14,7 @@ METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, .entity
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_PhaserTurretAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_PhaserTurretAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
@@ -33,9 +33,10 @@ METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, .entity
         beam.owner = actor;
         beam.shot_dmg = actor.shot_dmg / (actor.shot_speed / beam.ticrate);
         beam.scale = actor.target_range / 256;
-        beam.movetype = MOVETYPE_NONE;
+        set_movetype(beam, MOVETYPE_NONE);
         beam.enemy = actor.enemy;
         beam.bot_dodge = true;
+        IL_PUSH(g_bot_dodge, beam);
         beam.bot_dodgerating = beam.shot_dmg;
         sound (beam, CH_SHOTS_SINGLE, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM);
         actor.fireflag = 1;
@@ -60,7 +61,7 @@ void beam_think(entity this)
         this.owner.fireflag = 2;
         this.owner.tur_head.frame = 10;
         sound (this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
-        remove(this);
+        delete(this);
         return;
     }
 
index 7b71ccfc7fa78be080d7b1fa1dba7f189550111d..d161436ab0807041b4bff31e65205cea02a15264 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_PLASMA_H
-#define TURRET_PLASMA_H
-
-#include "plasma_weapon.qh"
-
-CLASS(PlasmaTurret, Turret)
-/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
-/* mins       */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(PlasmaTurret, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(PlasmaTurret, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(PlasmaTurret, head_model, string, strcat("models/turrets/", "plasma.md3"));
-/* netname    */ ATTRIB(PlasmaTurret, netname, string, "plasma");
-/* fullname   */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon"));
-    ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA);
-ENDCLASS(PlasmaTurret)
-REGISTER_TURRET(PLASMA, NEW(PlasmaTurret));
-
-#endif
+#include "plasma.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_plasma) { if (!turret_initialize(this, TUR_PLASMA)) remove(this); }
+spawnfunc(turret_plasma) { if (!turret_initialize(this, TUR_PLASMA)) delete(this); }
 
 METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/plasma.qh b/qcsrc/common/turrets/turret/plasma.qh
new file mode 100644 (file)
index 0000000..fc2a96d
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "plasma_weapon.qh"
+
+CLASS(PlasmaTurret, Turret)
+/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(PlasmaTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(PlasmaTurret, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(PlasmaTurret, head_model, string, strcat("models/turrets/", "plasma.md3"));
+/* netname    */ ATTRIB(PlasmaTurret, netname, string, "plasma");
+/* fullname   */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon"));
+    ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA);
+ENDCLASS(PlasmaTurret)
+REGISTER_TURRET(PLASMA, NEW(PlasmaTurret));
index 86df15059bfc136ae83102ac93ed6cae39dbcbc1..9e6d80b2fd830f165117630e685779d19afb82ca 100644 (file)
@@ -1,34 +1,10 @@
-#ifndef TURRET_PLASMA_DUAL_H
-#define TURRET_PLASMA_DUAL_H
-
-#include "plasma_weapon.qh"
-
-CLASS(PlasmaDualAttack, PlasmaAttack)
-/* refname   */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual");
-/* wepname   */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma"));
-ENDCLASS(PlasmaDualAttack)
-REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack));
-
-CLASS(DualPlasmaTurret, PlasmaTurret)
-/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
-/* mins       */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0');
-/* maxs       */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64');
-/* modelname  */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3");
-/* model      */ ATTRIB_STRZONE(DualPlasmaTurret, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(DualPlasmaTurret, head_model, string, strcat("models/turrets/", "plasmad.md3"));
-/* netname    */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual");
-/* fullname   */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon"));
-    ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL);
-ENDCLASS(DualPlasmaTurret)
-REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret));
-
-#endif
+#include "plasma_dual.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_plasma_dual) { if (!turret_initialize(this, TUR_PLASMA_DUAL)) remove(this); }
+spawnfunc(turret_plasma_dual) { if (!turret_initialize(this, TUR_PLASMA_DUAL)) delete(this); }
 
 METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret thistur, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/plasma_dual.qh b/qcsrc/common/turrets/turret/plasma_dual.qh
new file mode 100644 (file)
index 0000000..e4c7b0e
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "plasma_weapon.qh"
+
+CLASS(PlasmaDualAttack, PlasmaAttack)
+/* refname   */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual");
+/* wepname   */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma"));
+ENDCLASS(PlasmaDualAttack)
+REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack));
+
+CLASS(DualPlasmaTurret, PlasmaTurret)
+/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
+/* mins       */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0');
+/* maxs       */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64');
+/* modelname  */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3");
+/* model      */ ATTRIB_STRZONE(DualPlasmaTurret, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(DualPlasmaTurret, head_model, string, strcat("models/turrets/", "plasmad.md3"));
+/* netname    */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual");
+/* fullname   */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon"));
+    ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL);
+ENDCLASS(DualPlasmaTurret)
+REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret));
index 535e8b4754661fa7fcf772415484e0c3a84f70c0..f6f717f85ff6b612974ec854de8888913199aa20 100644 (file)
@@ -10,7 +10,7 @@ METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, .entity weapon
        if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
                if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_PlasmaAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_PlasmaAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
index cdc6ba3ceb95a7d4fba543e26a0532d01dcdda4d..249fe18eb92042465d499a947929c672aee49fa3 100644 (file)
@@ -1,28 +1,10 @@
-#ifndef TURRET_TESLA_H
-#define TURRET_TESLA_H
-
-#include "tesla_weapon.qh"
-
-CLASS(TeslaCoil, Turret)
-/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
-/* mins       */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0');
-/* maxs       */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128');
-/* modelname  */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3");
-/* model      */ ATTRIB_STRZONE(TeslaCoil, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(TeslaCoil, head_model, string, strcat("models/turrets/", "tesla_head.md3"));
-/* netname    */ ATTRIB(TeslaCoil, netname, string, "tesla");
-/* fullname   */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil"));
-    ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA);
-ENDCLASS(TeslaCoil)
-REGISTER_TURRET(TESLA, NEW(TeslaCoil));
-
-#endif
+#include "tesla.qh"
 
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
 
-spawnfunc(turret_tesla) { if (!turret_initialize(this, TUR_TESLA)) remove(this); }
+spawnfunc(turret_tesla) { if (!turret_initialize(this, TUR_TESLA)) delete(this); }
 
 METHOD(TeslaCoil, tr_think, void(TeslaCoil thistur, entity it))
 {
diff --git a/qcsrc/common/turrets/turret/tesla.qh b/qcsrc/common/turrets/turret/tesla.qh
new file mode 100644 (file)
index 0000000..c5f67b1
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "tesla_weapon.qh"
+
+CLASS(TeslaCoil, Turret)
+/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
+/* mins       */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0');
+/* maxs       */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128');
+/* modelname  */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3");
+/* model      */ ATTRIB_STRZONE(TeslaCoil, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(TeslaCoil, head_model, string, strcat("models/turrets/", "tesla_head.md3"));
+/* netname    */ ATTRIB(TeslaCoil, netname, string, "tesla");
+/* fullname   */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil"));
+    ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA);
+ENDCLASS(TeslaCoil)
+REGISTER_TURRET(TESLA, NEW(TeslaCoil));
index e21cd498f71738dc66921e209e83b28282ebbb55..a173d8b675cbd590d1defb4ef85f424542fc736b 100644 (file)
@@ -12,7 +12,7 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .enti
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_TeslaCoilTurretAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_TeslaCoilTurretAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
@@ -27,7 +27,7 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .enti
         actor.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
 
         entity t = toast(actor, e,r,d);
-        remove(e);
+        delete(e);
 
         if (t == NULL) return;
 
@@ -42,12 +42,10 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .enti
 
         }
 
-        e = findchainfloat(railgunhit, 1);
-        while (e) {
-            e.railgunhit = 0;
-            e = e.chain;
-        }
-
+        FOREACH_ENTITY_FLOAT(railgunhit, 1,
+        {
+            it.railgunhit = 0;
+        });
     }
 }
 
index 1a5c3f09ab03aa5f51ec97d3b98ff386861781b9..de744a65d7a6c6109aa43736beb3df1c82d9eff3 100644 (file)
@@ -1,24 +1,4 @@
-#ifndef TURRET_WALKER_H
-#define TURRET_WALKER_H
-
-//#define WALKER_FANCYPATHING
-
-#include "walker_weapon.qh"
-
-CLASS(WalkerTurret, Turret)
-/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE);
-/* mins       */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0');
-/* maxs       */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95');
-/* modelname  */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3");
-/* model      */ ATTRIB_STRZONE(WalkerTurret, model, string, strcat("models/turrets/", this.mdl));
-/* head_model */ ATTRIB_STRZONE(WalkerTurret, head_model, string, strcat("models/turrets/", "walker_head_minigun.md3"));
-/* netname    */ ATTRIB(WalkerTurret, netname, string, "walker");
-/* fullname   */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret"));
-    ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER);
-ENDCLASS(WalkerTurret)
-REGISTER_TURRET(WALKER, NEW(WalkerTurret));
-
-#endif
+#include "walker.qh"
 
 #ifdef IMPLEMENTATION
 
@@ -100,7 +80,12 @@ void walker_setnoanim(entity this)
 void walker_rocket_explode(entity this)
 {
     RadiusDamage (this, this.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), this, NULL, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET.m_id, NULL);
-    remove (this);
+    delete (this);
+}
+
+void walker_rocket_touch(entity this, entity toucher)
+{
+    walker_rocket_explode(this);
 }
 
 void walker_rocket_damage(entity this, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
@@ -250,11 +235,13 @@ void walker_fire_rocket(entity this, vector org)
     rocket.event_damage           = walker_rocket_damage;
 
     rocket.nextthink             = time;
-    rocket.movetype               = MOVETYPE_FLY;
+    set_movetype(rocket, MOVETYPE_FLY);
     rocket.velocity               = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed);
     rocket.angles                       = vectoangles(rocket.velocity);
-    settouch(rocket, walker_rocket_explode);
-    rocket.flags                         = FL_PROJECTILE;
+    settouch(rocket, walker_rocket_touch);
+    rocket.flags = FL_PROJECTILE;
+    IL_PUSH(g_projectiles, rocket);
+    IL_PUSH(g_bot_dodge, rocket);
     rocket.solid                         = SOLID_BBOX;
     rocket.max_health           = time + 9;
     rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
@@ -339,7 +326,7 @@ void walker_move_path(entity this)
 #endif
 }
 
-spawnfunc(turret_walker) { if(!turret_initialize(this, TUR_WALKER)) remove(this); }
+spawnfunc(turret_walker) { if(!turret_initialize(this, TUR_WALKER)) delete(this); }
 
 METHOD(WalkerTurret, tr_think, void(WalkerTurret thistur, entity it))
 {
@@ -566,7 +553,7 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
     entity e;
 
     // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn.
-    if(it.movetype == MOVETYPE_WALK)
+    if(it.move_movetype == MOVETYPE_WALK)
     {
         if(it.pos1)
             setorigin(it, it.pos1);
@@ -583,9 +570,10 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
     it.iscreature = true;
     it.teleportable = TELEPORT_NORMAL;
     it.damagedbycontents = true;
+    IL_PUSH(g_damagedbycontents, it);
     it.solid = SOLID_SLIDEBOX;
     it.takedamage = DAMAGE_AIM;
-    if(it.movetype != MOVETYPE_WALK)
+    if(it.move_movetype != MOVETYPE_WALK)
     {
         setorigin(it, it.origin);
         tracebox(it.origin + '0 0 128', it.mins, it.maxs, it.origin - '0 0 10000', MOVE_NORMAL, it);
@@ -593,7 +581,7 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
         it.pos1 = it.origin;
         it.pos2 = it.angles;
     }
-    it.movetype = MOVETYPE_WALK;
+    set_movetype(it, MOVETYPE_WALK);
     it.idle_aim = '0 0 0';
     it.turret_firecheckfunc = walker_firecheck;
 
@@ -602,12 +590,12 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
         e = find(NULL, targetname, it.target);
         if (!e)
         {
-            LOG_TRACE("Initital waypoint for walker does NOT exsist, fix your map!\n");
+            LOG_TRACE("Initital waypoint for walker does NOT exsist, fix your map!");
             it.target = "";
         }
 
         if (e.classname != "turret_checkpoint")
-            LOG_TRACE("Warning: not a turrret path\n");
+            LOG_TRACE("Warning: not a turrret path");
         else
         {
 #ifdef WALKER_FANCYPATHING
@@ -637,8 +625,7 @@ void walker_draw(entity this)
     fixedmakevectors(this.angles);
     movelib_groundalign4point(this, 300, 100, 0.25, 45);
     setorigin(this, this.origin + this.velocity * dt);
-    this.tur_head.angles += dt * this.tur_head.move_avelocity;
-    this.angles_y = this.move_angles_y;
+    this.tur_head.angles += dt * this.tur_head.avelocity;
 
     if (this.health < 127)
     if(random() < 0.15)
@@ -648,9 +635,7 @@ void walker_draw(entity this)
         METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
         {
             it.gravity         = 1;
-            it.movetype                = MOVETYPE_BOUNCE;
-            it.move_movetype   = MOVETYPE_BOUNCE;
-            it.move_origin     = it.origin;
+            set_movetype(it, MOVETYPE_BOUNCE);
             it.move_time               = time;
             it.draw                    = walker_draw;
         }
diff --git a/qcsrc/common/turrets/turret/walker.qh b/qcsrc/common/turrets/turret/walker.qh
new file mode 100644 (file)
index 0000000..54a908b
--- /dev/null
@@ -0,0 +1,18 @@
+#pragma once
+
+//#define WALKER_FANCYPATHING
+
+#include "walker_weapon.qh"
+
+CLASS(WalkerTurret, Turret)
+/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE);
+/* mins       */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0');
+/* maxs       */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95');
+/* modelname  */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3");
+/* model      */ ATTRIB_STRZONE(WalkerTurret, model, string, strcat("models/turrets/", this.mdl));
+/* head_model */ ATTRIB_STRZONE(WalkerTurret, head_model, string, strcat("models/turrets/", "walker_head_minigun.md3"));
+/* netname    */ ATTRIB(WalkerTurret, netname, string, "walker");
+/* fullname   */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret"));
+    ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER);
+ENDCLASS(WalkerTurret)
+REGISTER_TURRET(WALKER, NEW(WalkerTurret));
index 91e4345a95b3f6c6c0e58f79dfe8d7b5198c5867..88b18b9198385bb8b5027fc2139c61750f0680eb 100644 (file)
@@ -11,7 +11,7 @@ METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, .entity
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
         if (isPlayer) {
             turret_initparams(actor);
-            W_SetupShot_Dir(actor, v_forward, false, 0, SND_WalkerTurretAttack_FIRE, CH_WEAPON_B, 0);
+            W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_WalkerTurretAttack_FIRE, CH_WEAPON_B, 0);
             actor.tur_shotdir_updated = w_shotdir;
             actor.tur_shotorg = w_shotorg;
             actor.tur_head = actor;
diff --git a/qcsrc/common/turrets/turrets.qc b/qcsrc/common/turrets/turrets.qc
new file mode 100644 (file)
index 0000000..a53f859
--- /dev/null
@@ -0,0 +1 @@
+#include "turrets.qh"
diff --git a/qcsrc/common/turrets/turrets.qh b/qcsrc/common/turrets/turrets.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 4928262f2f4ef85c355e875c7a1c2097a1bdd58d..5fef364580860b256d624eb552b32a13c55fc098 100644 (file)
@@ -1,3 +1,4 @@
+#include "util.qh"
 /*
 * Update this.tur_shotorg by getting up2date bone info
 * NOTICE this func overwrites the global v_forward, v_right and v_up vectors.
@@ -6,7 +7,7 @@ float turret_tag_fire_update(entity this)
 {
        if(!this.tur_head)
        {
-               LOG_DEBUG("Call to turret_tag_fire_update with this.tur_head missing!\n");
+               LOG_DEBUG("Call to turret_tag_fire_update with this.tur_head missing!");
                this.tur_shotorg = '0 0 0';
                return false;
        }
@@ -115,7 +116,7 @@ void mark_error(vector where,float lifetime)
        entity err = new(error_marker);
        setmodel(err, MDL_MARKER);
        setorigin(err, where);
-       err.movetype = MOVETYPE_NONE;
+       set_movetype(err, MOVETYPE_NONE);
        setthink(err, marker_think);
        err.nextthink = time;
        err.skin = 0;
@@ -128,7 +129,7 @@ void mark_info(vector where,float lifetime)
        entity err = spawn(info_marker);
        setmodel(err, MDL_MARKER);
        setorigin(err, where);
-       err.movetype = MOVETYPE_NONE;
+       set_movetype(err, MOVETYPE_NONE);
        setthink(err, marker_think);
        err.nextthink = time;
        err.skin = 1;
@@ -141,7 +142,7 @@ entity mark_misc(vector where,float lifetime)
        entity err = spawn(mark_misc);
        setmodel(err, MDL_MARKER);
        setorigin(err, where);
-       err.movetype = MOVETYPE_NONE;
+       set_movetype(err, MOVETYPE_NONE);
        setthink(err, marker_think);
        err.nextthink = time;
        err.skin = 3;
@@ -167,7 +168,7 @@ void paint_target(entity onwho, float f_size, vector v_color, float f_time)
        //setattachment(e,onwho,"");
        setorigin(e, onwho.origin + '0 0 1');
        e.alpha = 0.15;
-       e.movetype = MOVETYPE_FLY;
+       set_movetype(e, MOVETYPE_FLY);
 
        e.velocity = (v_color * 32); // + '0 0 1' * 64;
 
@@ -186,7 +187,7 @@ void paint_target2(entity onwho, float f_size, vector v_color, float f_time)
 
        setorigin(e, onwho.origin + '0 0 1');
        e.alpha = 0.15;
-       e.movetype = MOVETYPE_FLY;
+       set_movetype(e, MOVETYPE_FLY);
 
        e.velocity = (v_color * 32); // + '0 0 1' * 64;
        e.avelocity_x = -128;
@@ -203,7 +204,7 @@ void paint_target3(vector where, float f_size, vector v_color, float f_time)
        e.scale = (f_size/512);
        setsize(e, '0 0 0', '0 0 0');
        setorigin(e, where + '0 0 1');
-       e.movetype = MOVETYPE_NONE;
+       set_movetype(e, MOVETYPE_NONE);
        e.velocity = '0 0 0';
        e.colormod = v_color;
        SUB_SetFade(e,time,f_time);
index d5c948d573e85fb46b2cc28de60d413989766392..5f52695bafe7b2df5d9c26e8c6f74ffaa78b73b0 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef TURRETS_UTIL_H
-#define TURRETS_UTIL_H
+#pragma once
 
 float shortangle_f(float ang1, float ang2);
 float anglemods(float v);
@@ -8,5 +7,3 @@ vector shortangle_vxy(vector ang1, vector ang2);
 vector angleofs(entity from, entity to);
 vector angleofs3(vector from, vector from_a, entity to);
 void FireImoBeam(entity this, vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype);
-
-#endif
index d04fdecd68dc791f05a8fe93878ed0e6185822b4..d83871080d1f19167fd803096f8ef03efd2cb11f 100644 (file)
@@ -18,7 +18,7 @@
     #include "mapinfo.qh"
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 /*
 * Get "real" origin, in worldspace, even if ent is attached to something else.
 */
@@ -55,8 +55,7 @@ string wordwrap(string s, float l)
        return r;
 }
 
-#ifndef MENUQC
-#ifndef CSQC
+#ifdef SVQC
 entity _wordwrap_buffer_sprint_ent;
 void wordwrap_buffer_sprint(string s)
 {
@@ -80,7 +79,6 @@ void wordwrap_sprint(entity to, string s, float l)
        return;
 }
 #endif
-#endif
 
 #ifndef SVQC
 string draw_UseSkinFor(string pic)
@@ -230,13 +228,13 @@ string ScoreString(int pFlags, float pValue)
 
 float lengthLogTable[128];
 
-float invertLengthLog(float x)
+float invertLengthLog(float dist)
 {
        int l, r, m;
 
-       if(x >= lengthLogTable[127])
+       if(dist >= lengthLogTable[127])
                return 127;
-       if(x <= lengthLogTable[0])
+       if(dist <= lengthLogTable[0])
                return 0;
 
        l = 0;
@@ -245,15 +243,15 @@ float invertLengthLog(float x)
        while(r - l > 1)
        {
                m = floor((l + r) / 2);
-               if(lengthLogTable[m] < x)
+               if(lengthLogTable[m] < dist)
                        l = m;
                else
                        r = m;
        }
 
        // now: r is >=, l is <
-       float lerr = (x - lengthLogTable[l]);
-       float rerr = (lengthLogTable[r] - x);
+       float lerr = (dist - lengthLogTable[l]);
+       float rerr = (lengthLogTable[r] - dist);
        if(lerr < rerr)
                return l;
        return r;
@@ -265,26 +263,26 @@ vector decompressShortVector(int data)
        if(data == 0)
                return '0 0 0';
        float p = (data & 0xF000) / 0x1000;
-       float y = (data & 0x0F80) / 0x80;
+       float q = (data & 0x0F80) / 0x80;
        int len = (data & 0x007F);
 
-       //print("\ndecompress: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
+       //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n");
 
        if(p == 0)
        {
                out.x = 0;
                out.y = 0;
-               if(y == 31)
+               if(q == 31)
                        out.z = -1;
                else
                        out.z = +1;
        }
        else
        {
-               y   = .19634954084936207740 * y;
+               q   = .19634954084936207740 * q;
                p = .19634954084936207740 * p - 1.57079632679489661922;
-               out.x = cos(y) *  cos(p);
-               out.y = sin(y) *  cos(p);
+               out.x = cos(q) *  cos(p);
+               out.y = sin(q) *  cos(p);
                out.z =          -sin(p);
        }
 
@@ -325,7 +323,7 @@ float compressShortVector(vector vec)
        return (p * 0x1000) + (y * 0x80) + len;
 }
 
-void compressShortVector_init()
+STATIC_INIT(compressShortVector)
 {
        float l = 1;
        float f = pow(2, 1/8);
@@ -352,7 +350,7 @@ void compressShortVector_init()
        }
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
 {
        traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
@@ -399,6 +397,9 @@ string fixPriorityList(string order, float from, float to, float subtract, float
                n = tokenize_console(neworder);
                for(w = to; w >= from; --w)
                {
+                       int wflags = Weapons_from(w).spawnflags;
+                       if((wflags & WEP_FLAG_HIDDEN) && (wflags & WEP_FLAG_MUTATORBLOCKED) && !(wflags & WEP_FLAG_NORMAL))
+                               continue;
                        for(i = 0; i < n; ++i)
                                if(stof(argv(i)) == w)
                                        break;
@@ -448,7 +449,7 @@ string swapInPriorityList(string order, float i, float j)
        return order;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 void get_mi_min_max(float mode)
 {
        vector mi, ma;
@@ -472,7 +473,7 @@ void get_mi_min_max(float mode)
 
        mi_min = mi;
        mi_max = ma;
-       MapInfo_Get_ByName(mi_shortname, 0, 0);
+       MapInfo_Get_ByName(mi_shortname, 0, NULL);
        if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
        {
                mi_min = MapInfo_Map_mins;
@@ -592,7 +593,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
 
        if (!(tmp_cvar || tmp_value))
        {
-               LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !\n");
+               LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !");
                return 0;
        }
 
@@ -602,7 +603,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
                return 0;
        }
 
-       FOREACH_ENTITY_CLASS("saved_cvar_value", it.netname == tmp_cvar,
+       IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
        {
                created_saved_value = -1; // skip creation
                break; // no need to continue
@@ -612,6 +613,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
        {
                // creating a new entity to keep track of this cvar
                entity e = new_pure(saved_cvar_value);
+               IL_PUSH(g_saved_cvars, e);
                e.netname = strzone(tmp_cvar);
                e.message = strzone(cvar_string(tmp_cvar));
                created_saved_value = 1;
@@ -635,7 +637,7 @@ int cvar_settemp_restore()
                        cvar_set(it.netname, it.message);
                        strunzone(it.netname);
                        strunzone(it.message);
-                       remove(it);
+                       delete(it);
                        ++j;
                }
                else
@@ -649,7 +651,7 @@ int cvar_settemp_restore()
                if(cvar_type(e.netname))
                {
                        cvar_set(e.netname, e.message);
-                       remove(e);
+                       delete(e);
                        ++j;
                }
                else
@@ -660,6 +662,58 @@ int cvar_settemp_restore()
        return j;
 }
 
+bool isCaretEscaped(string theText, float pos)
+{
+       int i = 0;
+       while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^")
+               ++i;
+       return (i & 1);
+}
+
+int skipIncompleteTag(string theText, float pos, int len)
+{
+       int i = 0, ch = 0;
+       int tag_start = -1;
+
+       if(substring(theText, pos - 1, 1) == "^")
+       {
+               if(isCaretEscaped(theText, pos - 1) || pos >= len)
+                       return 0;
+
+               ch = str2chr(theText, pos);
+               if(ch >= '0' && ch <= '9')
+                       return 1; // ^[0-9] color code found
+               else if (ch == 'x')
+                       tag_start = pos - 1; // ^x tag found
+               else
+                       return 0;
+       }
+       else
+       {
+               for(i = 2; pos - i >= 0 && i <= 4; ++i)
+               {
+                       if(substring(theText, pos - i, 2) == "^x")
+                       {
+                               tag_start = pos - i; // ^x tag found
+                               break;
+                       }
+               }
+       }
+
+       if(tag_start >= 0)
+       {
+               if(tag_start + 5 < len)
+               if(IS_HEXDIGIT(substring(theText, tag_start + 2, 1)))
+               if(IS_HEXDIGIT(substring(theText, tag_start + 3, 1)))
+               if(IS_HEXDIGIT(substring(theText, tag_start + 4, 1)))
+               {
+                       if(!isCaretEscaped(theText, tag_start))
+                               return 5 - (pos - tag_start); // ^xRGB color code found
+               }
+       }
+       return 0;
+}
+
 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
 {
        // STOP.
@@ -670,57 +724,25 @@ float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLe
        if(w(theText, theSize) <= maxWidth)
                return strlen(theText); // yeah!
 
+       bool colors = (w("^7", theSize) == 0);
+
        // binary search for right place to cut string
-       float ch;
-       float left, right, middle; // this always works
+       int len, left, right, middle;
        left = 0;
-       right = strlen(theText); // this always fails
+       right = len = strlen(theText);
+       int ofs = 0;
        do
        {
                middle = floor((left + right) / 2);
-               if(w(substring(theText, 0, middle), theSize) <= maxWidth)
-                       left = middle;
+               if(colors)
+                       ofs = skipIncompleteTag(theText, middle, len);
+               if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth)
+                       left = middle + ofs;
                else
                        right = middle;
        }
        while(left < right - 1);
 
-       if(w("^7", theSize) == 0) // detect color codes support in the width function
-       {
-               // NOTE: when color codes are involved, this binary search is,
-               // mathematically, BROKEN. However, it is obviously guaranteed to
-               // terminate, as the range still halves each time - but nevertheless, it is
-               // guaranteed that it finds ONE valid cutoff place (where "left" is in
-               // range, and "right" is outside).
-
-               // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
-               // and decrease left on the basis of the chars detected of the truncated tag
-               // Even if the ^xrgb tag is not complete/correct, left is decreased
-               // (sometimes too much but with a correct result)
-               // it fixes also ^[0-9]
-               while(left >= 1 && substring(theText, left-1, 1) == "^")
-                       left-=1;
-
-               if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
-                       left-=2;
-               else if (left >= 3 && substring(theText, left-3, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-1);
-                               if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
-                                       left-=3;
-                       }
-               else if (left >= 4 && substring(theText, left-4, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-2);
-                               if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
-                               {
-                                       ch = str2chr(theText, left-1);
-                                       if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
-                                               left-=4;
-                               }
-                       }
-       }
-
        return left;
 }
 
@@ -734,57 +756,25 @@ float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_
        if(w(theText) <= maxWidth)
                return strlen(theText); // yeah!
 
+       bool colors = (w("^7") == 0);
+
        // binary search for right place to cut string
-       float ch;
-       float left, right, middle; // this always works
+       int len, left, right, middle;
        left = 0;
-       right = strlen(theText); // this always fails
+       right = len = strlen(theText);
+       int ofs = 0;
        do
        {
                middle = floor((left + right) / 2);
-               if(w(substring(theText, 0, middle)) <= maxWidth)
-                       left = middle;
+               if(colors)
+                       ofs = skipIncompleteTag(theText, middle, len);
+               if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
+                       left = middle + ofs;
                else
                        right = middle;
        }
        while(left < right - 1);
 
-       if(w("^7") == 0) // detect color codes support in the width function
-       {
-               // NOTE: when color codes are involved, this binary search is,
-               // mathematically, BROKEN. However, it is obviously guaranteed to
-               // terminate, as the range still halves each time - but nevertheless, it is
-               // guaranteed that it finds ONE valid cutoff place (where "left" is in
-               // range, and "right" is outside).
-
-               // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
-               // and decrease left on the basis of the chars detected of the truncated tag
-               // Even if the ^xrgb tag is not complete/correct, left is decreased
-               // (sometimes too much but with a correct result)
-               // it fixes also ^[0-9]
-               while(left >= 1 && substring(theText, left-1, 1) == "^")
-                       left-=1;
-
-               if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
-                       left-=2;
-               else if (left >= 3 && substring(theText, left-3, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-1);
-                               if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
-                                       left-=3;
-                       }
-               else if (left >= 4 && substring(theText, left-4, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-2);
-                               if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
-                               {
-                                       ch = str2chr(theText, left-1);
-                                       if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
-                                               left-=4;
-                               }
-                       }
-       }
-
        return left;
 }
 
@@ -808,14 +798,14 @@ string find_last_color_code(string s)
                if (carets & 1)
                {
                        if(i+1 <= len)
-                       if(strstrofs("0123456789", substring(s, i+1, 1), 0) >= 0)
+                       if(IS_DIGIT(substring(s, i+1, 1)))
                                return substring(s, i, 2);
 
                        if(i+4 <= len)
                        if(substring(s, i+1, 1) == "x")
-                       if(strstrofs("0123456789abcdefABCDEF", substring(s, i+2, 1), 0) >= 0)
-                       if(strstrofs("0123456789abcdefABCDEF", substring(s, i+3, 1), 0) >= 0)
-                       if(strstrofs("0123456789abcdefABCDEF", substring(s, i+4, 1), 0) >= 0)
+                       if(IS_HEXDIGIT(substring(s, i + 2, 1)))
+                       if(IS_HEXDIGIT(substring(s, i + 3, 1)))
+                       if(IS_HEXDIGIT(substring(s, i + 4, 1)))
                                return substring(s, i, 5);
                }
                i -= carets; // this also skips one char before the carets
@@ -932,7 +922,7 @@ string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_
                return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
 }
 
-float isGametypeInFilter(float gt, float tp, float ts, string pattern)
+float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
 {
        string subpattern, subpattern2, subpattern3, subpattern4;
        subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
@@ -1083,26 +1073,25 @@ vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_styl
 
 float compressShotOrigin(vector v)
 {
-       float x, y, z;
-       x = rint(v.x * 2);
-       y = rint(v.y * 4) + 128;
-       z = rint(v.z * 4) + 128;
-       if(x > 255 || x < 0)
+       float rx = rint(v.x * 2);
+       float ry = rint(v.y * 4) + 128;
+       float rz = rint(v.z * 4) + 128;
+       if(rx > 255 || rx < 0)
        {
                LOG_INFO("shot origin ", vtos(v), " x out of bounds\n");
-               x = bound(0, x, 255);
+               rx = bound(0, rx, 255);
        }
-       if(y > 255 || y < 0)
+       if(ry > 255 || ry < 0)
        {
                LOG_INFO("shot origin ", vtos(v), " y out of bounds\n");
-               y = bound(0, y, 255);
+               ry = bound(0, ry, 255);
        }
-       if(z > 255 || z < 0)
+       if(rz > 255 || rz < 0)
        {
                LOG_INFO("shot origin ", vtos(v), " z out of bounds\n");
-               z = bound(0, z, 255);
+               rz = bound(0, rz, 255);
        }
-       return x * 0x10000 + y * 0x100 + z;
+       return rx * 0x10000 + ry * 0x100 + rz;
 }
 vector decompressShotOrigin(int f)
 {
@@ -1113,7 +1102,7 @@ vector decompressShotOrigin(int f)
        return v;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
 {
        // NOTE: we'll always choose the SMALLER value...
@@ -1227,8 +1216,9 @@ float get_model_parameters(string m, float sk)
                get_model_parameters_bone_aimweight[i] = 0;
        }
        get_model_parameters_fixbone = 0;
+       get_model_parameters_hidden = false;
 
-#ifndef MENUQC
+#ifdef GAMEQC
        MUTATOR_CALLHOOK(ClearModelParams);
 #endif
 
@@ -1293,7 +1283,7 @@ float get_model_parameters(string m, float sk)
                        get_model_parameters_bone_upperbody = s;
                if(c == "bone_weapon")
                        get_model_parameters_bone_weapon = s;
-       #ifndef MENUQC
+       #ifdef GAMEQC
                MUTATOR_CALLHOOK(GetModelParams, c, s);
        #endif
                for(int i = 0; i < MAX_AIM_BONES; ++i)
@@ -1304,6 +1294,8 @@ float get_model_parameters(string m, float sk)
                        }
                if(c == "fixbone")
                        get_model_parameters_fixbone = stof(s);
+               if(c == "hidden")
+                       get_model_parameters_hidden = stob(s);
        }
 
        while((s = fgets(fh)))
@@ -1386,7 +1378,7 @@ void m_shutdown()
        cvar_settemp_restore(); // this must be done LAST, but in any case
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 .float skeleton_bones_index;
 void Skeleton_SetBones(entity e)
 {
@@ -1464,7 +1456,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
                queue_start.FindConnectedComponent_processing = 0;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector animfixfps(entity e, vector a, vector b)
 {
        // multi-frame anim: keep as-is
@@ -1483,7 +1475,7 @@ vector animfixfps(entity e, vector a, vector b)
 }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 Notification Announcer_PickNumber(int type, int num)
 {
     return = NULL;
@@ -1595,7 +1587,7 @@ Notification Announcer_PickNumber(int type, int num)
 }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
 {
        switch(nativecontents)
index fa17a06a328af4414350a1a9a29632f5114cd34b..05e4a4ea2736b4e5c8e92a9859e58ae870391c7b 100644 (file)
@@ -1,19 +1,19 @@
-#ifndef COMMON_UTIL_H
-#define COMMON_UTIL_H
+#pragma once
 
-#ifndef MENUQC
+#ifdef GAMEQC
 
 vector real_origin(entity ent);
 #endif
 
+IntrusiveList g_saved_cvars;
+STATIC_INIT(g_saved_cvars) { g_saved_cvars = IL_NEW(); }
+
 // this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline
 // NOTE: s IS allowed to be a tempstring
 string wordwrap(string s, float l);
-#ifndef MENUQC
-#ifndef CSQC
+#ifdef SVQC
 void wordwrap_sprint(entity to, string s, float l);
 #endif
-#endif
 void wordwrap_cb(string s, float l, void(string) callback);
 
 #ifndef SVQC
@@ -64,11 +64,10 @@ const float TIME_FACTOR = 100;
 
 string ScoreString(float vflags, float value);
 
-void compressShortVector_init();
 vector decompressShortVector(float data);
 float compressShortVector(vector vec);
 
-#ifndef MENUQC
+#ifdef GAMEQC
 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz);
 #endif
 
@@ -81,7 +80,7 @@ float cvar_value_issafe(string s);
 float cvar_settemp(string pKey, string pValue);
 float cvar_settemp_restore();
 
-#ifndef MENUQC
+#ifdef GAMEQC
 // modes: 0 = trust q3map2 (_mini images)
 //        1 = trust tracebox (_radar images)
 // in both modes, mapinfo's "size" overrides
@@ -104,7 +103,7 @@ float almost_equals(float a, float b);
 float almost_in_bounds(float a, float b, float c);
 
 float power2of(float e);
-float log2of(float x);
+float log2of(float e);
 
 vector rgb_to_hsl(vector rgb);
 vector hsl_to_rgb(vector hsl);
@@ -126,7 +125,7 @@ string getWrappedLine_remaining;
 string getWrappedLine(float w, vector size, textLengthUpToWidth_widthFunction_t tw);
 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw);
 
-float isGametypeInFilter(float gt, float tp, float ts, string pattern);
+float isGametypeInFilter(entity gt, float tp, float ts, string pattern);
 
 string swapwords(string str, float i, float j);
 string shufflewords(string str);
@@ -150,14 +149,14 @@ string rankings_reply, ladder_reply, lsmaps_reply, maplist_reply, monsterlist_re
 string records_reply[10];
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype); // returns vector: maxdamage, armorideal, 1 if fully armored
 vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage); // returns vector: take, save, 0
 #endif
 
 string getcurrentmod();
 
-#ifndef MENUQC
+#ifdef GAMEQC
 #ifdef CSQC
 int ReadInt24_t();
 #else
@@ -182,6 +181,7 @@ float get_model_parameters_species;
 string get_model_parameters_sex;
 float get_model_parameters_weight;
 float get_model_parameters_age;
+bool get_model_parameters_hidden;
 string get_model_parameters_description;
 string get_model_parameters_bone_upperbody;
 string get_model_parameters_bone_weapon;
@@ -192,7 +192,7 @@ float get_model_parameters_fixbone;
 string get_model_parameters_desc;
 float get_model_parameters(string mod, float skn); // call with string_null to clear; skin -1 means mod is the filename of the txt file and is to be split
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector NearestPointOnBox(entity box, vector org);
 #endif
 
@@ -207,7 +207,7 @@ const float XENCODE_LEN = 5;
 string xencode(float f);
 float xdecode(string s);
 
-#ifndef MENUQC
+#ifdef GAMEQC
 string strtolower(string s);
 #endif
 
@@ -216,7 +216,7 @@ string MakeConsoleSafe(string input);
 // generic shutdown handler
 void Shutdown();
 
-#ifndef MENUQC
+#ifdef GAMEQC
 .float skeleton_bones;
 void Skeleton_SetBones(entity e);
 // loops through the tags of model v using counter tagnum
@@ -233,15 +233,12 @@ float ReadApproxPastTime();
 void execute_next_frame();
 void queue_to_execute_next_frame(string s);
 
-// for marking written-to values as unused where it's a good idea to do this
-noref float unused_float;
-
 // a function f with:
 // f(0) = 0
 // f(1) = 1
 // f'(0) = startspeedfactor
 // f'(1) = endspeedfactor
-float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x);
+float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float spd);
 
 // checks whether f'(x) = 0 anywhere from 0 to 1
 // because if this is the case, the function is not usable for platforms
@@ -261,7 +258,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
 
 string CCR(string input);
 
-#ifndef MENUQC
+#ifdef GAMEQC
        #ifdef CSQC
                #define GENTLE (autocvar_cl_gentle || autocvar_cl_gentle_messages)
        #else
@@ -270,11 +267,11 @@ string CCR(string input);
        #define normal_or_gentle(normal, gentle) (GENTLE ? ((gentle != "") ? gentle : normal) : normal)
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector animfixfps(entity e, vector a, vector b);
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 const float CNT_NORMAL = 1;
 const float CNT_GAMESTART = 2;
 const float CNT_IDLE = 3;
@@ -284,7 +281,7 @@ const float CNT_ROUNDSTART = 6;
 entity Announcer_PickNumber(float type, float num);
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents);
 int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents);
 #endif
@@ -297,4 +294,3 @@ vector bezier_quadratic_getderivative(vector a, vector p, vector b, float t);
 
 // Returns the correct difference between two always increasing numbers
 #define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from)
-#endif
diff --git a/qcsrc/common/vehicles/_all.inc b/qcsrc/common/vehicles/_all.inc
new file mode 100644 (file)
index 0000000..8bc63f7
--- /dev/null
@@ -0,0 +1,2 @@
+#include "_all.qh"
+#include "_mod.inc"
diff --git a/qcsrc/common/vehicles/_all.qh b/qcsrc/common/vehicles/_all.qh
new file mode 100644 (file)
index 0000000..947026d
--- /dev/null
@@ -0,0 +1,2 @@
+#pragma once
+#include "_mod.qh"
index 269858f8475ac67cf427be7290191c01c365a8ca..ed26659daa5c9ed37cbb384c8d9a2823e73d6c05 100644 (file)
@@ -1,4 +1,9 @@
 // generated file; do not modify
 #include <common/vehicles/all.qc>
-#include <common/vehicles/cl_vehicles.qc>
-#include <common/vehicles/sv_vehicles.qc>
+#include <common/vehicles/vehicles.qc>
+#ifdef CSQC
+    #include <common/vehicles/cl_vehicles.qc>
+#endif
+#ifdef SVQC
+    #include <common/vehicles/sv_vehicles.qc>
+#endif
index d21e829f7a81cc10c07d372ae501d09313e293f8..4892b0f317baad43e5bc15d6e8fef0d91931f2ff 100644 (file)
@@ -1,4 +1,9 @@
 // generated file; do not modify
 #include <common/vehicles/all.qh>
-#include <common/vehicles/cl_vehicles.qh>
-#include <common/vehicles/sv_vehicles.qh>
+#include <common/vehicles/vehicles.qh>
+#ifdef CSQC
+    #include <common/vehicles/cl_vehicles.qh>
+#endif
+#ifdef SVQC
+    #include <common/vehicles/sv_vehicles.qh>
+#endif
index 251df22839e750646abef244455aa63d7e846df5..4aef11cad56ed35fc49ca0b037842aceb861a74b 100644 (file)
@@ -1,8 +1,7 @@
+#include "all.qh"
 #ifndef VEHICLES_ALL_C
 #define VEHICLES_ALL_C
 
-#include "all.qh"
-
 REGISTER_NET_LINKED(ENT_CLIENT_AUXILIARYXHAIR)
 
 #if defined(SVQC)
index b58389d4abb7955feb7c3c80934583fadd4f6f1b..158492f661e7e1326cc248283c3b4841bb64c76a 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VEHICLES_ALL_H
-#define VEHICLES_ALL_H
+#pragma once
 
 #include "vehicle.qh"
 
@@ -22,5 +21,3 @@ const int VEH_FIRST = 1;
 REGISTER_VEHICLE(Null, NEW(Vehicle));
 
 #include "vehicle/_mod.inc"
-
-#endif
index 57f5990bb77a3915f9296c4f530074dad7485af8..afb48c918b89f74e353d3c2f624ecfafdb69e65d 100644 (file)
@@ -1,3 +1,4 @@
+#include "cl_vehicles.qh"
 const string vCROSS_BURST = "gfx/vehicles/crosshair_burst.tga";
 const string vCROSS_DROP  = "gfx/vehicles/crosshair_drop.tga";
 const string vCROSS_GUIDE = "gfx/vehicles/crosshair_guide.tga";
@@ -33,7 +34,7 @@ void AuxiliaryXhair_Draw2D(entity this)
                return;
 
        vector size = draw_getimagesize(this.axh_image) * autocvar_cl_vehicles_crosshair_size;
-       vector pos = project_3d_to_2d(this.move_origin) - 0.5 * size;
+       vector pos = project_3d_to_2d(this.origin) - 0.5 * size;
 
        if (!(pos.z < 0 || pos.x < 0 || pos.y < 0 || pos.x > vid_conwidth || pos.y > vid_conheight))
        {
@@ -48,12 +49,14 @@ void AuxiliaryXhair_Draw2D(entity this)
 
 NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew)
 {
+       int sf = ReadByte();
+
        int axh_id      = bound(0, ReadByte(), MAX_AXH);
-       entity axh              = AuxiliaryXhair[axh_id];
+       entity axh = AuxiliaryXhair[axh_id];
 
-       if(axh == NULL || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
+       if(axh == NULL || wasfreed(axh))
        {
-               axh                                     = spawn();
+               axh                                     = new(auxiliary_crosshair);
                axh.draw2d                      = func_null;
                axh.drawmask            = MASK_NORMAL;
                axh.axh_drawflag        = DRAWFLAG_ADDITIVE;
@@ -61,14 +64,23 @@ NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew)
                axh.axh_image           = vCROSS_HINT;
                axh.alpha                       = 1;
                AuxiliaryXhair[axh_id] = axh;
+               IL_PUSH(g_drawables_2d, axh);
+       }
+
+       if(sf & 2)
+       {
+               axh.origin_x = ReadCoord();
+               axh.origin_y = ReadCoord();
+               axh.origin_z = ReadCoord();
+       }
+
+       if(sf & 4)
+       {
+               axh.colormod_x = ReadByte() / 255;
+               axh.colormod_y = ReadByte() / 255;
+               axh.colormod_z = ReadByte() / 255;
        }
 
-       axh.move_origin_x       = ReadCoord();
-       axh.move_origin_y       = ReadCoord();
-       axh.move_origin_z       = ReadCoord();
-       axh.colormod_x          = ReadByte() / 255;
-       axh.colormod_y          = ReadByte() / 255;
-       axh.colormod_z          = ReadByte() / 255;
        axh.cnt                         = time;
        axh.draw2d                      = AuxiliaryXhair_Draw2D;
        return true;
@@ -80,10 +92,22 @@ NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew)
        return = true;
 
        // hud_id == 0 means we exited a vehicle, so stop alarm sound/s
-       if(hud_id == 0)
+       // note: HUD_NORMAL is set to 0 currently too, but we'll check both just in case
+       if(hud_id == 0 || hud_id == HUD_NORMAL)
        {
                sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASEVOICE, ATTEN_NONE);
                sound(this, CH_PAIN_SINGLE, SND_Null, VOL_BASEVOICE, ATTEN_NONE);
+
+               for(int i = 0; i < MAX_AXH; ++i)
+               {
+                       entity axh = AuxiliaryXhair[i];
+
+                       if(axh != NULL && !wasfreed(axh))
+                       {
+                               AuxiliaryXhair[i] = NULL;
+                               delete(axh);
+                       }
+               }
                return;
        }
 
@@ -93,7 +117,7 @@ NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew)
                entity axh = AuxiliaryXhair[i];
 
                if(axh != NULL && !wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
-                       remove(axh);
+                       delete(axh);
 
                axh              = spawn();
                axh.draw2d       = func_null;
@@ -103,6 +127,7 @@ NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew)
                axh.axh_image    = vCROSS_HINT;
                axh.alpha        = 1;
                AuxiliaryXhair[i] = axh;
+               IL_PUSH(g_drawables_2d, axh);
        }
 
        if(hud_id == HUD_BUMBLEBEE_GUN)
@@ -163,7 +188,7 @@ void Vehicles_drawHUD(
                tmpPos.x = vehicleHud_Pos.x + vehicleHud_Size.x * (96/256) - tmpSize.x;
                tmpPos.y = vehicleHud_Pos.y;
                tmpSize = '1 1 1' * hud_fontsize;
-               drawstring(tmpPos, sprintf(_("Press %s"), getcommandkey("dropweapon", "dropweapon")), tmpSize, '1 0 0' + '0 1 1' * tmpblinkValue, hudAlpha, DRAWFLAG_NORMAL);
+               drawstring(tmpPos, sprintf(_("Press %s"), getcommandkey(_("drop weapon"), "dropweapon")), tmpSize, '1 0 0' + '0 1 1' * tmpblinkValue, hudAlpha, DRAWFLAG_NORMAL);
        }
 
        // Model
index 5293030c19fe9ecda1aad3c8e733edabc29eda72..327074b98641e72631bbb3089ecc3829b008dca5 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CL_VEHICLES_H
-#define CL_VEHICLES_H
+#pragma once
 
 vector vehicleHud_Size;
 vector vehicleHud_Pos;
@@ -8,5 +7,3 @@ void RaptorCBShellfragDraw(entity this);
 void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang);
 
 #define weapon2mode STAT(VEHICLESTAT_W2MODE)
-
-#endif
index a797f84c8db90877ddaf48b6dc8ad8c9b92c0f46..9c8d05ce4e0fef38a8ecd06999c923f290d69804 100644 (file)
@@ -1,83 +1,62 @@
 #include "sv_vehicles.qh"
 
-#if 0
-bool vehicle_send(entity to, int sf)
+bool SendAuxiliaryXhair(entity this, entity to, int sf)
 {
-       WriteByte(MSG_ENTITY, ENT_CLIENT_VEHICLE);
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
        WriteByte(MSG_ENTITY, sf);
 
-       if(sf & VSF_SPAWN)
-       {
-               WriteByte(MSG_ENTITY, this.vehicleid);
-       }
-
-       if(sf & VSF_SETUP)
-       {
-               // send stuff?
-       }
-
-       if(sf & VSF_ENTER)
-       {
-               // player handles the .vehicle stuff, we need only set ourselves up for driving
-
-               // send stuff?
-       }
+       WriteByte(MSG_ENTITY, this.cnt);
 
-       if(sf & VSF_EXIT)
+       if(sf & 2)
        {
-               // senf stuff?
+               WriteCoord(MSG_ENTITY, this.origin_x);
+               WriteCoord(MSG_ENTITY, this.origin_y);
+               WriteCoord(MSG_ENTITY, this.origin_z);
        }
 
-       if(sf & VSF_PRECACHE)
+       if(sf & 4)
        {
-               // send stuff?!
+               WriteByte(MSG_ENTITY, rint(this.colormod_x * 255));
+               WriteByte(MSG_ENTITY, rint(this.colormod_y * 255));
+               WriteByte(MSG_ENTITY, rint(this.colormod_z * 255));
        }
 
        return true;
 }
-#endif
-
-bool SendAuxiliaryXhair(entity this, entity to, int sf)
-{
 
-       WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
-
-       WriteByte(MSG_ENTITY, this.cnt);
-
-       WriteCoord(MSG_ENTITY, this.origin_x);
-       WriteCoord(MSG_ENTITY, this.origin_y);
-       WriteCoord(MSG_ENTITY, this.origin_z);
-
-       WriteByte(MSG_ENTITY, rint(this.colormod_x * 255));
-       WriteByte(MSG_ENTITY, rint(this.colormod_y * 255));
-       WriteByte(MSG_ENTITY, rint(this.colormod_z * 255));
-
-       return true;
-}
+.vector axh_prevorigin;
+.vector axh_prevcolors;
 
 void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, int axh_id)
 {
        if(!IS_REAL_CLIENT(own))
                return;
 
-       entity axh;
-
        axh_id = bound(0, axh_id, MAX_AXH);
-       axh = own.(AuxiliaryXhair[axh_id]);
+       entity axh = own.(AuxiliaryXhair[axh_id]);
 
-       if(axh == NULL || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
+       if(axh == NULL || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist? Mario: because of sloppy code like this)
        {
-               axh                                      = spawn();
-               axh.cnt                          = axh_id;
-               axh.drawonlytoclient    = own;
-               axh.owner                          = own;
+               axh = new(auxiliary_xhair);
+               axh.cnt = axh_id;
+               axh.drawonlytoclient = own;
+               axh.owner = own;
                Net_LinkEntity(axh, false, 0, SendAuxiliaryXhair);
        }
 
-       setorigin(axh, loc);
-       axh.colormod                    = clr;
-       axh.SendFlags              = 0x01;
-       own.(AuxiliaryXhair[axh_id]) = axh;
+       if(loc != axh.axh_prevorigin)
+       {
+               setorigin(axh, loc);
+               axh.SendFlags |= 2;
+       }
+
+       if(clr != axh.axh_prevcolors)
+       {
+               axh.colormod = clr;
+               axh.SendFlags |= 4;
+       }
+
+       own.(AuxiliaryXhair[axh_id]) = axh; // set it anyway...?
 }
 
 void CSQCVehicleSetup(entity own, int vehicle_id)
@@ -164,6 +143,12 @@ void vehicles_locktarget(entity this, float incr, float decr, float _lock_time)
        }
 }
 
+float vehicle_altitude(entity this, float amax)
+{
+       tracebox(this.origin, this.mins, this.maxs, this.origin - ('0 0 1' * amax), MOVE_WORLDONLY, this);
+       return vlen(this.origin - trace_endpos);
+}
+
 vector vehicles_force_fromtag_hover(entity this, string tag_name, float spring_length, float max_power)
 {
        force_fromtag_origin = gettaginfo(this, gettagindex(this, tag_name));
@@ -213,28 +198,33 @@ void vehicles_projectile_damage(entity this, entity inflictor, entity attacker,
        }
 }
 
-void vehicles_projectile_explode(entity this)
+void vehicles_projectile_explode(entity this, entity toucher)
 {
-       if(this.owner && other != NULL)
+       if(this.owner && toucher != NULL)
        {
-               if(other == this.owner.vehicle)
+               if(toucher == this.owner.vehicle)
                        return;
 
-               if(other == this.owner.vehicle.tur_head)
+               if(toucher == this.owner.vehicle.tur_head)
                        return;
        }
 
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        this.event_damage = func_null;
-       RadiusDamage (this, this.realowner, this.shot_dmg, 0, this.shot_radius, this, NULL, this.shot_force, this.totalfrags, other);
+       RadiusDamage (this, this.realowner, this.shot_dmg, 0, this.shot_radius, this, NULL, this.shot_force, this.totalfrags, toucher);
 
-       remove (this);
+       delete (this);
+}
+
+void vehicles_projectile_explode_think(entity this)
+{
+       vehicles_projectile_explode(this, NULL);
 }
 
 void vehicles_projectile_explode_use(entity this, entity actor, entity trigger)
 {
-       vehicles_projectile_explode(this);
+       vehicles_projectile_explode(this, trigger);
 }
 
 entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound,
@@ -256,8 +246,10 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound,
        proj.shot_force    = _force;
        proj.totalfrags    = _deahtype;
        proj.solid                      = SOLID_BBOX;
-       proj.movetype            = MOVETYPE_FLYMISSILE;
-       proj.flags                      = FL_PROJECTILE;
+       set_movetype(proj, MOVETYPE_FLYMISSILE);
+       proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.bot_dodge          = true;
        proj.bot_dodgerating  = _dmg;
        proj.velocity            = _vel;
@@ -275,7 +267,7 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound,
                proj.health                = _health;
        }
        else
-               proj.flags                 = FL_PROJECTILE | FL_NOTARGET;
+               proj.flags |= FL_NOTARGET;
 
        if(_mzlsound != SND_Null)
                sound (this, CH_WEAPON_A, _mzlsound, VOL_BASE, ATTEN_NORM);
@@ -295,14 +287,19 @@ void vehicles_gib_explode(entity this)
        sound (this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
        Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (this.origin + '0 0 100'), '0 0 0', 1);
        Send_Effect(EFFECT_EXPLOSION_SMALL, this.wp00.origin + '0 0 64', '0 0 0', 1);
-       remove(this);
+       delete(this);
+}
+
+void vehicles_gib_touch(entity this, entity toucher)
+{
+       vehicles_gib_explode(this);
 }
 
 void vehicles_gib_think(entity this)
 {
        this.alpha -= 0.1;
        if(this.cnt >= time)
-               remove(this);
+               delete(this);
        else
                this.nextthink = time + 0.1;
 }
@@ -314,7 +311,7 @@ entity vehicle_tossgib(entity this, entity _template, vector _vel, string _tag,
        vector org = gettaginfo(this, gettagindex(this, _tag));
        setorigin(_gib, org);
        _gib.velocity = _vel;
-       _gib.movetype = MOVETYPE_TOSS;
+       set_movetype(_gib, MOVETYPE_TOSS);
        _gib.solid = SOLID_CORPSE;
        _gib.colormod = '-0.5 -0.5 -0.5';
        _gib.effects = EF_LOWPRECISION;
@@ -327,7 +324,7 @@ entity vehicle_tossgib(entity this, entity _template, vector _vel, string _tag,
        {
                setthink(_gib, vehicles_gib_explode);
                _gib.nextthink = time + random() * _explode;
-               settouch(_gib, vehicles_gib_explode);
+               settouch(_gib, vehicles_gib_touch);
        }
        else
        {
@@ -343,7 +340,7 @@ bool vehicle_addplayerslot( entity _owner,
                                                                entity _slot,
                                                                int _hud,
                                                                Model _hud_model,
-                                                               bool(entity) _framefunc,
+                                                               bool(entity,float) _framefunc,
                                                                void(entity,bool) _exitfunc, float(entity, entity) _enterfunc)
 {
        if(!(_owner.vehicle_flags & VHF_MULTISLOT))
@@ -436,21 +433,17 @@ void vehicles_reset_colors(entity this)
 
 void vehicles_clearreturn(entity veh)
 {
-       // Remove "return helper", if any.
-       for (entity ret = findchain(classname, "vehicle_return"); ret; ret = ret.chain)
+       // Remove "return helper" entities, if any.
+       IL_EACH(g_vehicle_returners, it.wp00 == veh,
        {
-               if(ret.wp00 == veh)
-               {
-                       ret.classname   = "";
-                       setthink(ret, SUB_Remove);
-                       ret.nextthink   = time + 0.1;
-
-                       if(ret.waypointsprite_attached)
-                               WaypointSprite_Kill(ret.waypointsprite_attached);
+               it.classname = "";
+               setthink(it, SUB_Remove);
+               it.nextthink = time + 0.1;
+               IL_REMOVE(g_vehicle_returners, it);
 
-                       return;
-               }
-       }
+               if(it.waypointsprite_attached)
+                       WaypointSprite_Kill(it.waypointsprite_attached);
+       });
 }
 
 void vehicles_spawn(entity this);
@@ -464,7 +457,7 @@ void vehicles_return(entity this)
        if(this.waypointsprite_attached)
                WaypointSprite_Kill(this.waypointsprite_attached);
 
-       remove(this);
+       delete(this);
 }
 
 void vehicles_showwp_goaway(entity this)
@@ -472,14 +465,11 @@ void vehicles_showwp_goaway(entity this)
        if(this.waypointsprite_attached)
                WaypointSprite_Kill(this.waypointsprite_attached);
 
-       remove(this);
-
+       delete(this);
 }
 
 void vehicles_showwp(entity this)
 {
-       vector rgb;
-
        entity ent = this;
 
        if(ent.cnt)
@@ -493,7 +483,6 @@ void vehicles_showwp(entity this)
                ent.nextthink  = time + 1;
 
                ent = spawn();
-               setmodel(ent, MDL_Null);
                ent.team = this.wp00.team;
                ent.wp00 = this.wp00;
                setorigin(ent, this.wp00.pos1);
@@ -502,6 +491,7 @@ void vehicles_showwp(entity this)
                setthink(ent, vehicles_showwp_goaway);
        }
 
+       vector rgb;
        if(teamplay && ent.team)
                rgb = Team_ColorRGB(ent.team);
        else
@@ -519,11 +509,10 @@ void vehicles_showwp(entity this)
 
 void vehicles_setreturn(entity veh)
 {
-       entity ret;
-
        vehicles_clearreturn(veh);
 
-       ret = new(vehicle_return);
+       entity ret = new(vehicle_return);
+       IL_PUSH(g_vehicle_returners, ret);
        ret.wp00           = veh;
        ret.team                = veh.team;
        setthink(ret, vehicles_showwp);
@@ -538,14 +527,12 @@ void vehicles_setreturn(entity veh)
                ret.nextthink   = min(time + veh.respawntime, time + veh.respawntime - 1);
        }
 
-       setmodel(ret, MDL_Null);
        setorigin(ret, veh.pos1 + '0 0 96');
-
 }
 
 void vehicle_use(entity this, entity actor, entity trigger)
 {
-       LOG_TRACE("vehicle ",this.netname, " used by ", actor.classname, "\n");
+       LOG_DEBUG("vehicle ", this.netname, " used by ", actor.classname);
 
        this.tur_head.team = actor.team;
 
@@ -556,7 +543,7 @@ void vehicle_use(entity this, entity actor, entity trigger)
 
        if(this.active == ACTIVE_ACTIVE && !IS_DEAD(this) && !gameover)
        {
-               LOG_TRACE("Respawning vehicle: ", this.netname, "\n");
+               LOG_DEBUG("Respawning vehicle: ", this.netname);
                if(this.effects & EF_NODRAW)
                {
                        setthink(this, vehicles_spawn);
@@ -634,20 +621,15 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag
        // WEAPONTODO
        if(DEATH_ISWEAPON(deathtype, WEP_VORTEX))
                damage *= autocvar_g_vehicles_vortex_damagerate;
-
-       if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
+       else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
                damage *= autocvar_g_vehicles_machinegun_damagerate;
-
-       if(DEATH_ISWEAPON(deathtype, WEP_RIFLE))
+       else if(DEATH_ISWEAPON(deathtype, WEP_RIFLE))
                damage *= autocvar_g_vehicles_rifle_damagerate;
-
-       if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
+       else if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
                damage *= autocvar_g_vehicles_vaporizer_damagerate;
-
-       if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
+       else if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
                damage *= autocvar_g_vehicles_tag_damagerate;
-
-       if(DEATH_WEAPONOF(deathtype) != WEP_Null)
+       else if(DEATH_WEAPONOF(deathtype) != WEP_Null)
                damage *= autocvar_g_vehicles_weapon_damagerate;
 
        this.enemy = attacker;
@@ -721,7 +703,7 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag
        }
 }
 
-float vehicles_crushable(entity e)
+bool vehicles_crushable(entity e)
 {
        if(IS_PLAYER(e) && time >= e.vehicle_enter_delay)
                return true;
@@ -757,49 +739,24 @@ void vehicles_impact(entity this, float _minspeed, float _speedfac, float _maxpa
 // vehicle enter/exit handling
 vector vehicles_findgoodexit(entity this, vector prefer_spot)
 {
-       //vector exitspot;
-       float mysize;
-
-       tracebox(this.origin + '0 0 32', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), prefer_spot, MOVE_NORMAL, this.owner);
+       // TODO: we actually want the player's size here
+       tracebox(this.origin + '0 0 32', PL_MIN_CONST, PL_MAX_CONST, prefer_spot, MOVE_NORMAL, this.owner);
        if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                return prefer_spot;
 
-       mysize = 1.5 * vlen(this.maxs - this.mins);
-       float i;
-       vector v, v2;
-       v2 = 0.5 * (this.absmin + this.absmax);
-       for(i = 0; i < 100; ++i)
+       float mysize = 1.5 * vlen(this.maxs - this.mins);
+       vector v;
+       vector v2 = 0.5 * (this.absmin + this.absmax);
+       for(int i = 0; i < autocvar_g_vehicles_exit_attempts; ++i)
        {
                v = randomvec();
                v_z = 0;
                v = v2 + normalize(v) * mysize;
-               tracebox(v2, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, MOVE_NORMAL, this.owner);
+               tracebox(v2, PL_MIN_CONST, PL_MAX_CONST, v, MOVE_NORMAL, this.owner);
                if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                        return v;
        }
 
-       /*
-       exitspot = (this.origin + '0 0 48') + v_forward * mysize;
-       tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner);
-       if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
-               return exitspot;
-
-       exitspot = (this.origin + '0 0 48') - v_forward * mysize;
-       tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner);
-       if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
-               return exitspot;
-
-       exitspot = (this.origin + '0 0 48') + v_right * mysize;
-       tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner);
-       if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
-               return exitspot;
-
-       exitspot = (this.origin + '0 0 48') - v_right * mysize;
-       tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner);
-       if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
-               return exitspot;
-       */
-
        return this.origin;
 }
 
@@ -809,7 +766,7 @@ void vehicles_exit(entity vehic, bool eject)
 
        if(vehicles_exit_running)
        {
-               LOG_TRACE("^1vehicles_exit already running! this is not good...\n");
+               LOG_TRACE("^1vehicles_exit already running! this is not good...");
                return;
        }
 
@@ -837,17 +794,17 @@ void vehicles_exit(entity vehic, bool eject)
                        WriteAngle(MSG_ONE, 0);
                }
 
-               setsize(player, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+               setsize(player, STAT(PL_MIN,player), STAT(PL_MAX, player));
 
                player.takedamage               = DAMAGE_AIM;
                player.solid                    = SOLID_SLIDEBOX;
-               player.movetype         = MOVETYPE_WALK;
+               set_movetype(player, MOVETYPE_WALK);
                player.effects             &= ~EF_NODRAW;
                player.teleportable     = TELEPORT_NORMAL;
                player.alpha                    = 1;
                player.PlayerPhysplug   = func_null;
                player.vehicle                  = NULL;
-               player.view_ofs         = STAT(PL_VIEW_OFS, NULL);
+               player.view_ofs         = STAT(PL_VIEW_OFS, player);
                player.event_damage     = PlayerDamage;
                player.hud                              = HUD_NORMAL;
                PS(player).m_switchweapon = vehic.m_switchweapon;
@@ -891,21 +848,21 @@ void vehicles_exit(entity vehic, bool eject)
        vehicles_exit_running = false;
 }
 
-void vehicles_touch(entity this)
+void vehicles_touch(entity this, entity toucher)
 {
-       if(MUTATOR_CALLHOOK(VehicleTouch, this, other))
+       if(MUTATOR_CALLHOOK(VehicleTouch, this, toucher))
                return;
 
        // Vehicle currently in use
        if(this.owner)
        {
                if(!forbidWeaponUse(this.owner))
-               if(other != NULL)
-               if((this.origin_z + this.maxs_z) > (other.origin_z))
-               if(vehicles_crushable(other))
+               if(toucher != NULL)
+               if((this.origin_z + this.maxs_z) > (toucher.origin_z))
+               if(vehicles_crushable(toucher))
                {
                        if(vdist(this.velocity, >=, 30))
-                               Damage(other, this, this.owner, autocvar_g_vehicles_crush_dmg, DEATH_VH_CRUSH.m_id, '0 0 0', normalize(other.origin - this.origin) * autocvar_g_vehicles_crush_force);
+                               Damage(toucher, this, this.owner, autocvar_g_vehicles_crush_dmg, DEATH_VH_CRUSH.m_id, '0 0 0', normalize(toucher.origin - this.origin) * autocvar_g_vehicles_crush_force);
 
                        return; // Dont do selfdamage when hitting "soft targets".
                }
@@ -921,7 +878,7 @@ void vehicles_touch(entity this)
        if(autocvar_g_vehicles_enter)
                return;
 
-       vehicles_enter(other, this);
+       vehicles_enter(toucher, this);
 }
 
 bool vehicle_impulse(entity this, int imp)
@@ -956,23 +913,20 @@ void vehicles_enter(entity pl, entity veh)
        || (pl.vehicle)
        ) { return; }
 
+       Vehicle info = Vehicles_from(veh.vehicleid);
+
        if(autocvar_g_vehicles_enter) // vehicle's touch function should handle this if entering via use key is disabled (TODO)
        if(veh.vehicle_flags & VHF_MULTISLOT)
-       if(veh.owner)
+       if(veh.owner && SAME_TEAM(pl, veh))
        {
-               if(!veh.gunner1)
-               if(time >= veh.gun1.phase)
-               if(veh.gun1.vehicle_enter)
-               if(veh.gun1.vehicle_enter(veh, pl))
-                       return;
-
-               if(!veh.gunner2)
-               if(time >= veh.gun2.phase)
-               if(veh.gun2.vehicle_enter)
-               if(veh.gun2.vehicle_enter(veh, pl))
-                       return;
+               // we don't need a return value or anything here
+               // if successful the owner check below will prevent anything weird
+               info.vr_gunner_enter(info, veh, pl);
        }
 
+       if(veh.owner)
+               return; // got here and didn't enter the gunner, return
+
        if(teamplay)
        if(veh.team)
        if(DIFF_TEAM(pl, veh))
@@ -1005,17 +959,17 @@ void vehicles_enter(entity pl, entity veh)
 
        veh.vehicle_hudmodel.viewmodelforclient = pl;
 
-       tracebox(pl.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), pl.origin, false, pl);
        pl.crouch = false;
-       pl.view_ofs = STAT(PL_VIEW_OFS, NULL);
-       setsize (pl, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+       pl.view_ofs = STAT(PL_VIEW_OFS, pl);
+       setsize (pl, STAT(PL_MIN, pl), STAT(PL_MAX, pl));
 
        veh.event_damage        = vehicles_damage;
        veh.nextthink           = 0;
        pl.angles                       = veh.angles;
        pl.takedamage           = DAMAGE_NO;
        pl.solid                        = SOLID_NOT;
-       pl.movetype                     = MOVETYPE_NOCLIP;
+       pl.disableclientprediction = 1; // physics is no longer run, so this won't be reset
+       set_movetype(pl, MOVETYPE_NOCLIP);
        pl.teleportable         = false;
        pl.alpha                        = -1;
        pl.event_damage         = func_null;
@@ -1073,7 +1027,6 @@ void vehicles_enter(entity pl, entity veh)
        MUTATOR_CALLHOOK(VehicleEnter, pl, veh);
 
        CSQCModel_UnlinkEntity(veh);
-       Vehicle info = Vehicles_from(veh.vehicleid);
        info.vr_enter(info, veh);
 
        antilag_clear(pl, CS(pl));
@@ -1081,7 +1034,7 @@ void vehicles_enter(entity pl, entity veh)
 
 void vehicles_think(entity this)
 {
-       this.nextthink = time;
+       this.nextthink = time + autocvar_g_vehicles_thinkrate;
 
        if(this.owner)
                this.owner.vehicle_weapon2mode = this.vehicle_weapon2mode;
@@ -1094,10 +1047,21 @@ void vehicles_think(entity this)
        CSQCMODEL_AUTOUPDATE(this);
 }
 
+void vehicles_reset(entity this)
+{
+       if(this.owner)
+               vehicles_exit(this, VHEF_RELEASE);
+
+       vehicles_clearreturn(this);
+
+       if(this.active != ACTIVE_NOT)
+               vehicles_spawn(this);
+}
+
 // initialization
 void vehicles_spawn(entity this)
 {
-       LOG_TRACE("Spawning vehicle: ", this.classname, "\n");
+       LOG_DEBUG("Spawning vehicle: ", this.classname);
 
        // disown & reset
        this.vehicle_hudmodel.viewmodelforclient = this;
@@ -1105,13 +1069,16 @@ void vehicles_spawn(entity this)
        this.owner                              = NULL;
        settouch(this, vehicles_touch);
        this.event_damage               = vehicles_damage;
+       this.reset                              = vehicles_reset;
        this.iscreature                 = true;
        this.teleportable               = false; // no teleporting for vehicles, too buggy
        this.damagedbycontents  = true;
-       this.movetype                   = MOVETYPE_WALK;
+       set_movetype(this, MOVETYPE_WALK);
        this.solid                              = SOLID_SLIDEBOX;
        this.takedamage                 = DAMAGE_AIM;
        this.deadflag                   = DEAD_NO;
+       if(!this.bot_attack)
+               IL_PUSH(g_bot_targets, this);
        this.bot_attack                 = true;
        this.flags                              = FL_NOTARGET;
        this.avelocity                  = '0 0 0';
@@ -1152,14 +1119,17 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop)
                return false;
 
        if(!this.tur_head)
+       {
                info.vr_precache(info);
+               IL_PUSH(g_vehicles, this);
+       }
 
        if(this.targetname && this.targetname != "")
        {
                this.vehicle_controller = find(NULL, target, this.targetname);
                if(!this.vehicle_controller)
                {
-                       bprint("^1WARNING: ^7Vehicle with invalid .targetname\n");
+                       LOG_DEBUG("^1WARNING: ^7Vehicle with invalid .targetname");
                        this.active = ACTIVE_ACTIVE;
                }
                else
@@ -1194,9 +1164,11 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop)
        this.tur_head.owner                     = this;
        this.takedamage                         = DAMAGE_NO;
        this.bot_attack                         = true;
+       IL_PUSH(g_bot_targets, this);
        this.iscreature                         = true;
        this.teleportable                       = false; // no teleporting for vehicles, too buggy
        this.damagedbycontents          = true;
+       IL_PUSH(g_damagedbycontents, this);
        this.vehicleid                          = info.vehicleid;
        this.PlayerPhysplug                     = info.PlayerPhysplug;
        this.event_damage                       = func_null;
@@ -1234,6 +1206,8 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop)
 
        setsize(this, info.mins, info.maxs);
 
+       info.vr_setup(info, this);
+
        if(!nodrop)
        {
                setorigin(this, this.origin);
@@ -1245,8 +1219,6 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop)
        this.pos2 = this.angles;
        this.tur_head.team = this.team;
 
-       info.vr_setup(info, this);
-
        if(this.active == ACTIVE_NOT)
                this.nextthink = 0; // wait until activated
        else if(autocvar_g_vehicles_delayspawn)
@@ -1254,7 +1226,7 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop)
        else
                this.nextthink = time + game_starttime;
 
-       if(MUTATOR_CALLHOOK(VehicleSpawn, this))
+       if(MUTATOR_CALLHOOK(VehicleInit, this))
                return false;
 
        return true;
index 8c53724b578525c50b0fd70337a53c42b8de624e..948427db33e0923c8c28af0ddbf61845a2369e32 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VEHICLES_DEF_H
-#define VEHICLES_DEF_H
+#pragma once
 #ifdef SVQC
 
 #include <common/turrets/sv_turrets.qh>
@@ -16,6 +15,8 @@ float autocvar_g_vehicles_crush_force = 50;
 bool autocvar_g_vehicles_delayspawn = true;
 float autocvar_g_vehicles_delayspawn_jitter = 10;
 float autocvar_g_vehicles_allow_bots;
+int autocvar_g_vehicles_exit_attempts = 25;
+float autocvar_g_vehicles_thinkrate = 0.1;
 
 AUTOCVAR(g_vehicles_teams, bool, true, "allow team specific vehicles");
 float autocvar_g_vehicles_teleportable;
@@ -105,6 +106,9 @@ void vehicles_exit(entity vehic, int eject);
 bool vehicle_initialize(entity this, Vehicle info, float nodrop);
 bool vehicle_impulse(entity this, int imp);
 bool vehicles_crushable(entity e);
+float vehicle_altitude(entity this, float amax);
+
+IntrusiveList g_vehicle_returners;
+STATIC_INIT(g_vehicle_returners) { g_vehicle_returners = IL_NEW(); }
 
-#endif
 #endif
index 45cdacf39828b9d97b63ab6c7115adddc36db201..cb4225a27df3030664b3b0e624de795433f1be23 100644 (file)
@@ -1,40 +1,39 @@
-#ifndef VEHICLE_H
-#define VEHICLE_H
+#pragma once
 
 CLASS(Vehicle, Object)
-    ATTRIB(Vehicle, vehicleid, int, 0)
+    ATTRIB(Vehicle, vehicleid, int, 0);
     /** hud icon */
-    ATTRIB(Vehicle, m_icon, string, string_null)
+    ATTRIB(Vehicle, m_icon, string);
     /** short name */
-    ATTRIB(Vehicle, netname, string, "")
+    ATTRIB(Vehicle, netname, string, "");
     /** human readable name */
-    ATTRIB(Vehicle, vehicle_name, string, "Vehicle")
+    ATTRIB(Vehicle, vehicle_name, string, "Vehicle");
     /** full name of model */
-    ATTRIB(Vehicle, model, string, "")
+    ATTRIB(Vehicle, model, string, "");
     /** currently a copy of the model */
-    ATTRIB(Vehicle, mdl, string, "")
+    ATTRIB(Vehicle, mdl, string, "");
     /** full name of tur_head model */
-    ATTRIB(Vehicle, head_model, string, "")
+    ATTRIB(Vehicle, head_model, string, "");
     /** cockpit model */
-    ATTRIB(Vehicle, hud_model, string, "")
+    ATTRIB(Vehicle, hud_model, string, "");
     /** tur_head model tag */
-    ATTRIB(Vehicle, tag_head, string, string_null)
+    ATTRIB(Vehicle, tag_head, string);
     /** hud model tag */
-    ATTRIB(Vehicle, tag_hud, string, string_null)
+    ATTRIB(Vehicle, tag_hud, string);
     /** cockpit model tag */
-    ATTRIB(Vehicle, tag_view, string, string_null)
+    ATTRIB(Vehicle, tag_view, string);
     /** player physics mod */
-    ATTRIB(Vehicle, PlayerPhysplug, bool(entity), func_null)
+    ATTRIB(Vehicle, PlayerPhysplug, bool(entity,float));
     /**  */
-    ATTRIB(Vehicle, spawnflags, int, 0)
+    ATTRIB(Vehicle, spawnflags, int, 0);
     /** vehicle hitbox size */
-    ATTRIB(Vehicle, mins, vector, '-0 -0 -0')
+    ATTRIB(Vehicle, mins, vector, '-0 -0 -0');
     /** vehicle hitbox size */
-    ATTRIB(Vehicle, maxs, vector, '0 0 0')
+    ATTRIB(Vehicle, maxs, vector, '0 0 0');
     /** vehicle 3rd person view offset */
-    ATTRIB(Vehicle, view_ofs, vector, '0 0 0')
+    ATTRIB(Vehicle, view_ofs, vector, '0 0 0');
     /** vehicle 3rd person view distance */
-    ATTRIB(Vehicle, height, float, 0)
+    ATTRIB(Vehicle, height, float, 0);
 
     /** (BOTH) setup vehicle data */
     METHOD(Vehicle, vr_setup, void(Vehicle this, entity instance)) { }
@@ -46,6 +45,8 @@ CLASS(Vehicle, Object)
     METHOD(Vehicle, vr_precache, void(Vehicle this)) { }
     /** (SERVER) called when a player enters this vehicle */
     METHOD(Vehicle, vr_enter, void(Vehicle this, entity instance)) { }
+    /** (SERVER) called when a player enters this vehicle while occupied */
+    METHOD(Vehicle, vr_gunner_enter, void(Vehicle this, entity instance, entity actor)) { }
     /** (SERVER) called when the vehicle re-spawns */
     METHOD(Vehicle, vr_spawn, void(Vehicle this, entity instance)) { }
     /** (SERVER) called when a vehicle hits something */
@@ -57,22 +58,20 @@ CLASS(Vehicle, Object)
 ENDCLASS(Vehicle)
 
 // vehicle spawn flags (need them here for common registrations)
-const int VHF_ISVEHICLE                        = 2; /// Indicates vehicle
-const int VHF_HASSHIELD                        = 4; /// Vehicle has shileding
-const int VHF_SHIELDREGEN              = 8; /// Vehicles shield regenerates
-const int VHF_HEALTHREGEN              = 16; /// Vehicles health regenerates
-const int VHF_ENERGYREGEN              = 32; /// Vehicles energy regenerates
-const int VHF_DEATHEJECT               = 64; /// Vehicle ejects pilot upon fatal damage
-const int VHF_MOVE_GROUND              = 128; /// Vehicle moves on gound
-const int VHF_MOVE_HOVER               = 256; /// Vehicle hover close to gound
-const int VHF_MOVE_FLY                 = 512; /// Vehicle is airborn
-const int VHF_DMGSHAKE                 = 1024; /// Add random velocity each frame if health < 50%
-const int VHF_DMGROLL                  = 2048; /// Add random angles each frame if health < 50%
-const int VHF_DMGHEADROLL              = 4096; /// Add random head angles each frame if health < 50%
-const int VHF_MULTISLOT                        = 8192; /// Vehicle has multiple player slots
-const int VHF_PLAYERSLOT               = 16384; /// This ent is a player slot on a multi-person vehicle
+const int VHF_ISVEHICLE                        = BIT(1); /// Indicates vehicle
+const int VHF_HASSHIELD                        = BIT(2); /// Vehicle has shileding
+const int VHF_SHIELDREGEN              = BIT(3); /// Vehicles shield regenerates
+const int VHF_HEALTHREGEN              = BIT(4); /// Vehicles health regenerates
+const int VHF_ENERGYREGEN              = BIT(5); /// Vehicles energy regenerates
+const int VHF_DEATHEJECT               = BIT(6); /// Vehicle ejects pilot upon fatal damage
+const int VHF_MOVE_GROUND              = BIT(7); /// Vehicle moves on gound
+const int VHF_MOVE_HOVER               = BIT(8); /// Vehicle hover close to gound
+const int VHF_MOVE_FLY                 = BIT(9); /// Vehicle is airborn
+const int VHF_DMGSHAKE                 = BIT(10); /// Add random velocity each frame if health < 50%
+const int VHF_DMGROLL                  = BIT(11); /// Add random angles each frame if health < 50%
+const int VHF_DMGHEADROLL              = BIT(12); /// Add random head angles each frame if health < 50%
+const int VHF_MULTISLOT                        = BIT(13); /// Vehicle has multiple player slots
+const int VHF_PLAYERSLOT               = BIT(14); /// This ent is a player slot on a multi-person vehicle
 
 // fields:
 .entity tur_head;
-
-#endif
index 401483735ca409394d38a72173430a55262f8430..573e46aa77fa522973d97aca965f0d1388d7b4d4 100644 (file)
@@ -1,34 +1,5 @@
-#ifndef VEHICLE_BUMBLEBEE
-#define VEHICLE_BUMBLEBEE
 #include "bumblebee.qh"
 
-#include "bumblebee_weapons.qh"
-
-CLASS(Bumblebee, Vehicle)
-/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE);
-/* mins       */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130');
-/* maxs       */ ATTRIB(Bumblebee, maxs, vector, '230 130 130');
-/* view offset*/ ATTRIB(Bumblebee, view_ofs, vector, '0 0 300');
-/* view dist  */ ATTRIB(Bumblebee, height, float, 450);
-/* model         */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm");
-/* model         */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm");
-/* head_model */ ATTRIB(Bumblebee, head_model, string, "");
-/* hud_model  */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm");
-/* tags       */ ATTRIB(Bumblebee, tag_head, string, "");
-/* tags       */ ATTRIB(Bumblebee, tag_hud, string, "");
-/* tags       */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport");
-/* netname    */ ATTRIB(Bumblebee, netname, string, "bumblebee");
-/* fullname   */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee"));
-/* icon       */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble");
-ENDCLASS(Bumblebee)
-REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee));
-
-#ifndef MENUQC
-       MODEL(VEH_BUMBLEBEE_GUNCOCKPIT, "models/vehicles/wakizashi_cockpit.dpm");
-#endif
-
-#endif
-
 #ifdef IMPLEMENTATION
 
 const float BRG_SETUP = 2;
@@ -46,6 +17,7 @@ float autocvar_g_vehicle_bumblebee_turnspeed = 120;
 float autocvar_g_vehicle_bumblebee_pitchspeed = 60;
 float autocvar_g_vehicle_bumblebee_pitchlimit = 60;
 float autocvar_g_vehicle_bumblebee_friction = 0.5;
+bool autocvar_g_vehicle_bumblebee_swim = false;
 
 float autocvar_g_vehicle_bumblebee_energy = 500;
 float autocvar_g_vehicle_bumblebee_energy_regen = 50;
@@ -98,7 +70,7 @@ vector autocvar_g_vehicle_bumblebee_bouncepain = '1 100 200';
 
 bool autocvar_g_vehicle_bumblebee = true;
 
-bool bumblebee_gunner_frame(entity this)
+bool bumblebee_gunner_frame(entity this, float dt)
 {
        entity vehic = this.vehicle.owner;
        entity gun = this.vehicle;
@@ -138,7 +110,7 @@ bool bumblebee_gunner_frame(entity this)
                        gun.enemy = NULL;
 
                if(trace_ent)
-                       if(trace_ent.movetype)
+                       if(trace_ent.move_movetype)
                                if(trace_ent.takedamage)
                                        if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent))
                                        {
@@ -156,7 +128,7 @@ bool bumblebee_gunner_frame(entity this)
 
                vector vf = real_origin(gun.enemy);
                vector _vel = gun.enemy.velocity;
-               if(gun.enemy.movetype == MOVETYPE_WALK)
+               if(gun.enemy.move_movetype == MOVETYPE_WALK)
                        _vel.z *= 0.1;
 
 
@@ -212,11 +184,11 @@ vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity p
        //vector exitspot;
        float mysize;
 
-       tracebox(gunner.origin + '0 0 32', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), prefer_spot, MOVE_NORMAL, player);
+       tracebox(gunner.origin + '0 0 32', STAT(PL_MIN, player), STAT(PL_MAX, player), prefer_spot, MOVE_NORMAL, player);
        if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                return prefer_spot;
 
-       mysize = 1.5 * vlen(STAT(PL_MAX, NULL) - STAT(PL_MIN, NULL)); // can't use gunner's size, as they don't have a size
+       mysize = 1.5 * vlen(STAT(PL_MAX, player) - STAT(PL_MIN, player)); // can't use gunner's size, as they don't have a size
        float i;
        vector v, v2;
        v2 = 0.5 * (gunner.absmin + gunner.absmax);
@@ -225,7 +197,7 @@ vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity p
                v = randomvec();
                v_z = 0;
                v = v2 + normalize(v) * mysize;
-               tracebox(v2, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, MOVE_NORMAL, player);
+               tracebox(v2, STAT(PL_MIN, player), STAT(PL_MAX, player), v, MOVE_NORMAL, player);
                if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                        return v;
        }
@@ -252,15 +224,15 @@ void bumblebee_gunner_exit(entity this, int _exitflag)
        }
 
        CSQCVehicleSetup(player, HUD_NORMAL);
-       setsize(player, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+       setsize(player, STAT(PL_MIN, player), STAT(PL_MAX, player));
 
        player.takedamage     = DAMAGE_AIM;
        player.solid          = SOLID_SLIDEBOX;
-       player.movetype       = MOVETYPE_WALK;
+       set_movetype(player, MOVETYPE_WALK);
        player.effects       &= ~EF_NODRAW;
        player.alpha          = 1;
        player.PlayerPhysplug = func_null;
-       player.view_ofs       = STAT(PL_VIEW_OFS, NULL);
+       player.view_ofs       = STAT(PL_VIEW_OFS, player);
        player.event_damage   = PlayerDamage;
        player.hud            = HUD_NORMAL;
        player.teleportable       = TELEPORT_NORMAL;
@@ -297,27 +269,29 @@ bool bumblebee_gunner_enter(entity this, entity player)
        if(!vehic.gunner1 && !vehic.gunner2 && ((time >= vehic.gun1.phase) + (time >= vehic.gun2.phase)) == 2)
        {
                // we can have some fun
-               if(vlen2(real_origin(vehic.gun2) - player.origin) < vlen2(real_origin(vehic.gun1) - player.origin))
+               vector v1 = gettaginfo(vehic, gettagindex(vehic, "cannon_right"));
+               vector v2 = gettaginfo(vehic, gettagindex(vehic, "cannon_left"));
+               if(vlen2(player.origin - v1) < vlen2(player.origin - v2))
                {
-                       gunner = vehic.gun2;
-                       vehic.gunner2 = player;
+                       gunner = vehic.gun1;
+                       vehic.gunner1 = player;
                }
                else
                {
-                       gunner = vehic.gun1;
-                       vehic.gunner1 = player;
+                       gunner = vehic.gun2;
+                       vehic.gunner2 = player;
                }
        }
        else if(!vehic.gunner1 && time >= vehic.gun1.phase)     { gunner = vehic.gun1; vehic.gunner1 = player; }
        else if(!vehic.gunner2 && time >= vehic.gun2.phase)             { gunner = vehic.gun2; vehic.gunner2 = player; }
-       else { LOG_TRACE("Vehicle is full, fail\n"); return false; }
+       else { LOG_TRACE("Vehicle is full, fail"); return false; }
 
        player.vehicle                  = gunner;
        player.angles                   = vehic.angles;
        player.takedamage               = DAMAGE_NO;
        player.solid                    = SOLID_NOT;
        player.alpha                    = -1;
-       player.movetype                 = MOVETYPE_NOCLIP;
+       set_movetype(player, MOVETYPE_NOCLIP);
        player.event_damage     = func_null;
        player.view_ofs                 = '0 0 0';
        player.hud                              = gunner.hud;
@@ -328,7 +302,7 @@ bool bumblebee_gunner_enter(entity this, entity player)
        player.vehicle_reload1  = vehic.vehicle_reload1;
        player.vehicle_reload2  = vehic.vehicle_reload2;
        player.vehicle_energy   = vehic.vehicle_energy;
-       player.flags               &= ~FL_ONGROUND;
+       UNSET_ONGROUND(player);
 
        RemoveGrapplingHook(player);
 
@@ -369,58 +343,59 @@ bool vehicles_valid_pilot(entity this, entity toucher)
        return true;
 }
 
-void bumblebee_touch(entity this)
+void bumblebee_touch(entity this, entity toucher)
 {
        if(autocvar_g_vehicles_enter) { return; }
 
        if(this.gunner1 != NULL && this.gunner2 != NULL)
        {
-               vehicles_touch(this);
+               vehicles_touch(this, toucher);
                return;
        }
 
-       if(vehicles_valid_pilot(this, other))
+       if(vehicles_valid_pilot(this, toucher))
        {
                float phase_time = (time >= this.gun1.phase) + (time >= this.gun2.phase);
 
-               if(time >= other.vehicle_enter_delay && phase_time)
-               if(bumblebee_gunner_enter(this, other))
+               if(time >= toucher.vehicle_enter_delay && phase_time)
+               if(bumblebee_gunner_enter(this, toucher))
                        return;
        }
 
-       vehicles_touch(this);
+       vehicles_touch(this, toucher);
 }
 
-void bumblebee_regen(entity this)
+void bumblebee_regen(entity this, float dt)
 {
        if(this.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
                this.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
-                                                                          this.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
+                                                                          this.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * dt);
 
        if(this.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
                this.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
-                                                                          this.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
+                                                                          this.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * dt);
 
        if(this.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(this, this.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, true);
+               vehicles_regen(this, this.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, dt, true);
 
        if(this.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(this, this.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, false);
+               vehicles_regen(this, this.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, dt, false);
 
        if(this.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(this, this.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, false);
+               vehicles_regen(this, this.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, dt, false);
 
 }
 
-bool bumblebee_pilot_frame(entity this)
+bool bumblebee_pilot_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
-               vehic.velocity = '0 0 0';
-               vehic.avelocity = '0 0 0';
+               vehic.solid = SOLID_NOT;
+               vehic.takedamage = DAMAGE_NO;
+               set_movetype(vehic, MOVETYPE_NONE);
                return;
        }
 
@@ -432,7 +407,7 @@ bool bumblebee_pilot_frame(entity this)
                return;
        }
 
-       bumblebee_regen(vehic);
+       bumblebee_regen(vehic, dt);
 
        crosshair_trace(this);
 
@@ -483,7 +458,7 @@ bool bumblebee_pilot_frame(entity this)
                else if(this.movement.y > 0)
                        newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe;
                ftmp = newvel * v_right;
-               ftmp *= frametime * 0.1;
+               ftmp *= dt * 0.1;
                vehic.angles_z = bound(-15, vehic.angles.z + ftmp, 15);
        }
        else
@@ -498,7 +473,7 @@ bool bumblebee_pilot_frame(entity this)
        else if(PHYS_INPUT_BUTTON_JUMP(this))
                newvel +=  v_up * autocvar_g_vehicle_bumblebee_speed_up;
 
-       vehic.velocity  += newvel * frametime;
+       vehic.velocity  += newvel * dt;
        this.velocity = this.movement  = vehic.velocity;
 
 
@@ -508,7 +483,7 @@ bool bumblebee_pilot_frame(entity this)
                        vehic.tur_head.enemy = NULL;
 
                if(trace_ent)
-               if(trace_ent.movetype)
+               if(trace_ent.move_movetype)
                if(trace_ent.takedamage)
                if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent))
                {
@@ -539,7 +514,7 @@ bool bumblebee_pilot_frame(entity this)
                                          autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1,  autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides,  autocvar_g_vehicle_bumblebee_raygun_turnspeed);
 
        if(!forbidWeaponUse(this))
-       if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0))
+       if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME || autocvar_g_vehicle_bumblebee_raygun == 0))
        {
                vehic.gun3.enemy.realowner = this;
                vehic.gun3.enemy.effects &= ~EF_NODRAW;
@@ -553,8 +528,8 @@ bool bumblebee_pilot_frame(entity this)
                {
                        if(autocvar_g_vehicle_bumblebee_raygun)
                        {
-                               Damage(trace_ent, vehic, this, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC.m_id, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime);
-                               vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime;
+                               Damage(trace_ent, vehic, this, autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME, DEATH_GENERIC.m_id, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * PHYS_INPUT_FRAMETIME);
+                               vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * PHYS_INPUT_FRAMETIME;
                        }
                        else
                        {
@@ -562,28 +537,28 @@ bool bumblebee_pilot_frame(entity this)
                                        if((teamplay && trace_ent.team == this.team) || !teamplay)
                                        {
 
-                                               if(trace_ent.vehicle_flags & VHF_ISVEHICLE)
+                                               if(IS_VEHICLE(trace_ent))
                                                {
                                                        if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health)
-                                                               trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.max_health);
+                                                               trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * dt, trace_ent.tur_head.max_health);
 
                                                        if(autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health);
+                                                               trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health);
                                                }
                                                else if(IS_CLIENT(trace_ent))
                                                {
                                                        if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
+                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax);
 
                                                        if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps)
-                                                               trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * frametime, autocvar_g_vehicle_bumblebee_healgun_amax);
+                                                               trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax);
 
-                                                       trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
+                                                       trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax);
                                                }
                                                else if(IS_TURRET(trace_ent))
                                                {
                                                        if(trace_ent.health  <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health);
+                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health);
                                                        //else ..hmmm what? ammo?
 
                                                        trace_ent.SendFlags |= TNSF_STATUS;
@@ -629,8 +604,8 @@ void bumblebee_land(entity this)
 {
        float hgt;
 
-       hgt = raptor_altitude(this, 512);
-       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
+       hgt = vehicle_altitude(this, 512);
+       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * PHYS_INPUT_FRAMETIME);
        this.angles_x *= 0.95;
        this.angles_z *= 0.95;
 
@@ -658,7 +633,7 @@ void bumblebee_exit(entity this, int eject)
                this.nextthink  = time;
        }
 
-       this.movetype = MOVETYPE_TOSS;
+       set_movetype(this, MOVETYPE_TOSS);
 
        if(!this.owner)
                return;
@@ -698,7 +673,12 @@ void bumblebee_blowup(entity this)
        if(this.owner.deadflag == DEAD_DYING)
                this.owner.deadflag = DEAD_DEAD;
 
-       remove(this);
+       delete(this);
+}
+
+void bumblebee_dead_touch(entity this, entity toucher)
+{
+       bumblebee_blowup(this);
 }
 
 void bumblebee_diethink(entity this)
@@ -717,8 +697,8 @@ void bumblebee_diethink(entity this)
 
 spawnfunc(vehicle_bumblebee)
 {
-       if(!autocvar_g_vehicle_bumblebee) { remove(this); return; }
-       if(!vehicle_initialize(this, VEH_BUMBLEBEE, false)) { remove(this); return; }
+       if(!autocvar_g_vehicle_bumblebee) { delete(this); return; }
+       if(!vehicle_initialize(this, VEH_BUMBLEBEE, false)) { delete(this); return; }
 }
 
 METHOD(Bumblebee, vr_impact, void(Bumblebee thisveh, entity instance))
@@ -730,7 +710,21 @@ METHOD(Bumblebee, vr_enter, void(Bumblebee thisveh, entity instance))
 {
     settouch(instance, bumblebee_touch);
     instance.nextthink = 0;
-    instance.movetype = MOVETYPE_BOUNCEMISSILE;
+    set_movetype(instance, MOVETYPE_BOUNCEMISSILE);
+}
+METHOD(Bumblebee, vr_gunner_enter, void(Bumblebee thisveh, entity instance, entity actor))
+{
+       if(!instance.gunner1)
+       if(time >= instance.gun1.phase)
+       if(instance.gun1.vehicle_enter)
+       if(instance.gun1.vehicle_enter(instance, actor))
+               return;
+
+       if(!instance.gunner2)
+       if(time >= instance.gun2.phase)
+       if(instance.gun2.vehicle_enter)
+       if(instance.gun2.vehicle_enter(instance, actor))
+               return;
 }
 METHOD(Bumblebee, vr_think, void(Bumblebee thisveh, entity instance))
 {
@@ -745,11 +739,8 @@ METHOD(Bumblebee, vr_think, void(Bumblebee thisveh, entity instance))
         {
                entity e = instance.gunner1;
                instance.gun1.vehicle_exit(instance.gun1, VHEF_EJECT);
-               entity oldother = other;
-               other = e;
                instance.phase = 0;
-               gettouch(instance)(instance);
-               other = oldother;
+               gettouch(instance)(instance, e);
                return;
         }
 
@@ -757,11 +748,8 @@ METHOD(Bumblebee, vr_think, void(Bumblebee thisveh, entity instance))
         {
                entity e = instance.gunner2;
                instance.gun2.vehicle_exit(instance.gun2, VHEF_EJECT);
-               entity oldother = other;
-               other = e;
                instance.phase = 0;
-               gettouch(instance)(instance);
-               other = oldother;
+               gettouch(instance)(instance, e);
             return;
         }
     }
@@ -790,7 +778,7 @@ METHOD(Bumblebee, vr_death, void(Bumblebee thisveh, entity instance))
     entity _body = vehicle_tossgib(instance, instance, instance.velocity + randomvec() * 200, "", rint(random()), rint(random()), 6, randomvec() * 100);
 
     if(random() > 0.5)
-        settouch(_body, bumblebee_blowup);
+        settouch(_body, bumblebee_dead_touch);
     else
         settouch(_body, func_null);
 
@@ -809,7 +797,7 @@ METHOD(Bumblebee, vr_death, void(Bumblebee thisveh, entity instance))
     instance.solid                     = SOLID_NOT;
     instance.takedamage                = DAMAGE_NO;
     instance.deadflag          = DEAD_DYING;
-    instance.movetype          = MOVETYPE_NONE;
+    set_movetype(instance, MOVETYPE_NONE);
     instance.effects           = EF_NODRAW;
     instance.colormod          = '0 0 0';
     instance.avelocity         = '0 0 0';
@@ -886,10 +874,13 @@ METHOD(Bumblebee, vr_spawn, void(Bumblebee thisveh, entity instance))
         }
     }
 
+    if(!autocvar_g_vehicle_bumblebee_swim)
+       instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK;
+
     instance.vehicle_health = autocvar_g_vehicle_bumblebee_health;
     instance.vehicle_shield = autocvar_g_vehicle_bumblebee_shield;
     instance.solid = SOLID_BBOX;
-    instance.movetype = MOVETYPE_TOSS;
+    set_movetype(instance, MOVETYPE_TOSS);
     instance.damageforcescale = 0.025;
 
     instance.PlayerPhysplug = bumblebee_pilot_frame;
index 7a9fc2ccbe684b1bfb1b90a6b8224c59590c6ffb..2c90b7c40b9452d610de5e3f02f0a6b91613b0dd 100644 (file)
@@ -1,13 +1,30 @@
-#ifndef BUMBLEBEE_H
-#define BUMBLEBEE_H
+#pragma once
 
-#ifdef CSQC
+#include "bumblebee_weapons.qh"
 
-void CSQC_BUMBLE_GUN_HUD();
-#endif
+CLASS(Bumblebee, Vehicle)
+/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE);
+/* mins       */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130');
+/* maxs       */ ATTRIB(Bumblebee, maxs, vector, '230 130 130');
+/* view offset*/ ATTRIB(Bumblebee, view_ofs, vector, '0 0 300');
+/* view dist  */ ATTRIB(Bumblebee, height, float, 450);
+/* model         */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm");
+/* model         */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm");
+/* head_model */ ATTRIB(Bumblebee, head_model, string, "");
+/* hud_model  */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm");
+/* tags       */ ATTRIB(Bumblebee, tag_head, string, "");
+/* tags       */ ATTRIB(Bumblebee, tag_hud, string, "");
+/* tags       */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport");
+/* netname    */ ATTRIB(Bumblebee, netname, string, "bumblebee");
+/* fullname   */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee"));
+/* icon       */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble");
+ENDCLASS(Bumblebee)
+REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee));
 
-#ifdef SVQC
-float raptor_altitude(entity this, float amax);
+#ifdef GAMEQC
+       MODEL(VEH_BUMBLEBEE_GUNCOCKPIT, "models/vehicles/wakizashi_cockpit.dpm");
 #endif
 
+#ifdef CSQC
+void CSQC_BUMBLE_GUN_HUD();
 #endif
index b2e838f944b92ccec09440b3e37a6b3781e4c762..9cb8b74c2b5ed03c8b1b083c26f7640dfb614e3e 100644 (file)
@@ -50,6 +50,8 @@ bool bumble_raygun_send(entity this, entity to, float sf)
 
 void bumble_raygun_draw(entity this);
 
+.vector bumble_origin;
+
 NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew)
 {
     int sf = ReadByte();
@@ -69,6 +71,7 @@ NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew)
         this.lip = particleeffectnum(EFFECT_BUMBLEBEE_HEAL_IMPACT);
 
         this.draw = bumble_raygun_draw;
+        if (isnew) IL_PUSH(g_drawables, this);
     }
 
 
@@ -82,9 +85,9 @@ NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew)
 
     if(sf & BRG_END)
     {
-        this.move_origin_x = ReadCoord();
-        this.move_origin_y = ReadCoord();
-        this.move_origin_z = ReadCoord();
+        this.bumble_origin_x = ReadCoord();
+        this.bumble_origin_y = ReadCoord();
+        this.bumble_origin_z = ReadCoord();
     }
     return true;
 }
@@ -96,13 +99,13 @@ void bumble_raygun_draw(entity this)
     vector _dir;
     vector _vtmp1, _vtmp2;
 
-    _len = vlen(this.origin - this.move_origin);
-    _dir = normalize(this.move_origin - this.origin);
+    _len = vlen(this.origin - this.bumble_origin);
+    _dir = normalize(this.bumble_origin - this.origin);
 
     if(this.bumble_raygun_nextdraw < time)
     {
         boxparticles(particleeffectnum(Effects_from(this.traileffect)), this, this.origin, this.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA);
-        boxparticles(this.lip, this, this.move_origin, this.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA);
+        boxparticles(this.lip, this, this.bumble_origin, this.bumble_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA);
         this.bumble_raygun_nextdraw = time + 0.1;
     }
 
@@ -124,7 +127,7 @@ void bumble_raygun_draw(entity this)
         _vtmp1 += randomvec() * (_len * 0.2) * (frametime * 10);     //this.raygun_l3;
         Draw_CylindricLine(_vtmp2, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, this.colormod, al, df, view_origin);
 
-        Draw_CylindricLine(_vtmp1, this.move_origin +  randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, this.colormod, al, df, view_origin);
+        Draw_CylindricLine(_vtmp1, this.bumble_origin +  randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, this.colormod, al, df, view_origin);
     }
 }
 
index 73fed55e1cfaecd936a1419ea88926052a5460e9..e9c5bf41d002167c5f3b74b17d99de8fad0ad60a 100644 (file)
@@ -1,7 +1,8 @@
 #pragma once
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
+#ifdef SVQC
 float autocvar_g_vehicle_bumblebee_cannon_cost = 2;
 float autocvar_g_vehicle_bumblebee_cannon_damage = 60;
 float autocvar_g_vehicle_bumblebee_cannon_radius = 225;
@@ -9,6 +10,7 @@ float autocvar_g_vehicle_bumblebee_cannon_refire = 0.2;
 float autocvar_g_vehicle_bumblebee_cannon_speed = 20000;
 float autocvar_g_vehicle_bumblebee_cannon_spread = 0.02;
 float autocvar_g_vehicle_bumblebee_cannon_force = -35;
+#endif
 
 #ifdef SVQC
 void bumblebee_fire_cannon(entity this, entity _gun, string _tagname, entity _owner);
index 254f9bfc4a74b5772023f786f1425ad37fd97734..80c28a8ab9ac7eab11f3f0fbd29053ad072864c3 100644 (file)
@@ -1,28 +1,4 @@
-#ifndef VEHICLE_RACER
-#define VEHICLE_RACER
-
-#include "racer_weapon.qh"
-
-CLASS(Racer, Vehicle)
-/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
-/* mins       */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5);
-/* maxs       */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5);
-/* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50');
-/* view dist  */ ATTRIB(Racer, height, float, 200);
-/* model         */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm");
-/* model         */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm");
-/* head_model */ ATTRIB(Racer, head_model, string, "null");
-/* hud_model  */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm");
-/* tags       */ ATTRIB(Racer, tag_head, string, "");
-/* tags       */ ATTRIB(Racer, tag_hud, string, "");
-/* tags       */ ATTRIB(Racer, tag_view, string, "tag_viewport");
-/* netname    */ ATTRIB(Racer, netname, string, "racer");
-/* fullname   */ ATTRIB(Racer, vehicle_name, string, _("Racer"));
-/* icon       */ ATTRIB(Racer, m_icon, string, "vehicle_racer");
-ENDCLASS(Racer)
-REGISTER_VEHICLE(RACER, NEW(Racer));
-
-#endif
+#include "racer.qh"
 
 #ifdef IMPLEMENTATION
 
@@ -59,7 +35,7 @@ float autocvar_g_vehicle_racer_friction = 0.45;
 
 float autocvar_g_vehicle_racer_water_time = 5;
 
-float autocvar_g_vehicle_racer_collision_multiplier = 0.05;
+//float autocvar_g_vehicle_racer_collision_multiplier = 0.05;
 
 // 0 = hover, != 0 = maglev
 int autocvar_g_vehicle_racer_hovertype = 0;
@@ -170,15 +146,16 @@ void racer_fire_rocket_aim(entity player, string tagname, entity trg)
        racer_fire_rocket(player, v, v_forward, trg);
 }
 
-bool racer_frame(entity this)
+bool racer_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
-               vehic.velocity = '0 0 0';
-               vehic.avelocity = '0 0 0';
+               vehic.solid = SOLID_NOT;
+               vehic.takedamage = DAMAGE_NO;
+               set_movetype(vehic, MOVETYPE_NONE);
                return;
        }
 
@@ -186,7 +163,7 @@ bool racer_frame(entity this)
 
        traceline(vehic.origin, vehic.origin + '0 0 1', MOVE_NOMONSTERS, this);
        int cont = trace_dpstartcontents;
-       if(cont & DPCONTENTS_WATER)
+       if(!(cont & DPCONTENTS_WATER))
                vehic.air_finished = time + autocvar_g_vehicle_racer_water_time;
 
        if(IS_DEAD(vehic))
@@ -195,28 +172,22 @@ bool racer_frame(entity this)
                return;
        }
 
-       racer_align4point(vehic, PHYS_INPUT_TIMELENGTH);
+       racer_align4point(vehic, dt);
 
        PHYS_INPUT_BUTTON_ZOOM(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false;
 
-       if(time >= vehic.vehicle_last_trace)
-       {
-               crosshair_trace(this);
-               vehic.vehicle_last_trace = time + autocvar_g_vehicle_racer_thinkrate;
-       }
-
        vehic.angles_x *= -1;
 
        // Yaw
-       float ftmp = autocvar_g_vehicle_racer_turnspeed * PHYS_INPUT_TIMELENGTH;
+       float ftmp = autocvar_g_vehicle_racer_turnspeed * dt;
        ftmp = bound(-ftmp, shortangle_f(this.v_angle_y - vehic.angles_y, vehic.angles_y), ftmp);
        vehic.angles_y = anglemods(vehic.angles_y + ftmp);
 
        // Roll
-       vehic.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * PHYS_INPUT_TIMELENGTH;
+       vehic.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * dt;
 
        // Pitch
-       ftmp = autocvar_g_vehicle_racer_pitchspeed  * PHYS_INPUT_TIMELENGTH;
+       ftmp = autocvar_g_vehicle_racer_pitchspeed  * dt;
        ftmp = bound(-ftmp, shortangle_f(this.v_angle_x - vehic.angles_x, vehic.angles_x), ftmp);
        vehic.angles_x = bound(-autocvar_g_vehicle_racer_pitchlimit, anglemods(vehic.angles_x + ftmp), autocvar_g_vehicle_racer_pitchlimit);
 
@@ -262,7 +233,7 @@ bool racer_frame(entity this)
 #endif
 
        // Afterburn
-       if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH))
+       if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * dt))
        {
 #ifdef SVQC
                if(time - vehic.wait > 0.2)
@@ -273,12 +244,12 @@ bool racer_frame(entity this)
 
                if(cont & DPCONTENTS_LIQUIDSMASK)
                {
-                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * PHYS_INPUT_TIMELENGTH;
+                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * dt;
                        df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed);
                }
                else
                {
-                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH;
+                       vehic.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * dt;
                        df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn);
                }
 
@@ -313,7 +284,7 @@ bool racer_frame(entity this)
                dforce = autocvar_g_vehicle_racer_water_downforce;
 
        df -= v_up * (vlen(vehic.velocity) * dforce);
-       this.movement = vehic.velocity += df * PHYS_INPUT_TIMELENGTH;
+       this.movement = vehic.velocity += df * dt;
 
 #ifdef SVQC
 
@@ -337,10 +308,16 @@ bool racer_frame(entity this)
 
        if(autocvar_g_vehicle_racer_rocket_locktarget)
        {
-               if(vehic.vehicle_last_trace == time + autocvar_g_vehicle_racer_thinkrate)
-               vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime,
-                                                (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime,
-                                                autocvar_g_vehicle_racer_rocket_locked_time);
+               if(time >= vehic.vehicle_last_trace)
+               {
+                       crosshair_trace(this);
+
+                       vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * dt,
+                                                        (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * dt,
+                                                        autocvar_g_vehicle_racer_rocket_locked_time);
+
+                       vehic.vehicle_last_trace = time + autocvar_g_vehicle_racer_thinkrate;
+               }
 
                if(vehic.lock_target)
                {
@@ -382,13 +359,13 @@ bool racer_frame(entity this)
        this.vehicle_reload2 = bound(0, 100 * ((time - vehic.lip) / (vehic.delay - vehic.lip)), 100);
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, dt, false);
 
        if(vehic.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(vehic, vehic.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, false);
+               vehicles_regen(vehic, vehic.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, dt, false);
 
        VEHICLE_UPDATE_PLAYER(this, vehic, health, racer);
        VEHICLE_UPDATE_PLAYER(this, vehic, energy, racer);
@@ -439,7 +416,7 @@ void racer_exit(entity this, int eject)
 
        setthink(this, racer_think);
        this.nextthink  = time;
-       this.movetype   = MOVETYPE_BOUNCE;
+       set_movetype(this, MOVETYPE_BOUNCE);
        sound (this.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM);
 
        if(!this.owner)
@@ -490,7 +467,7 @@ void racer_blowup(entity this)
 
        this.nextthink  = time + autocvar_g_vehicle_racer_respawntime;
        setthink(this, vehicles_spawn);
-       this.movetype   = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.effects    = EF_NODRAW;
        this.solid = SOLID_NOT;
 
@@ -511,7 +488,7 @@ void racer_blowup_think(entity this)
        CSQCMODEL_AUTOUPDATE(this);
 }
 
-void racer_deadtouch(entity this)
+void racer_deadtouch(entity this, entity toucher)
 {
        this.avelocity_x *= 0.7;
        this.cnt -= 1;
@@ -521,47 +498,12 @@ void racer_deadtouch(entity this)
 
 spawnfunc(vehicle_racer)
 {
-       if(!autocvar_g_vehicle_racer) { remove(this); return; }
-       if(!vehicle_initialize(this, VEH_RACER, false)) { remove(this); return; }
+       if(!autocvar_g_vehicle_racer) { delete(this); return; }
+       if(!vehicle_initialize(this, VEH_RACER, false)) { delete(this); return; }
 }
 
 #endif // SVQC
 
-#ifdef CSQC
-#if 0
-void racer_draw(entity this)
-{
-       float pushdeltatime = time - this.lastpushtime;
-       if (pushdeltatime > 0.15) pushdeltatime = 0;
-       this.lastpushtime = time;
-       if(!pushdeltatime) return;
-
-       tracebox(this.move_origin, this.mins, this.maxs, this.move_origin - ('0 0 1' * STAT(VEH_RACER_SPRINGLENGTH)), MOVE_NOMONSTERS, this);
-
-       vector df = this.move_velocity * -STAT(VEH_RACER_FRICTION);
-       df_z += (1 - trace_fraction) * STAT(VEH_RACER_HOVERPOWER) + sin(time * 2) * (STAT(VEH_RACER_SPRINGLENGTH) * 2);
-
-       float forced = STAT(VEH_RACER_UPFORCEDAMPER);
-
-       int cont = pointcontents(this.move_origin - '0 0 64');
-       if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
-       {
-               forced = STAT(VEH_RACER_WATER_UPFORCEDAMPER);
-               this.move_velocity_z += 200;
-       }
-
-       this.move_velocity += df * pushdeltatime;
-       if(this.move_velocity_z > 0)
-               this.move_velocity_z *= 1 - forced * pushdeltatime;
-
-       this.move_angles_x *= 1 - (STAT(VEH_RACER_ANGLESTABILIZER) * pushdeltatime);
-       this.move_angles_z *= 1 - (STAT(VEH_RACER_ANGLESTABILIZER) * pushdeltatime);
-
-       Movetype_Physics_MatchServer(this, false);
-}
-#endif
-#endif
-
 METHOD(Racer, vr_impact, void(Racer thisveh, entity instance))
 {
 #ifdef SVQC
@@ -573,14 +515,14 @@ METHOD(Racer, vr_impact, void(Racer thisveh, entity instance))
 METHOD(Racer, vr_enter, void(Racer thisveh, entity instance))
 {
 #ifdef SVQC
-    instance.movetype = MOVETYPE_BOUNCE;
+    set_movetype(instance, MOVETYPE_BOUNCE);
     instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_racer_health)  * 100;
     instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_racer_shield)  * 100;
 
     if(instance.owner.flagcarried)
        setorigin(instance.owner.flagcarried, '-190 0 96');
 #elif defined(CSQC)
-    instance.move_movetype = MOVETYPE_BOUNCE;
+    set_movetype(instance, MOVETYPE_BOUNCE);
 #endif
 }
 
@@ -607,7 +549,7 @@ METHOD(Racer, vr_spawn, void(Racer thisveh, entity instance))
     instance.vehicle_health = autocvar_g_vehicle_racer_health;
     instance.vehicle_shield = autocvar_g_vehicle_racer_shield;
 
-    instance.movetype    = MOVETYPE_TOSS;
+    set_movetype(instance, MOVETYPE_TOSS);
     instance.solid               = SOLID_SLIDEBOX;
     instance.delay               = time;
     instance.scale               = 0.5;
@@ -631,7 +573,7 @@ METHOD(Racer, vr_death, void(Racer thisveh, entity instance))
     instance.solid                     = SOLID_CORPSE;
     instance.takedamage                = DAMAGE_NO;
     instance.deadflag          = DEAD_DYING;
-    instance.movetype          = MOVETYPE_BOUNCE;
+    set_movetype(instance, MOVETYPE_BOUNCE);
     instance.wait                      = time;
     instance.delay                     = 2 + time + random() * 3;
     instance.cnt                       = 1 + random() * 2;
diff --git a/qcsrc/common/vehicles/vehicle/racer.qh b/qcsrc/common/vehicles/vehicle/racer.qh
new file mode 100644 (file)
index 0000000..dd14144
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "racer_weapon.qh"
+
+CLASS(Racer, Vehicle)
+/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
+/* mins       */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5);
+/* maxs       */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5);
+/* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50');
+/* view dist  */ ATTRIB(Racer, height, float, 200);
+/* model         */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm");
+/* model         */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm");
+/* head_model */ ATTRIB(Racer, head_model, string, "null");
+/* hud_model  */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm");
+/* tags       */ ATTRIB(Racer, tag_head, string, "");
+/* tags       */ ATTRIB(Racer, tag_hud, string, "");
+/* tags       */ ATTRIB(Racer, tag_view, string, "tag_viewport");
+/* netname    */ ATTRIB(Racer, netname, string, "racer");
+/* fullname   */ ATTRIB(Racer, vehicle_name, string, _("Racer"));
+/* icon       */ ATTRIB(Racer, m_icon, string, "vehicle_racer");
+ENDCLASS(Racer)
+REGISTER_VEHICLE(RACER, NEW(Racer));
index 1af09990bc5aca8ec8582ce313785a6a24e7cb99..d20210c6118434d1c83dffc44729c65080bf4a4e 100644 (file)
@@ -16,7 +16,7 @@ METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weapone
             veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
             veh.wait = time;
         }
-        if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
+        if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
         vector org = w_shotorg;
         vector dir = w_shotdir;
         entity bolt = vehicles_projectile(veh, EFFECT_RACER_MUZZLEFLASH.eent_eff_name, SND_LASERGUN_FIRE,
@@ -28,7 +28,7 @@ METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weapone
     }
     if (fire & 2)
     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
-        if (isPlayer) W_SetupShot_Dir(actor, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
+        if (isPlayer) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
         racer_fire_rocket(player, w_shotorg, w_shotdir, NULL);
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
     }
index fc9e352542c3a00c76cc2209241b7247bfec01b5..f8b18a86ff03aec6a5c1d3efe8519d37c558ee80 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 CLASS(RacerAttack, PortoLaunch)
 /* flags     */ ATTRIB(RacerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
@@ -15,6 +15,7 @@ REGISTER_WEAPON(RACER, NEW(RacerAttack));
 void racer_fire_rocket(entity player, vector org, vector dir, entity trg);
 #endif
 
+#ifdef SVQC
 float autocvar_g_vehicle_racer_cannon_cost = 2;
 float autocvar_g_vehicle_racer_cannon_damage = 15;
 float autocvar_g_vehicle_racer_cannon_radius = 100;
@@ -33,3 +34,4 @@ float autocvar_g_vehicle_racer_rocket_refire = 3;
 
 float autocvar_g_vehicle_racer_rocket_climbspeed = 1600;
 float autocvar_g_vehicle_racer_rocket_locked_maxangle = 1.8;
+#endif
index 28716fc2669beb3cab84ab078a9a34a2b27d9327..7c433f9c76f69dce59cbdba280418880c8210fe1 100644 (file)
@@ -1,30 +1,5 @@
-#ifndef VEHICLE_RAPTOR
-#define VEHICLE_RAPTOR
 #include "raptor.qh"
 
-#include "raptor_weapons.qh"
-
-CLASS(Raptor, Vehicle)
-/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
-/* mins       */ ATTRIB(Raptor, mins, vector, '-80 -80 0');
-/* maxs       */ ATTRIB(Raptor, maxs, vector, '80 80 70');
-/* view offset*/ ATTRIB(Raptor, view_ofs, vector, '0 0 160');
-/* view dist  */ ATTRIB(Raptor, height, float, 200);
-/* model         */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm");
-/* model         */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm");
-/* head_model */ ATTRIB(Raptor, head_model, string, "");
-/* hud_model  */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm");
-/* tags       */ ATTRIB(Raptor, tag_head, string, "");
-/* tags       */ ATTRIB(Raptor, tag_hud, string, "tag_hud");
-/* tags       */ ATTRIB(Raptor, tag_view, string, "tag_camera");
-/* netname    */ ATTRIB(Raptor, netname, string, "raptor");
-/* fullname   */ ATTRIB(Raptor, vehicle_name, string, _("Raptor"));
-/* icon       */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor");
-ENDCLASS(Raptor)
-REGISTER_VEHICLE(RAPTOR, NEW(Raptor));
-
-#endif
-
 #ifdef IMPLEMENTATION
 
 #ifdef SVQC
@@ -47,6 +22,8 @@ float autocvar_g_vehicle_raptor_speed_up = 1700;
 float autocvar_g_vehicle_raptor_speed_down = 1700;
 float autocvar_g_vehicle_raptor_friction = 2;
 
+bool autocvar_g_vehicle_raptor_swim = false;
+
 float autocvar_g_vehicle_raptor_cannon_turnspeed = 120;
 float autocvar_g_vehicle_raptor_cannon_turnlimit = 20;
 float autocvar_g_vehicle_raptor_cannon_pitchlimit_up = 12;
@@ -77,23 +54,16 @@ vector autocvar_g_vehicle_raptor_bouncepain = '1 4 1000';
 .entity bomb1;
 .entity bomb2;
 
-float raptor_altitude(entity this, float amax)
-{
-       tracebox(this.origin, this.mins, this.maxs, this.origin - ('0 0 1' * amax), MOVE_WORLDONLY, this);
-       return vlen(this.origin - trace_endpos);
-}
-
 void raptor_land(entity this)
 {
        float hgt;
 
-       hgt = raptor_altitude(this, 512);
-       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
+       hgt = vehicle_altitude(this, 512);
+       this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * PHYS_INPUT_FRAMETIME);
        this.angles_x *= 0.95;
        this.angles_z *= 0.95;
 
-       if(hgt < 128)
-       if(hgt > 0)
+       if(hgt < 128 && hgt > 0)
                this.frame = (hgt / 128) * 25;
 
        this.bomb1.gun1.avelocity_y = 90 + ((this.frame / 25) * 2000);
@@ -101,7 +71,7 @@ void raptor_land(entity this)
 
        if(hgt < 16)
        {
-               this.movetype = MOVETYPE_TOSS;
+               set_movetype(this, MOVETYPE_TOSS);
                setthink(this, vehicles_think);
                this.frame      = 0;
        }
@@ -113,19 +83,19 @@ void raptor_land(entity this)
 
 void raptor_exit(entity this, int eject)
 {
-       vector spot;
        this.tur_head.exteriormodeltoclient = NULL;
 
        if(!IS_DEAD(this))
        {
                setthink(this, raptor_land);
-               this.nextthink  = time;
+               this.nextthink = time;
        }
 
        if(!this.owner)
                return;
 
        makevectors(this.angles);
+       vector spot;
        if(eject)
        {
                spot = this.origin + v_forward * 100 + '0 0 64';
@@ -158,21 +128,21 @@ void raptor_exit(entity this, int eject)
        this.owner = NULL;
 }
 
-bool raptor_frame(entity this)
+bool raptor_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
-               vehic.velocity = '0 0 0';
-               vehic.avelocity = '0 0 0';
+               vehic.solid = SOLID_NOT;
+               vehic.takedamage = DAMAGE_NO;
+               set_movetype(vehic, MOVETYPE_NONE);
                return;
        }
 
        vehicles_frame(vehic, this);
 
-       float ftmp = 0;
        /*
        ftmp = vlen(vehic.velocity);
        if(ftmp > autocvar_g_vehicle_raptor_speed_forward)
@@ -186,7 +156,7 @@ bool raptor_frame(entity this)
                vehic.sound_nexttime = time + 7.955812;
                //sound (vehic.tur_head, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_FLY, 1 - ftmp,   ATTEN_NORM );
                sound (vehic, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_SPEED, 1, ATTEN_NORM);
-               vehic.wait = ftmp;
+               vehic.wait = 0;
        }
        /*
        else if(fabs(ftmp - vehic.wait) > 0.2)
@@ -226,7 +196,7 @@ bool raptor_frame(entity this)
        if(df_y > 180)  df_y -= 360;
        if(df_y < -180) df_y += 360;
 
-       ftmp = shortangle_f(this.v_angle_y - vang_y, vang_y);
+       float ftmp = shortangle_f(this.v_angle_y - vang_y, vang_y);
        if(ftmp > 180)  ftmp -= 360; if(ftmp < -180) ftmp += 360;
        vehic.avelocity_y = bound(-autocvar_g_vehicle_raptor_turnspeed, ftmp + vehic.avelocity_y * 0.9, autocvar_g_vehicle_raptor_turnspeed);
 
@@ -279,7 +249,7 @@ bool raptor_frame(entity this)
        else if (PHYS_INPUT_BUTTON_JUMP(this))
                df +=  v_up * autocvar_g_vehicle_raptor_speed_up;
 
-       vehic.velocity  += df * frametime;
+       vehic.velocity  += df * dt;
        this.velocity = this.movement  = vehic.velocity;
        setorigin(this, vehic.origin + '0 0 32');
 
@@ -293,7 +263,7 @@ bool raptor_frame(entity this)
                        vehic.gun1.enemy = NULL;
 
                if(trace_ent)
-               if(trace_ent.movetype)
+               if(trace_ent.move_movetype)
                if(trace_ent.takedamage)
                if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent))
                {
@@ -319,7 +289,7 @@ bool raptor_frame(entity this)
                        vf = real_origin(vehic.gun1.enemy);
                        UpdateAuxiliaryXhair(this, vf, '1 0 0', 1);
                        vector _vel = vehic.gun1.enemy.velocity;
-                       if(vehic.gun1.enemy.movetype == MOVETYPE_WALK)
+                       if(vehic.gun1.enemy.move_movetype == MOVETYPE_WALK)
                                _vel_z *= 0.1;
 
                        if(autocvar_g_vehicle_raptor_cannon_predicttarget)
@@ -337,8 +307,8 @@ bool raptor_frame(entity this)
        else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1)
        {
 
-               vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime,
-                                                        (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime,
+               vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_raptor_cannon_locking_time) * dt,
+                                                        (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * dt,
                                                         autocvar_g_vehicle_raptor_cannon_locked_time);
 
                if(vehic.lock_target != NULL)
@@ -395,13 +365,13 @@ bool raptor_frame(entity this)
        }
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false);
 
        if(vehic.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
+               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false);
 
        Weapon wep2a = WEP_RAPTOR_BOMB;
        if(!forbidWeaponUse(this))
@@ -436,9 +406,8 @@ bool raptor_frame(entity this)
        if(vehic.bomb1.cnt < time)
        {
                bool incoming = false;
-               FOREACH_ENTITY_ENT(enemy, vehic,
+               IL_EACH(g_projectiles, it.enemy == vehic,
                {
-                       if(it.flags & FL_PROJECTILE)
                        if(MISSILE_IS_TRACKING(it))
                        if(vdist(vehic.origin - it.origin, <, 2 * autocvar_g_vehicle_raptor_flare_range))
                        {
@@ -465,7 +434,7 @@ bool raptor_frame(entity this)
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false;
 }
 
-bool raptor_takeoff(entity this)
+bool raptor_takeoff(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
@@ -483,7 +452,7 @@ bool raptor_takeoff(entity this)
        // Takeoff sequense
        if(vehic.frame < 25)
        {
-               vehic.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime);
+               vehic.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / PHYS_INPUT_FRAMETIME);
                vehic.velocity_z = min(vehic.velocity_z * 1.5, 256);
                vehic.bomb1.gun1.avelocity_y = 90 + ((vehic.frame / 25) * 25000);
                vehic.bomb1.gun2.avelocity_y = -vehic.bomb1.gun1.avelocity_y;
@@ -495,13 +464,13 @@ bool raptor_takeoff(entity this)
                this.PlayerPhysplug = raptor_frame;
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false);
 
        if(vehic.vehicle_flags  & VHF_ENERGYREGEN)
-               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
+               vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false);
 
 
        vehic.bomb1.alpha = vehic.bomb2.alpha = (time - vehic.lip) / (vehic.delay - vehic.lip);
@@ -516,14 +485,14 @@ bool raptor_takeoff(entity this)
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false;
 }
 
-void raptor_blowup(entity this)
+void raptor_blowup(entity this, entity toucher)
 {
        this.deadflag   = DEAD_DEAD;
        this.vehicle_exit(this, VHEF_NORMAL);
        RadiusDamage (this, this.enemy, 250, 15, 250, NULL, NULL, 250, DEATH_VH_RAPT_DEATH.m_id, NULL);
 
        this.alpha                = -1;
-       this.movetype      = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.effects            = EF_NODRAW;
        this.colormod      = '0 0 0';
        this.avelocity    = '0 0 0';
@@ -537,7 +506,10 @@ void raptor_blowup(entity this)
 void raptor_diethink(entity this)
 {
        if(time >= this.wait)
-               setthink(this, raptor_blowup);
+       {
+               raptor_blowup(this, NULL);
+               return;
+       }
 
        if(random() < 0.05)
        {
@@ -603,8 +575,8 @@ bool raptor_impulse(entity this, int _imp)
 
 spawnfunc(vehicle_raptor)
 {
-       if(!autocvar_g_vehicle_raptor) { remove(this); return; }
-       if(!vehicle_initialize(this, VEH_RAPTOR, false)) { remove(this); return; }
+       if(!autocvar_g_vehicle_raptor) { delete(this); return; }
+       if(!vehicle_initialize(this, VEH_RAPTOR, false)) { delete(this); return; }
 }
 
 METHOD(Raptor, vr_impact, void(Raptor thisveh, entity instance))
@@ -616,11 +588,11 @@ METHOD(Raptor, vr_enter, void(Raptor thisveh, entity instance))
 {
     instance.vehicle_weapon2mode = RSM_BOMB;
     instance.owner.PlayerPhysplug = raptor_takeoff;
-    instance.movetype     = MOVETYPE_BOUNCEMISSILE;
+    set_movetype(instance, MOVETYPE_BOUNCEMISSILE);
     instance.solid               = SOLID_SLIDEBOX;
     instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_raptor_health) * 100;
     instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100;
-    instance.velocity_z = 1; // Nudge upwards to takeoff sequense can work.
+    instance.velocity = '0 0 1'; // nudge upwards so takeoff sequence can work
     instance.tur_head.exteriormodeltoclient = instance.owner;
 
     instance.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
@@ -638,7 +610,7 @@ METHOD(Raptor, vr_death, void(Raptor thisveh, entity instance))
     instance.solid                             = SOLID_CORPSE;
     instance.takedamage                        = DAMAGE_NO;
     instance.deadflag                  = DEAD_DYING;
-    instance.movetype                  = MOVETYPE_BOUNCE;
+    set_movetype(instance, MOVETYPE_BOUNCE);
     setthink(instance, raptor_diethink);
     instance.nextthink                 = time;
     instance.wait                              = time + 5 + (random() * 5);
@@ -705,7 +677,7 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance))
         spinner.owner = instance;
         setmodel(spinner, MDL_VEH_RAPTOR_PROP);
         setattachment(spinner, instance, "engine_left");
-        spinner.movetype = MOVETYPE_NOCLIP;
+        set_movetype(spinner, MOVETYPE_NOCLIP);
         spinner.avelocity = '0 90 0';
         instance.bomb1.gun1 = spinner;
 
@@ -713,7 +685,7 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance))
         spinner.owner = instance;
         setmodel(spinner, MDL_VEH_RAPTOR_PROP);
         setattachment(spinner, instance, "engine_right");
-        spinner.movetype = MOVETYPE_NOCLIP;
+        set_movetype(spinner, MOVETYPE_NOCLIP);
         spinner.avelocity = '0 -90 0';
         instance.bomb1.gun2 = spinner;
 
@@ -727,10 +699,13 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance))
     instance.frame               = 0;
     instance.vehicle_health = autocvar_g_vehicle_raptor_health;
     instance.vehicle_shield = autocvar_g_vehicle_raptor_shield;
-    instance.movetype     = MOVETYPE_TOSS;
+    set_movetype(instance, MOVETYPE_TOSS);
     instance.solid               = SOLID_SLIDEBOX;
     instance.vehicle_energy = 1;
 
+    if(!autocvar_g_vehicle_raptor_swim)
+       instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK;
+
     instance.PlayerPhysplug = raptor_frame;
 
     instance.bomb1.gun1.avelocity_y = 90;
@@ -763,6 +738,9 @@ METHOD(Raptor, vr_setup, void(Raptor thisveh, entity instance))
     instance.vehicle_health = autocvar_g_vehicle_raptor_health;
     instance.vehicle_shield = autocvar_g_vehicle_raptor_shield;
     instance.max_health = instance.vehicle_health;
+
+    if(!autocvar_g_vehicle_raptor_swim)
+       instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK;
 }
 
 #endif
@@ -786,7 +764,7 @@ METHOD(Raptor, vr_crosshair, void(Raptor thisveh, entity player))
     }
 
     vector tmpSize = '0 0 0';
-    if(weapon2mode != RSM_FLARE)
+    if(weapon2mode != RSM_FLARE && !spectatee_status)
     {
         vector where;
 
index f24939ba9d21a2f697c41b6592e9ca970e598aad..12666523c7bf8507a390e27339bce29c808f3352 100644 (file)
@@ -1,9 +1,27 @@
-#ifndef RAPTOR_H
-#define RAPTOR_H
+#pragma once
+
+#include "raptor_weapons.qh"
+
+CLASS(Raptor, Vehicle)
+/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
+/* mins       */ ATTRIB(Raptor, mins, vector, '-80 -80 0');
+/* maxs       */ ATTRIB(Raptor, maxs, vector, '80 80 70');
+/* view offset*/ ATTRIB(Raptor, view_ofs, vector, '0 0 160');
+/* view dist  */ ATTRIB(Raptor, height, float, 200);
+/* model         */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm");
+/* model         */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm");
+/* head_model */ ATTRIB(Raptor, head_model, string, "");
+/* hud_model  */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm");
+/* tags       */ ATTRIB(Raptor, tag_head, string, "");
+/* tags       */ ATTRIB(Raptor, tag_hud, string, "tag_hud");
+/* tags       */ ATTRIB(Raptor, tag_view, string, "tag_camera");
+/* netname    */ ATTRIB(Raptor, netname, string, "raptor");
+/* fullname   */ ATTRIB(Raptor, vehicle_name, string, _("Raptor"));
+/* icon       */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor");
+ENDCLASS(Raptor)
+REGISTER_VEHICLE(RAPTOR, NEW(Raptor));
 
 const int RSM_FIRST = 1;
 const int RSM_BOMB = 1;
 const int RSM_FLARE = 2;
 const int RSM_LAST = 2;
-
-#endif
index 56c3e8c610ade76c871b28f091e84fc1f1a8fe9d..4e16efbcc903df3227c1dc30ae419ef7e4f45731 100644 (file)
@@ -12,7 +12,7 @@ METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, .entity weapon
     float t = autocvar_g_vehicle_raptor_cannon_refire * (1 + veh.misc_bulletcounter == 4);
     if (fire & 1)
     if (weapon_prepareattack(thiswep, player, weaponentity, false, t)) {
-        if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
+        if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
         vector org = w_shotorg;
         vector dir = w_shotdir;
         if (veh) {
@@ -53,7 +53,7 @@ METHOD(RaptorBomb, wr_think, void(entity thiswep, entity actor, .entity weaponen
 
 void raptor_flare_think(entity this);
 void raptor_flare_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-void raptor_flare_touch(entity this);
+void raptor_flare_touch(entity this, entity toucher);
 
 METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
     bool isPlayer = IS_PLAYER(actor);
@@ -67,7 +67,7 @@ METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weapone
             _flare.effects = EF_LOWPRECISION | EF_FLAME;
             _flare.scale = 0.5;
             setorigin(_flare, actor.origin - '0 0 16');
-            _flare.movetype = MOVETYPE_TOSS;
+            set_movetype(_flare, MOVETYPE_TOSS);
             _flare.gravity = 0.15;
             _flare.velocity = 0.25 * actor.velocity + (v_forward + randomvec() * 0.25)* -500;
             setthink(_flare, raptor_flare_think);
@@ -91,15 +91,15 @@ void raptor_bomblet_boom(entity this)
                                     autocvar_g_vehicle_raptor_bomblet_edgedamage,
                                     autocvar_g_vehicle_raptor_bomblet_radius, NULL, NULL,
                                     autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB.m_id, NULL);
-    remove(this);
+    delete(this);
 }
 
-void raptor_bomblet_touch(entity this)
+void raptor_bomblet_touch(entity this, entity toucher)
 {
-    if(other == this.owner)
+    if(toucher == this.owner)
         return;
 
-    PROJECTILE_TOUCH(this);
+    PROJECTILE_TOUCH(this, toucher);
     setthink(this, raptor_bomblet_boom);
     this.nextthink = time + random() * autocvar_g_vehicle_raptor_bomblet_explode_delay;
 }
@@ -128,7 +128,7 @@ void raptor_bomb_burst(entity this)
         bomblet = spawn();
         setorigin(bomblet, this.origin);
 
-        bomblet.movetype       = MOVETYPE_TOSS;
+        set_movetype(bomblet, MOVETYPE_TOSS);
         settouch(bomblet, raptor_bomblet_touch);
         setthink(bomblet, raptor_bomblet_boom);
         bomblet.nextthink   = time + 5;
@@ -140,7 +140,12 @@ void raptor_bomb_burst(entity this)
         CSQCProjectile(bomblet, true, PROJECTILE_RAPTORBOMBLET, true);
     }
 
-    remove(this);
+    delete(this);
+}
+
+void raptor_bomb_touch(entity this, entity toucher)
+{
+       raptor_bomb_burst(this);
 }
 
 void raptor_bombdrop(entity this)
@@ -155,10 +160,11 @@ void raptor_bombdrop(entity this)
     org = gettaginfo(this, gettagindex(this, "bombmount_right"));
     setorigin(bomb_2, org);
 
-    bomb_1.movetype     = bomb_2.movetype   = MOVETYPE_BOUNCE;
+    set_movetype(bomb_1, MOVETYPE_BOUNCE);
+    set_movetype(bomb_2, MOVETYPE_BOUNCE);
     bomb_1.velocity     = bomb_2.velocity   = this.velocity;
-    settouch(bomb_1, raptor_bomb_burst);
-    settouch(bomb_2, raptor_bomb_burst);
+    settouch(bomb_1, raptor_bomb_touch);
+    settouch(bomb_2, raptor_bomb_touch);
     setthink(bomb_1, raptor_bomb_burst);
     setthink(bomb_2, raptor_bomb_burst);
     bomb_1.cnt           = bomb_2.cnt          = time + 10;
@@ -180,33 +186,30 @@ void raptor_bombdrop(entity this)
     CSQCProjectile(bomb_2, true, PROJECTILE_RAPTORBOMB, true);
 }
 
-void raptor_flare_touch(entity this)
+void raptor_flare_touch(entity this, entity toucher)
 {
-    remove(this);
+    delete(this);
 }
 
 void raptor_flare_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
 {
     this.health -= damage;
     if(this.health <= 0)
-        remove(this);
+        delete(this);
 }
 
 void raptor_flare_think(entity this)
 {
     this.nextthink = time + 0.1;
-    entity _missile = findchainentity(enemy, this.owner);
-    while(_missile)
+    IL_EACH(g_projectiles, it.enemy == this.owner,
     {
-        if(_missile.flags & FL_PROJECTILE)
-        if(vdist(this.origin - _missile.origin, <, autocvar_g_vehicle_raptor_flare_range))
+        if(vdist(this.origin - it.origin, <, autocvar_g_vehicle_raptor_flare_range))
         if(random() > autocvar_g_vehicle_raptor_flare_chase)
-            _missile.enemy = this;
-        _missile = _missile.chain;
-    }
+            it.enemy = this;
+    });
 
     if(this.tur_impacttime < time)
-        remove(this);
+        delete(this);
 }
 
 #endif
@@ -219,14 +222,14 @@ void RaptorCBShellfragDraw(entity this)
         return;
 
     Movetype_Physics_MatchTicrate(this, autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
-    this.move_avelocity += randomvec() * 15;
+    this.avelocity += randomvec() * 15;
     this.renderflags = 0;
 
     if(this.cnt < time)
         this.alpha = bound(0, this.nextthink - time, 1);
 
     if(this.alpha < ALPHA_MIN_VISIBLE)
-        remove(this);
+        delete(this);
 }
 
 void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
@@ -235,16 +238,16 @@ void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
     setmodel(sfrag, MDL_VEH_RAPTOR_CB_FRAGMENT);
     setorigin(sfrag, _org);
 
-    sfrag.move_movetype = MOVETYPE_BOUNCE;
+    set_movetype(sfrag, MOVETYPE_BOUNCE);
     sfrag.gravity = 0.15;
     sfrag.solid = SOLID_CORPSE;
 
     sfrag.draw = RaptorCBShellfragDraw;
+    IL_PUSH(g_drawables, sfrag);
 
-    sfrag.move_origin = sfrag.origin = _org;
-    sfrag.move_velocity = _vel;
-    sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity);
-    sfrag.angles = sfrag.move_angles = _ang;
+    sfrag.velocity = _vel;
+    sfrag.avelocity = prandomvec() * vlen(sfrag.velocity);
+    sfrag.angles = _ang;
 
     sfrag.move_time = time;
     sfrag.damageforcescale = 4;
index 0b3af4169d143dafdf42243d246b1c4e0ddf1e13..a7908d50d340829f44d903c59749aadd134e85f8 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 CLASS(RaptorCannon, PortoLaunch)
 /* flags     */ ATTRIB(RaptorCannon, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
@@ -26,7 +26,7 @@ CLASS(RaptorFlare, PortoLaunch)
 ENDCLASS(RaptorFlare)
 REGISTER_WEAPON(RAPTOR_FLARE, NEW(RaptorFlare));
 
-
+#ifdef SVQC
 float autocvar_g_vehicle_raptor_cannon_cost = 1;
 float autocvar_g_vehicle_raptor_cannon_damage = 10;
 float autocvar_g_vehicle_raptor_cannon_radius = 60;
@@ -51,3 +51,4 @@ float autocvar_g_vehicle_raptor_flare_refire = 5;
 float autocvar_g_vehicle_raptor_flare_lifetime = 10;
 float autocvar_g_vehicle_raptor_flare_chase = 0.9;
 float autocvar_g_vehicle_raptor_flare_range = 2000;
+#endif
index d41e81ce6154c0d3bc5c57bfc8b877d891e47d0f..6ad46946d70927728dd7447d96d8314a907cc5f9 100644 (file)
@@ -1,29 +1,4 @@
-#ifndef VEHICLE_SPIDERBOT
-#define VEHICLE_SPIDERBOT
-
-#include "spiderbot_weapons.qh"
-
-CLASS(Spiderbot, Vehicle)
-/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE);
-/* mins       */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10');
-/* maxs       */ ATTRIB(Spiderbot, maxs, vector, '75 75 125');
-/* view offset*/ ATTRIB(Spiderbot, view_ofs, vector, '0 0 70');
-/* view dist  */ ATTRIB(Spiderbot, height, float, 170);
-/* model         */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm");
-/* model         */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm");
-/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm");
-/* hud_model  */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm");
-/* tags       */ ATTRIB(Spiderbot, tag_head, string, "tag_head");
-/* tags       */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud");
-/* tags       */ ATTRIB(Spiderbot, tag_view, string, "");
-/* netname    */ ATTRIB(Spiderbot, netname, string, "spiderbot");
-/* fullname   */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot"));
-/* icon       */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider");
-ENDCLASS(Spiderbot)
-
-REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot));
-
-#endif
+#include "spiderbot.qh"
 
 #ifdef IMPLEMENTATION
 
@@ -68,15 +43,16 @@ float autocvar_g_vehicle_spiderbot_shield_regen_pause = 0.35;
 vector autocvar_g_vehicle_spiderbot_bouncepain = '0 0 0';
 
 .float jump_delay;
-bool spiderbot_frame(entity this)
+bool spiderbot_frame(entity this, float dt)
 {
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
-               vehic.velocity = '0 0 0';
-               vehic.avelocity = '0 0 0';
+               vehic.solid = SOLID_NOT;
+               vehic.takedamage = DAMAGE_NO;
+               set_movetype(vehic, MOVETYPE_NONE);
                return;
        }
 
@@ -116,7 +92,7 @@ bool spiderbot_frame(entity this)
        //UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload2) + ('0 1 0' * (1 - this.vehicle_reload2)), 2);
 
        // Rotate head
-       float ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime;
+       float ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * PHYS_INPUT_FRAMETIME;
        ad_y = bound(-ftmp, ad_y, ftmp);
        vehic.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, vehic.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit);
 
@@ -193,9 +169,9 @@ bool spiderbot_frame(entity this)
                        {
                                // Turn Body
                                if(this.movement_x == 0 && this.movement_y != 0)
-                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime;
+                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * PHYS_INPUT_FRAMETIME;
                                else
-                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime;
+                                       ftmp = autocvar_g_vehicle_spiderbot_turnspeed * PHYS_INPUT_FRAMETIME;
 
                                ftmp = bound(-ftmp, vehic.tur_head.angles_y, ftmp);
                                vehic.angles_y = anglemods(vehic.angles_y + ftmp);
@@ -221,7 +197,7 @@ bool spiderbot_frame(entity this)
                                        vehic.velocity_z = oldvelz;
                                        float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
                                        if(vehic.velocity_z <= 20) // not while jumping
-                                               vehic.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
+                                               vehic.velocity_z -= g * PHYS_INPUT_FRAMETIME * autocvar_sv_gravity;
                                        if(IS_ONGROUND(vehic))
                                        if(vehic.sound_nexttime < time || vehic.delay != 1)
                                        {
@@ -251,7 +227,7 @@ bool spiderbot_frame(entity this)
                                        vehic.velocity_z = oldvelz;
                                        float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
                                        if(vehic.velocity_z <= 20) // not while jumping
-                                               vehic.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
+                                               vehic.velocity_z -= g * PHYS_INPUT_FRAMETIME * autocvar_sv_gravity;
                                        if(IS_ONGROUND(vehic))
                                        if(vehic.sound_nexttime < time || vehic.delay != 2)
                                        {
@@ -306,16 +282,16 @@ bool spiderbot_frame(entity this)
        else
                vehicles_regen(vehic, vehic.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max,
                                                                                   autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause,
-                                                                                  autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, false);
+                                                                                  autocvar_g_vehicle_spiderbot_minigun_ammo_regen, dt, false);
 
 
        spiderbot_rocket_do(vehic);
 
        if(vehic.vehicle_flags  & VHF_SHIELDREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, true);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, dt, true);
 
        if(vehic.vehicle_flags  & VHF_HEALTHREGEN)
-               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, false);
+               vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, dt, false);
 
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
        //this.vehicle_ammo2 = vehic.tur_head.frame;
@@ -337,24 +313,18 @@ bool spiderbot_frame(entity this)
 
 void spiderbot_exit(entity this, int eject)
 {
-       entity e;
        vector spot;
 
-       e = findchain(classname,"spiderbot_rocket");
-       while(e)
+       IL_EACH(g_projectiles, it.owner == this.owner && it.classname == "spiderbot_rocket",
        {
-               if(e.owner == this.owner)
-               {
-                       e.realowner = this.owner;
-                       e.owner = NULL;
-               }
-               e = e.chain;
-       }
+               it.realowner = this.owner;
+               it.owner = NULL;
+       });
 
        setthink(this, vehicles_think);
        this.nextthink = time;
        this.frame = 5;
-       this.movetype = MOVETYPE_WALK;
+       set_movetype(this, MOVETYPE_WALK);
 
        if(!this.owner)
                return;
@@ -405,7 +375,7 @@ void spiderbot_headfade(entity this)
                        sound (this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
                        Send_Effect(EFFECT_EXPLOSION_BIG, this.origin + '0 0 100', '0 0 0', 1);
                }
-               remove(this);
+               delete(this);
        }
 }
 
@@ -440,7 +410,7 @@ void spiderbot_blowup(entity this)
 
        vector org = gettaginfo(this, gettagindex(this, "tag_head"));
        setorigin(h, org);
-       h.movetype = MOVETYPE_BOUNCE;
+       set_movetype(h, MOVETYPE_BOUNCE);
        h.solid = SOLID_BBOX;
        h.velocity = v_up * (500 + random() * 500) + randomvec() * 128;
        h.modelflags = MF_ROCKET;
@@ -456,14 +426,14 @@ void spiderbot_blowup(entity this)
 
        org = gettaginfo(this.tur_head, gettagindex(this.tur_head, "tag_hardpoint01"));
        setorigin(g1, org);
-       g1.movetype = MOVETYPE_TOSS;
+       set_movetype(g1, MOVETYPE_TOSS);
        g1.solid = SOLID_CORPSE;
        g1.velocity = v_forward * 700 + (randomvec() * 32);
        g1.avelocity = randomvec() * 180;
 
        org = gettaginfo(this.tur_head, gettagindex(this.tur_head, "tag_hardpoint02"));
        setorigin(g2, org);
-       g2.movetype = MOVETYPE_TOSS;
+       set_movetype(g2, MOVETYPE_TOSS);
        g2.solid = SOLID_CORPSE;
        g2.velocity = v_forward * 700 + (randomvec() * 32);
        g2.avelocity = randomvec() * 180;
@@ -478,7 +448,7 @@ void spiderbot_blowup(entity this)
        RadiusDamage (this, this.enemy, 250, 15, 250, NULL, NULL, 250, DEATH_VH_SPID_DEATH.m_id, NULL);
 
        this.alpha = this.tur_head.alpha = this.gun1.alpha = this.gun2.alpha = -1;
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.deadflag = DEAD_DEAD;
        this.solid = SOLID_NOT;
        this.tur_head.effects &= ~EF_FLAME;
@@ -536,8 +506,8 @@ bool spiderbot_impulse(entity this, int _imp)
 
 spawnfunc(vehicle_spiderbot)
 {
-       if(!autocvar_g_vehicle_spiderbot) { remove(this); return; }
-       if(!vehicle_initialize(this, VEH_SPIDERBOT, false)) { remove(this); return; }
+       if(!autocvar_g_vehicle_spiderbot) { delete(this); return; }
+       if(!vehicle_initialize(this, VEH_SPIDERBOT, false)) { delete(this); return; }
 }
 
 METHOD(Spiderbot, vr_impact, void(Spiderbot thisveh, entity instance))
@@ -548,7 +518,7 @@ METHOD(Spiderbot, vr_impact, void(Spiderbot thisveh, entity instance))
 METHOD(Spiderbot, vr_enter, void(Spiderbot thisveh, entity instance))
 {
     instance.vehicle_weapon2mode = SBRM_GUIDE;
-    instance.movetype = MOVETYPE_WALK;
+    set_movetype(instance, MOVETYPE_WALK);
     CSQCVehicleSetup(instance.owner, 0);
     instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100;
     instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100;
@@ -578,7 +548,7 @@ METHOD(Spiderbot, vr_death, void(Spiderbot thisveh, entity instance))
     instance.tur_head.effects  |= EF_FLAME;
     instance.colormod                  = instance.tur_head.colormod = '-1 -1 -1';
     instance.frame                             = 10;
-    instance.movetype                  = MOVETYPE_TOSS;
+    set_movetype(instance, MOVETYPE_TOSS);
 
     CSQCModel_UnlinkEntity(instance); // networking the death scene would be a nightmare
 }
@@ -599,7 +569,7 @@ METHOD(Spiderbot, vr_spawn, void(Spiderbot thisveh, entity instance))
 
     instance.frame = 5;
     instance.tur_head.frame = 1;
-    instance.movetype = MOVETYPE_WALK;
+    set_movetype(instance, MOVETYPE_WALK);
     instance.solid = SOLID_SLIDEBOX;
     instance.alpha = instance.tur_head.alpha = instance.gun1.alpha = instance.gun2.alpha = 1;
     instance.tur_head.angles = '0 0 0';
@@ -633,8 +603,8 @@ METHOD(Spiderbot, vr_setup, void(Spiderbot thisveh, entity instance))
 
 #endif // SVQC
 #ifdef CSQC
-float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
-float autocvar_cl_vehicle_spiderbot_cross_size = 1;
+//float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
+//float autocvar_cl_vehicle_spiderbot_cross_size = 1;
 
 METHOD(Spiderbot, vr_hud, void(Spiderbot thisveh))
 {
diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qh b/qcsrc/common/vehicles/vehicle/spiderbot.qh
new file mode 100644 (file)
index 0000000..a594ace
--- /dev/null
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "spiderbot_weapons.qh"
+
+CLASS(Spiderbot, Vehicle)
+/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE);
+/* mins       */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10');
+/* maxs       */ ATTRIB(Spiderbot, maxs, vector, '75 75 125');
+/* view offset*/ ATTRIB(Spiderbot, view_ofs, vector, '0 0 70');
+/* view dist  */ ATTRIB(Spiderbot, height, float, 170);
+/* model         */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm");
+/* model         */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm");
+/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm");
+/* hud_model  */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm");
+/* tags       */ ATTRIB(Spiderbot, tag_head, string, "tag_head");
+/* tags       */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud");
+/* tags       */ ATTRIB(Spiderbot, tag_view, string, "");
+/* netname    */ ATTRIB(Spiderbot, netname, string, "spiderbot");
+/* fullname   */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot"));
+/* icon       */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider");
+ENDCLASS(Spiderbot)
+
+REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot));
index 27177019ef8f3a398106f2d6346991099fb0d02d..5f7a2c31c8a928cbbc5efec1d99e869a0c01a6fc 100644 (file)
@@ -48,21 +48,18 @@ void spiderbot_rocket_guided(entity this)
 
 void spiderbot_guide_release(entity this)
 {
-    entity rkt;
-    rkt = findchainentity(realowner, this.owner);
-    if(!rkt)
-        return;
-
-    crosshair_trace(this.owner);
-    while(rkt)
+    bool donetrace = false;
+    IL_EACH(g_projectiles, it.realowner == this.owner && getthink(it) == spiderbot_rocket_guided,
     {
-        if(getthink(rkt) == spiderbot_rocket_guided)
+        if(!donetrace) // something exists, let's trace!
         {
-            rkt.pos1 = trace_endpos;
-            setthink(rkt, spiderbot_rocket_unguided);
+            donetrace = true;
+            crosshair_trace(this.owner);
         }
-        rkt = rkt.chain;
-    }
+
+        it.pos1 = trace_endpos;
+        setthink(it, spiderbot_rocket_unguided);
+    });
 }
 
 float spiberbot_calcartillery_flighttime;
@@ -140,7 +137,7 @@ vector spiberbot_calcartillery(vector org, vector tgt, float ht)
 }
 
 void spiderbot_rocket_do(entity this)
-{;
+{
     vector v;
     entity rocket = NULL;
 
@@ -196,7 +193,7 @@ void spiderbot_rocket_do(entity this)
             float _dist = (random() * autocvar_g_vehicle_spiderbot_rocket_radius) + vlen(v - trace_endpos);
             _dist -= (random() * autocvar_g_vehicle_spiderbot_rocket_radius) ;
             rocket.nextthink  = time + (_dist / autocvar_g_vehicle_spiderbot_rocket_speed);
-            setthink(rocket, vehicles_projectile_explode);
+            setthink(rocket, vehicles_projectile_explode_think);
 
             if(PHYS_INPUT_BUTTON_ATCK2(this.owner) && this.tur_head.frame == 1)
                 this.wait = -10;
@@ -232,7 +229,7 @@ void spiderbot_rocket_do(entity this)
             float h2 = 0.75 * vlen(rocket.pos1 - v);
 
             rocket.velocity  = spiberbot_calcartillery(v, rocket.pos1, ((h1 < h2) ? h1 : h2));
-            rocket.movetype  = MOVETYPE_TOSS;
+            set_movetype(rocket, MOVETYPE_TOSS);
             rocket.gravity   = 1;
             //setthink(rocket, spiderbot_rocket_artillery);
         break;
index 61e2b02501fc74e14eb6f064304116dde8415936..199d9cfa47d8debf5d1d6b7d459af1b4cca7b9ad 100644 (file)
@@ -1,11 +1,12 @@
 #pragma once
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 #ifdef SVQC
 void spiderbot_rocket_do(entity this);
 #endif
 
+#ifdef SVQC
 // 400 (x2) DPS
 float autocvar_g_vehicle_spiderbot_minigun_damage = 24;
 float autocvar_g_vehicle_spiderbot_minigun_refire = 0.06;
@@ -30,3 +31,4 @@ float autocvar_g_vehicle_spiderbot_rocket_health = 100;
 float autocvar_g_vehicle_spiderbot_rocket_noise = 0.2;
 float autocvar_g_vehicle_spiderbot_rocket_turnrate = 0.25;
 float autocvar_g_vehicle_spiderbot_rocket_lifetime = 20;
+#endif
diff --git a/qcsrc/common/vehicles/vehicles.qc b/qcsrc/common/vehicles/vehicles.qc
new file mode 100644 (file)
index 0000000..5e77365
--- /dev/null
@@ -0,0 +1 @@
+#include "vehicles.qh"
diff --git a/qcsrc/common/vehicles/vehicles.qh b/qcsrc/common/vehicles/vehicles.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 23b30792229f7e1e02f2720461e6fabb2720e644..300aed4fc7f99a36b1367a90e09a991cbb2fc6b9 100644 (file)
@@ -1,3 +1,4 @@
+#include "viewloc.qh"
 #include "util.qh"
 
 #if defined(CSQC)
@@ -74,6 +75,10 @@ void viewloc_SetTags(entity this)
 }
 
 vector old_camera_angle = '0 0 0';
+bool autocvar_cam_snap_close;
+bool autocvar_cam_track;
+bool autocvar_cam_snap_hard;
+bool autocvar_cam_snap_unlock;
 void viewloc_SetViewLocation()
 {
        entity view = CSQCModel_server2csqc(player_localentnum - 1);
@@ -81,7 +86,7 @@ void viewloc_SetViewLocation()
        //NOTE: the "cam_" cvars sould probably be changed out with a spawnflag or an entity key. I have it like this for my testing -- Player_2
        if(view.viewloc && !wasfreed(view.viewloc) && view.viewloc.enemy && view.viewloc.goalentity)
        {
-               vector position_a, position_b, camera_position, camera_angle, forward, backward;
+               vector position_a, position_b, camera_position, camera_angle = '0 0 0', forward, backward;
                //vector scratch;
 
                position_a = view.viewloc.enemy.origin;
@@ -99,20 +104,18 @@ void viewloc_SetViewLocation()
                        camera_position = vec_bounds_in(view.origin, position_a, position_b);
 
 
-               camera_angle = '0 0 0';
-
                // a tracking camera follows the player when it leaves the world box
-               if (cvar("cam_track")) {
+               if (autocvar_cam_track) {
                        camera_angle = aim_vec (camera_position, view.origin);
                }
 
                // hard snap changes the angle as soon as it crosses over the nearest 90 degree mark
-               if (cvar("cam_snap_hard")){
+               if (autocvar_cam_snap_hard){
                        camera_angle = angle_snap_vec(aim_vec(camera_position, view.origin), 90);
                }
 
                // tries to avoid snapping unless it *really* needs to
-               if (cvar("cam_snap_close")){
+               if (autocvar_cam_snap_close){
 
                        // like hard snap, but don't snap angles yet.
                        camera_angle = aim_vec(camera_position, view.origin);
@@ -130,15 +133,15 @@ void viewloc_SetViewLocation()
                }
 
                //unlocking this allows the camera to look up and down. this also allows a top-down view.
-               if (!cvar("cam_snap_unlock")) {
+               if (!autocvar_cam_snap_unlock) {
                        camera_angle_x = 0;
                        camera_angle_z = 0;
                }
 
 #if 0
-               LOG_TRACE(vtos(camera_position), "\n");
-               LOG_TRACE(vtos(old_camera_angle), "\n");
-               LOG_TRACE(vtos(camera_angle), "\n");
+               LOG_TRACE(vtos(camera_position));
+               LOG_TRACE(vtos(old_camera_angle));
+               LOG_TRACE(vtos(camera_angle));
 #endif
 
                freeze_org = getpropertyvec(VF_ORIGIN);
index 4c4d05f92653d6d76deb44fde08bc2cb48721f44..7725d2d8fd040ab506048fb91ce787e0a0cbc676 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef VIEWLOC_H
-#define VIEWLOC_H
+#pragma once
 
 .entity viewloc;
 
@@ -11,5 +10,3 @@ void viewloc_SetViewLocation();
 void viewloc_SetTags(entity this);
 
 #endif
-
-#endif
diff --git a/qcsrc/common/weapons/_all.inc b/qcsrc/common/weapons/_all.inc
new file mode 100644 (file)
index 0000000..213c39c
--- /dev/null
@@ -0,0 +1,2 @@
+#include "_all.qh"
+#include "all.qc"
diff --git a/qcsrc/common/weapons/_all.qh b/qcsrc/common/weapons/_all.qh
new file mode 100644 (file)
index 0000000..671dde0
--- /dev/null
@@ -0,0 +1,2 @@
+#pragma once
+#include "all.qh"
index 897e2caef2f5975e3627458dd0d6894d7da9a3d8..763dd6e8de83646cefda1786d8f6b51d3ce1dbaf 100644 (file)
@@ -1,8 +1,7 @@
+#include "all.qh"
 #ifndef WEAPONS_ALL_C
 #define WEAPONS_ALL_C
 
-#include "all.qh"
-
 #if defined(CSQC)
        #include <client/defs.qh>
        #include "../constants.qh"
@@ -19,6 +18,7 @@
        #include <lib/csqcmodel/cl_model.qh>
 #elif defined(MENUQC)
 #elif defined(SVQC)
+       #include <common/items/_mod.qh>
     #include <lib/warpzone/anglestransform.qh>
     #include <lib/warpzone/common.qh>
     #include <lib/warpzone/util_server.qh>
@@ -27,7 +27,7 @@
     #include "../stats.qh"
     #include "../teams.qh"
     #include "../util.qh"
-    #include "../monsters/all.qh"
+    #include "../monsters/_mod.qh"
     #include "config.qh"
     #include <server/weapons/csqcprojectile.qh>
     #include <server/weapons/tracing.qh>
     #include <server/defs.qh>
     #include "../notifications/all.qh"
     #include "../deathtypes/all.qh"
-    #include <server/mutators/all.qh>
+    #include <server/mutators/_mod.qh>
     #include "../mapinfo.qh"
-    #include <server/command/common.qh>
+    #include <server/command/_mod.qh>
     #include <lib/csqcmodel/sv_model.qh>
     #include <server/portals.qh>
     #include <server/g_hook.qh>
 #endif
-#ifndef MENUQC
+#ifdef GAMEQC
        #include "calculations.qc"
 #endif
 #ifdef SVQC
@@ -204,7 +204,7 @@ void W_RandomWeapons(entity e, float n)
                RandomSelection_Init();
                FOREACH(Weapons, it != WEP_Null, {
                        if (remaining & (it.m_wepset))
-                               RandomSelection_Add(it, 0, string_null, 1, 1);
+                               RandomSelection_AddEnt(it, 1, 1);
                });
                Weapon w = RandomSelection_chosen_ent;
                result |= WepSet_FromWeapon(w);
@@ -275,7 +275,7 @@ string W_Model(string w_mdl)
        return M_ARGV(1, string);
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
 {
        switch (algn)
@@ -392,7 +392,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
        if (name == "")
        {
                this.model = "";
-               if (this.weaponchild) remove(this.weaponchild);
+               if (this.weaponchild) delete(this.weaponchild);
                this.weaponchild = NULL;
                this.movedir = '0 0 0';
                this.spawnorigin = '0 0 0';
@@ -436,7 +436,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                }
                else
                {
-                       if (this.weaponchild) remove(this.weaponchild);
+                       if (this.weaponchild) delete(this.weaponchild);
                        this.weaponchild = NULL;
                }
 
@@ -462,7 +462,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                        }
                        else
                        {
-                               LOG_WARNINGF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong\n",
+                               LOG_WARNF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong",
                                        this.model);
                                this.movedir = '0 0 0';
                        }
@@ -481,7 +481,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                        }
                        else
                        {
-                               LOG_WARNINGF("weapon model %s does not support the 'shell' tag, will display casings wrong\n",
+                               LOG_WARNF("weapon model %s does not support the 'shell' tag, will display casings wrong",
                                        this.model);
                                this.spawnorigin = this.movedir;
                        }
@@ -503,7 +503,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
                        }
                        else
                        {
-                               LOG_WARNINGF(
+                               LOG_WARNF(
                                        "weapon model %s does not support the 'handle' tag "
                                        "and neither does the v_ model support the 'shot' tag, "
                                        "will display muzzle flashes TOTALLY wrong\n",
@@ -547,7 +547,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
 }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 
 REGISTER_NET_TEMP(wframe)
 #ifdef CSQC
index dd691a6921ca8ab2b9d619ee61df8731515189d5..a805ce5fd4210a014b938bcf41c7cf46df4ad034 100644 (file)
@@ -1,7 +1,6 @@
-#ifndef WEAPONS_ALL_H
-#define WEAPONS_ALL_H
+#pragma once
 
-#include <common/command/all.qh>
+#include <common/command/_mod.qh>
 #include <common/stats.qh>
 #include "config.qh"
 
@@ -19,22 +18,22 @@ WepSet ReadWepSet();
 
 #include "weapon.qh"
 
-#ifndef MENUQC
+#ifdef GAMEQC
 #include "calculations.qh"
 #include <common/models/all.qh>
 #endif
 
 #include <common/util.qh>
 
-#ifdef SVQC
-#include <server/bot/aim.qh>
-#endif
-
 REGISTRY(Weapons, 72) // Increase as needed. Can be up to 72.
 #define Weapons_from(i) _Weapons_from(i, WEP_Null)
 REGISTER_REGISTRY(Weapons)
 STATIC_INIT(WeaponPickup) { FOREACH(Weapons, true, it.m_pickup = NEW(WeaponPickup, it)); }
 
+#ifdef SVQC
+#include <server/bot/api.qh>
+#endif
+
 .WepSet m_wepset;
 #define WEPSET(id) (WEP_##id.m_wepset)
 #define WepSet_FromWeapon(it) ((it).m_wepset)
@@ -144,10 +143,12 @@ X(weaponreplace, string)
 X(weaponstartoverride, float)
 X(weaponstart, float)
 X(weaponthrowable, float)
+#ifdef SVQC
 X(reload_ammo, float)
 .float reloading_ammo = reload_ammo;
 X(reload_time, float)
 .float reloading_time = reload_time;
+#endif
 #undef X
 
 
@@ -299,6 +300,7 @@ REGISTRY_CHECK(Weapons)
 
 STATIC_INIT(register_weapons_done)
 {
+       string inaccessible = "";
     FOREACH(Weapons, true, {
         WepSet set = it.m_wepset = _WepSet_FromWeapon(it.m_id = i);
         WEPSET_ALL |= set;
@@ -309,8 +311,9 @@ STATIC_INIT(register_weapons_done)
         if (imp <= WEP_IMPULSE_END)
             localcmd(sprintf("alias weapon_%s \"impulse %d\"\n", it.netname, imp));
         else
-            LOG_TRACEF("Impulse limit exceeded, weapon will not be directly accessible: %s\n", it.netname);
+               inaccessible = strcat(inaccessible, "\n", it.netname);
     });
+    if (inaccessible) LOG_TRACEF("Impulse limit exceeded, weapon(s) will not be directly accessible: %s", inaccessible);
     #ifdef CSQC
     FOREACH(Weapons, true, it.wr_init(it));
     #endif
@@ -321,14 +324,14 @@ STATIC_INIT(register_weapons_done)
     weaponorder_byid = strzone(substring(weaponorder_byid, 1, -1));
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 
 .entity weaponchild;
 .entity exteriorweaponentity;
-vector weaponentity_glowmod(Weapon wep, int c)
+vector weaponentity_glowmod(Weapon wep, entity actor, int c)
 {
     vector g;
-    if (!(g = wep.wr_glow(wep))) g = colormapPaletteColor(c & 0x0F, true) * 2;
+    if (!(g = wep.wr_glow(wep, actor))) g = colormapPaletteColor(c & 0x0F, true) * 2;
     return g;
 }
 
@@ -358,5 +361,3 @@ ENUMCLASS_END(WFRAME)
 vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn);
 void CL_WeaponEntity_SetModel(entity this, string name, bool _anim);
 #endif
-
-#endif
index e8307f39d20068151236b4e26d9155c820d61d2f..c35b1930c44672995718ab746454da9aabfcd243 100644 (file)
@@ -1,3 +1,5 @@
+#include "calculations.qh"
+
 // =============================
 //  Explosion Force Calculation
 // =============================
@@ -256,7 +258,7 @@ vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float
                default:
                        error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!");
        }
-               
+
        return '0 0 0';
        /*
         * how to derive falloff functions:
index 4feed9fc549e16a48d44c204435f4a83e21672d7..c349eeca4f355a3b8b55ee588e65cfaf5471739e 100644 (file)
@@ -1,6 +1,6 @@
-#ifndef CALCULATIONS_H
-#define CALCULATIONS_H
+#pragma once
+
 vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor);
 vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle);
 int W_GetGunAlignment(entity player);
-#endif
+float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v);
index 26a05d10ac739f4b609a5ce39384be48e9ef86d8..4f6177b478f17bc870366358618c21d44133f3a7 100644 (file)
@@ -1,8 +1,8 @@
+#include "config.qh"
 #if defined(CSQC)
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include "../util.qh"
-    #include "config.qh"
     #include "all.qh"
 #endif
 
index d88e18194bf7c37af8991483eb4448f7448e8a50..753147307b3a253e9fb86b5b056bcbdab222c096 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef WEAPONS_CONFIG_H
-#define WEAPONS_CONFIG_H
+#pragma once
 
 #ifdef SVQC
 // ==========================
@@ -37,4 +36,3 @@ string wep_config_queue[MAX_WEP_CONFIG];
                cvar(sprintf("g_balance_%s_%s", #wepname, #name)))) }
 
 #endif
-#endif
index 4de39db1772f31ab462467f55a0b76e9f3eeb3ec..b287b148f3175727cebf8fa1e44a288099ae8e2b 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef WEAPON_H
-#define WEAPON_H
+#pragma once
+
 #include <common/items/item/pickup.qh>
 #include <common/stats.qh>
 
@@ -54,7 +54,7 @@ const int WS_READY  = 4;
 
 /** fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" */
 CLASS(Weapon, Object)
-       ATTRIB(Weapon, m_id, int, 0)
+       ATTRIB(Weapon, m_id, int, 0);
     /** A: WEPSET_id : WEPSET_... */
     ATTRIB(Weapon, weapons, WepSet, '0 0 0');
     /** M: ammotype  : main ammo field */
@@ -70,7 +70,7 @@ CLASS(Weapon, Object)
     /** M: modelname : name of model (without g_ v_ or h_ prefixes) */
     ATTRIB(Weapon, mdl, string, "");
     /** M: model MDL_id_ITEM */
-    ATTRIB(Weapon, m_model, entity, NULL);
+    ATTRIB(Weapon, m_model, entity);
     /** M: crosshair : per-weapon crosshair: "CrosshairImage Size" */
     ATTRIB(Weapon, w_crosshair, string, "gfx/crosshair1");
     /** A: crosshair : per-weapon crosshair size (argument two of "crosshair" field) */
@@ -82,7 +82,7 @@ CLASS(Weapon, Object)
     /** M: wepname   : human readable name */
     ATTRIB(Weapon, m_name, string, "AOL CD Thrower");
 
-    ATTRIB(Weapon, m_pickup, entity, NULL);
+    ATTRIB(Weapon, m_pickup, entity);
 
     /** (SERVER) setup weapon data */
     METHOD(Weapon, wr_setup, void(Weapon this, entity actor)) {}
@@ -107,9 +107,9 @@ CLASS(Weapon, Object)
     /** (CLIENT) impact effect for weapon explosion */
     METHOD(Weapon, wr_impacteffect, void(Weapon this, entity actor)) {}
     /** (SERVER) called whenever a player dies */
-    METHOD(Weapon, wr_playerdeath, void(Weapon this, entity actor)) {}
+    METHOD(Weapon, wr_playerdeath, void(Weapon this, entity actor, .entity weaponentity)) {}
     /** (SERVER) logic to run when weapon is lost */
-    METHOD(Weapon, wr_gonethink, void(Weapon this, entity actor)) {}
+    METHOD(Weapon, wr_gonethink, void(Weapon this, entity actor, .entity weaponentity)) {}
     /** (ALL)    dump weapon cvars to config in data directory (see: sv_cmd dumpweapons) */
     METHOD(Weapon, wr_config, void(Weapon this)) {}
     /** (CLIENT) weapon specific zoom reticle */
@@ -117,8 +117,10 @@ CLASS(Weapon, Object)
         // no weapon specific image for this weapon
         return false;
     }
+    /** (CLIENT) weapon specific view model */
+    METHOD(Weapon, wr_viewmodel, string(Weapon this, entity wep)) { return string_null; }
     /** (CLIENT) weapon specific glow */
-    METHOD(Weapon, wr_glow, vector(Weapon this)) { return '0 0 0'; }
+    METHOD(Weapon, wr_glow, vector(Weapon this, entity actor)) { return '0 0 0'; }
     /** (SERVER) the weapon is dropped */
     METHOD(Weapon, wr_drop, void(Weapon this, entity actor)) {}
     /** (SERVER) a weapon is picked up */
@@ -130,23 +132,23 @@ CLASS(Weapon, Object)
        }
 ENDCLASS(Weapon)
 
-#include <common/items/all.qh>
+#include <common/items/_mod.qh>
 CLASS(WeaponPickup, Pickup)
-    ATTRIB(WeaponPickup, m_weapon, Weapon, NULL)
-    ATTRIB(WeaponPickup, m_name, string, string_null)
-#ifndef MENUQC
-    ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP)
+    ATTRIB(WeaponPickup, m_weapon, Weapon);
+    ATTRIB(WeaponPickup, m_name, string);
+#ifdef GAMEQC
+    ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP);
 #endif
 #ifdef SVQC
-    ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON)
+    ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON);
     float weapon_pickupevalfunc(entity player, entity item);
-    ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc)
+    ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc);
 #endif
     CONSTRUCTOR(WeaponPickup, Weapon w) {
         CONSTRUCT(WeaponPickup);
         this.m_weapon = w;
         this.m_name = w.m_name;
-#ifndef MENUQC
+#ifdef GAMEQC
         this.m_model = w.m_model;
 #endif
 #ifdef SVQC
@@ -158,7 +160,7 @@ CLASS(WeaponPickup, Pickup)
     {
         bool b = Item_GiveTo(item, player);
         if (b) {
-            LOG_TRACEF("entity %i picked up %s\n", player, this.m_name);
+            LOG_TRACEF("entity %i picked up %s", player, this.m_name);
         }
         return b;
     }
@@ -175,11 +177,6 @@ ENDCLASS(OffhandWeapon)
 
 const int MAX_SHOT_DISTANCE = 32768;
 
-// weapon pickup ratings for bot logic
-const int BOT_PICKUP_RATING_LOW  =  2500;
-const int BOT_PICKUP_RATING_MID  =  5000;
-const int BOT_PICKUP_RATING_HIGH = 10000;
-
 // weapon flags
 const int WEP_TYPE_OTHER          =  0x00; // not for damaging people
 const int WEP_TYPE_SPLASH         =  0x01; // splash damage
@@ -191,6 +188,8 @@ const int WEP_FLAG_HIDDEN         =  0x40; // hides from menu
 const int WEP_FLAG_RELOADABLE     =  0x80; // can has reload
 const int WEP_FLAG_SUPERWEAPON    = 0x100; // powerup timer
 const int WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
+const int WEP_TYPE_MELEE_PRI      = 0x400; // primary attack is melee swing (for animation)
+const int WEP_TYPE_MELEE_SEC      = 0x800; // secondary attack is melee swing (for animation)
 
 // variables:
 string weaponorder_byid;
@@ -214,5 +213,3 @@ int GetAmmoStat(.int ammotype);
 
 string W_Sound(string w_snd);
 string W_Model(string w_mdl);
-
-#endif
index 9262698daa6c09a8f63840bb05ecf88ce5528499..4c9415305ab64b5fa68b765df55ca547ed734c3f 100644 (file)
@@ -1,12 +1,13 @@
+#include "arc.qh"
 #ifndef IMPLEMENTATION
 CLASS(Arc, Weapon)
-/* ammotype  */ ATTRIB(Arc, ammo_field, .int, ammo_cells)
-/* impulse   */ ATTRIB(Arc, impulse, int, 3)
+/* ammotype  */ ATTRIB(Arc, ammo_field, .int, ammo_cells);
+/* impulse   */ ATTRIB(Arc, impulse, int, 3);
 /* flags     */ ATTRIB(Arc, spawnflags, int, WEP_FLAG_NORMAL);
 /* rating    */ ATTRIB(Arc, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 /* color     */ ATTRIB(Arc, wpcolor, vector, '1 1 1');
 /* modelname */ ATTRIB(Arc, mdl, string, "arc");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Arc, m_model, Model, MDL_ARC_ITEM);
 #endif
 /* crosshair */ ATTRIB(Arc, w_crosshair, string, "gfx/crosshairhlac");
@@ -74,7 +75,7 @@ ENDCLASS(Arc)
 REGISTER_WEAPON(ARC, arc, NEW(Arc));
 
 
-#ifndef MENUQC
+#ifdef GAMEQC
 const float ARC_MAX_SEGMENTS = 20;
 vector arc_shotorigin[4];
 .vector beam_start;
@@ -101,7 +102,7 @@ const int ARC_SF_LOCALMASK =   ARC_SF_START | ARC_SF_WANTDIR | ARC_SF_BEAMDIR;
 #endif
 #ifdef SVQC
 .entity arc_beam;
-.bool arc_BUTTON_ATCK_prev; // for better animation control
+.bool arc_BUTTON_ATCK_prev[MAX_WEAPONSLOTS]; // for better animation control
 .float beam_prev;
 .float beam_initialized;
 .float beam_bursting;
@@ -199,14 +200,17 @@ bool W_Arc_Beam_Send(entity this, entity to, int sf)
 
 void Reset_ArcBeam(entity player, vector forward)
 {
-       if (!player.arc_beam) {
-               return;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               if(!player.(weaponentity).arc_beam)
+                       continue;
+               player.(weaponentity).arc_beam.beam_dir = forward;
+               player.(weaponentity).arc_beam.beam_teleporttime = time;
        }
-       player.arc_beam.beam_dir = forward;
-       player.arc_beam.beam_teleporttime = time;
 }
 
-float Arc_GetHeat_Percent(entity player)
+float Arc_GetHeat_Percent(entity player, .entity weaponentity)
 {
        if ( WEP_CVAR(arc, overheat_max) <= 0 ||  WEP_CVAR(arc, overheat_max) <= 0 )
        {
@@ -214,8 +218,8 @@ float Arc_GetHeat_Percent(entity player)
                return 0;
        }
 
-       if ( player.arc_beam )
-               return player.arc_beam.beam_heat/WEP_CVAR(arc, overheat_max);
+       if ( player.(weaponentity).arc_beam )
+               return player.(weaponentity).arc_beam.beam_heat/WEP_CVAR(arc, overheat_max);
 
        if ( player.arc_overheat > time )
        {
@@ -225,23 +229,23 @@ float Arc_GetHeat_Percent(entity player)
 
        return 0;
 }
-void Arc_Player_SetHeat(entity player)
+void Arc_Player_SetHeat(entity player, .entity weaponentity)
 {
-       player.arc_heat_percent = Arc_GetHeat_Percent(player);
+       player.arc_heat_percent = Arc_GetHeat_Percent(player, weaponentity);
        //dprint("Heat: ",ftos(player.arc_heat_percent*100),"%\n");
 }
 
-void W_Arc_Bolt_Explode(entity this)
+void W_Arc_Bolt_Explode(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
-       RadiusDamage(this, this.realowner, WEP_CVAR(arc, bolt_damage), WEP_CVAR(arc, bolt_edgedamage), WEP_CVAR(arc, bolt_radius), NULL, NULL, WEP_CVAR(arc, bolt_force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR(arc, bolt_damage), WEP_CVAR(arc, bolt_edgedamage), WEP_CVAR(arc, bolt_radius), NULL, NULL, WEP_CVAR(arc, bolt_force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
 }
 
 void W_Arc_Bolt_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_Arc_Bolt_Explode(this);
+       W_Arc_Bolt_Explode(this, trigger);
 }
 
 void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -259,25 +263,26 @@ void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float dam
                W_PrepareExplosionByDamage(this, attacker, getthink(this));
 }
 
-void W_Arc_Bolt_Touch(entity this)
+void W_Arc_Bolt_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       this.use(this, NULL, NULL);
+       PROJECTILE_TOUCH(this, toucher);
+       this.use(this, NULL, toucher);
 }
 
-void W_Arc_Attack_Bolt(Weapon thiswep, entity actor)
+void W_Arc_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity missile;
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR(arc, bolt_ammo));
 
-       W_SetupShot(actor, false, 2, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR(arc, bolt_damage));
+       W_SetupShot(actor, weaponentity, false, 2, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR(arc, bolt_damage));
 
        Send_Effect(EFFECT_ARC_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        missile = new(missile);
        missile.owner = missile.realowner = actor;
        missile.bot_dodge = true;
+       IL_PUSH(g_bot_dodge, missile);
        missile.bot_dodgerating = WEP_CVAR(arc, bolt_damage);
 
        missile.takedamage = DAMAGE_YES;
@@ -285,6 +290,7 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor)
        missile.damageforcescale = WEP_CVAR(arc, bolt_damageforcescale);
        missile.event_damage = W_Arc_Bolt_Damage;
        missile.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, missile);
 
        settouch(missile, W_Arc_Bolt_Touch);
        missile.use = W_Arc_Bolt_Explode_use;
@@ -295,7 +301,7 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor)
        setorigin(missile, w_shotorg);
        setsize(missile, '0 0 0', '0 0 0');
 
-       missile.movetype = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        W_SetupProjVelocity_PRE(missile, arc, bolt_);
 
        missile.angles = vectoangles(missile.velocity);
@@ -309,13 +315,13 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor)
 
 void W_Arc_Beam_Think(entity this)
 {
-       if(this != this.owner.arc_beam)
+       .entity weaponentity = this.weaponentity_fld;
+       if(this != this.owner.(weaponentity).arc_beam)
        {
-               remove(this);
+               delete(this);
                return;
        }
 
-
        float burst = 0;
        if( (PHYS_INPUT_BUTTON_ATCK2(this.owner) && !WEP_CVAR(arc, bolt)) || this.beam_bursting)
        {
@@ -333,11 +339,11 @@ void W_Arc_Beam_Think(entity this)
                ||
                IS_DEAD(this.owner)
                ||
-               gameover
+               forbidWeaponUse(this.owner)
                ||
-               (!PHYS_INPUT_BUTTON_ATCK(this.owner) && !burst )
+               PS(this.owner).m_switchweapon != WEP_ARC
                ||
-               STAT(FROZEN, this.owner)
+               (!PHYS_INPUT_BUTTON_ATCK(this.owner) && !burst )
                ||
                this.owner.vehicle
                ||
@@ -371,7 +377,7 @@ void W_Arc_Beam_Think(entity this)
                        }
                }
 
-               if(this == this.owner.arc_beam) { this.owner.arc_beam = NULL; }
+               if(this == this.owner.(weaponentity).arc_beam) { this.owner.(weaponentity).arc_beam = NULL; }
                entity own = this.owner;
                Weapon w = WEP_ARC;
                if(!w.wr_checkammo1(w, own) && !w.wr_checkammo2(w, own))
@@ -380,7 +386,7 @@ void W_Arc_Beam_Think(entity this)
                        // note: this doesn't force the switch
                        W_SwitchToOtherWeapon(own);
                }
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -407,6 +413,7 @@ void W_Arc_Beam_Think(entity this)
 
        W_SetupShot_Range(
                this.owner,
+               weaponentity, // TODO
                true,
                0,
                SND_Null,
@@ -674,29 +681,31 @@ void W_Arc_Beam_Think(entity this)
        this.nextthink = time;
 }
 
-void W_Arc_Beam(float burst, entity actor)
+void W_Arc_Beam(float burst, entity actor, .entity weaponentity)
 {
 
        // only play fire sound if 1 sec has passed since player let go the fire button
        if(time - actor.beam_prev > 1)
                sound(actor, CH_WEAPON_A, SND_ARC_FIRE, VOL_BASE, ATTN_NORM);
 
-       entity beam = actor.arc_beam = new(W_Arc_Beam);
+       entity beam = actor.(weaponentity).arc_beam = new(W_Arc_Beam);
+       beam.weaponentity_fld = weaponentity;
        beam.solid = SOLID_NOT;
        setthink(beam, W_Arc_Beam_Think);
        beam.owner = actor;
-       beam.movetype = MOVETYPE_NONE;
+       set_movetype(beam, MOVETYPE_NONE);
        beam.bot_dodge = true;
+       IL_PUSH(g_bot_dodge, beam);
        beam.bot_dodgerating = WEP_CVAR(arc, beam_damage);
        beam.beam_bursting = burst;
        Net_LinkEntity(beam, false, 0, W_Arc_Beam_Send);
 
        getthink(beam)(beam);
 }
-void Arc_Smoke(entity actor)
+void Arc_Smoke(entity actor, .entity weaponentity)
 {
        makevectors(actor.v_angle);
-       W_SetupShot_Range(actor,true,0,SND_Null,0,0,0);
+       W_SetupShot_Range(actor,weaponentity,true,0,SND_Null,0,0,0);
 
        vector smoke_origin = w_shotorg + actor.velocity*frametime;
        if ( actor.arc_overheat > time )
@@ -713,10 +722,10 @@ void Arc_Smoke(entity actor)
                        }
                }
        }
-       else if ( actor.arc_beam && WEP_CVAR(arc, overheat_max) > 0 &&
-                       actor.arc_beam.beam_heat > WEP_CVAR(arc, overheat_min) )
+       else if ( actor.(weaponentity).arc_beam && WEP_CVAR(arc, overheat_max) > 0 &&
+                       actor.(weaponentity).arc_beam.beam_heat > WEP_CVAR(arc, overheat_min) )
        {
-               if ( random() < (actor.arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) /
+               if ( random() < (actor.(weaponentity).arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) /
                                ( WEP_CVAR(arc, overheat_max)-WEP_CVAR(arc, overheat_min) ) )
                        Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 );
        }
@@ -754,16 +763,17 @@ METHOD(Arc, wr_aim, void(entity thiswep, entity actor))
 }
 METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
 {
-    Arc_Player_SetHeat(actor);
-    Arc_Smoke(actor);
+    Arc_Player_SetHeat(actor, weaponentity);
+    Arc_Smoke(actor, weaponentity);
 
     bool beam_fire2 = ((fire & 2) && !WEP_CVAR(arc, bolt));
+    int slot = weaponslot(weaponentity);
 
     if (time >= actor.arc_overheat)
-    if ((fire & 1) || beam_fire2 || actor.arc_beam.beam_bursting)
+    if ((fire & 1) || beam_fire2 || actor.(weaponentity).arc_beam.beam_bursting)
     {
 
-        if(actor.arc_BUTTON_ATCK_prev)
+        if(actor.arc_BUTTON_ATCK_prev[slot])
         {
             #if 0
             if(actor.animstate_startframe == actor.anim_shoot.x && actor.animstate_numframes == actor.anim_shoot.y)
@@ -773,16 +783,16 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i
                 weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), w_ready);
         }
 
-        if((!actor.arc_beam) || wasfreed(actor.arc_beam))
+        if((!actor.(weaponentity).arc_beam) || wasfreed(actor.(weaponentity).arc_beam))
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, boolean(beam_fire2), 0))
             {
-                W_Arc_Beam(boolean(beam_fire2), actor);
+                W_Arc_Beam(boolean(beam_fire2), actor, weaponentity);
 
-                if(!actor.arc_BUTTON_ATCK_prev)
+                if(!actor.arc_BUTTON_ATCK_prev[slot])
                 {
                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
-                    actor.arc_BUTTON_ATCK_prev = true;
+                    actor.arc_BUTTON_ATCK_prev[slot] = true;
                 }
             }
         }
@@ -793,19 +803,18 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(arc, bolt_refire)))
         {
-            W_Arc_Attack_Bolt(thiswep, actor);
+            W_Arc_Attack_Bolt(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready);
         }
     }
 
-    if(actor.arc_BUTTON_ATCK_prev)
+    if(actor.arc_BUTTON_ATCK_prev[slot])
     {
         sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM);
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
-        int slot = weaponslot(weaponentity);
         ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor);
     }
-    actor.arc_BUTTON_ATCK_prev = false;
+    actor.arc_BUTTON_ATCK_prev[slot] = false;
 
     #if 0
     if(fire & 2)
@@ -857,6 +866,8 @@ METHOD(Arc, wr_drop, void(entity thiswep, entity actor))
     weapon_dropevent_item.arc_cooldown = actor.arc_cooldown;
     actor.arc_overheat = 0;
     actor.arc_cooldown = 0;
+    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       actor.arc_BUTTON_ATCK_prev[slot] = false;
 }
 METHOD(Arc, wr_pickup, void(entity thiswep, entity actor))
 {
@@ -867,6 +878,20 @@ METHOD(Arc, wr_pickup, void(entity thiswep, entity actor))
         actor.arc_cooldown = weapon_dropevent_item.arc_cooldown;
     }
 }
+METHOD(Arc, wr_resetplayer, void(entity thiswep, entity actor))
+{
+    actor.arc_overheat = 0;
+    actor.arc_cooldown = 0;
+    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       actor.arc_BUTTON_ATCK_prev[slot] = false;
+}
+METHOD(Arc, wr_playerdeath, void(entity thiswep, entity actor, .entity weaponentity))
+{
+    actor.arc_overheat = 0;
+    actor.arc_cooldown = 0;
+    int slot = weaponslot(weaponentity);
+    actor.arc_BUTTON_ATCK_prev[slot] = false;
+}
 #endif
 #ifdef CSQC
 bool autocvar_cl_arcbeam_teamcolor = true;
@@ -1268,7 +1293,7 @@ void Draw_ArcBeam(entity this)
 
 void Remove_ArcBeam(entity this)
 {
-       remove(this.beam_muzzleentity);
+       delete(this.beam_muzzleentity);
        sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
 }
 
@@ -1285,6 +1310,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew)
 
                // set other main attributes of the beam
                this.draw = Draw_ArcBeam;
+               IL_PUSH(g_drawables, this);
                this.entremove = Remove_ArcBeam;
                this.move_time = time;
                loopsound(this, CH_SHOTS_SINGLE, SND(ARC_LOOP), VOL_BASE, ATTEN_NORM);
@@ -1377,7 +1403,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew)
        {
                this.beam_type = ReadByte();
 
-               vector beamcolor = ((autocvar_cl_arcbeam_teamcolor) ? colormapPaletteColor(stof(getplayerkeyvalue(this.sv_entnum - 1, "colors")) & 0x0F, true) : '1 1 1');
+               vector beamcolor = ((autocvar_cl_arcbeam_teamcolor) ? colormapPaletteColor(entcs_GetClientColors(this.sv_entnum - 1) & 0x0F, true) : '1 1 1');
                switch(this.beam_type)
                {
                        case ARC_BT_MISS:
diff --git a/qcsrc/common/weapons/weapon/arc.qh b/qcsrc/common/weapons/weapon/arc.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index a4fb13c44ee0176212dfa59cd09dfc9592d99cf1..2eb8f00b4179c72cd91727168bbee9f7719c93d4 100644 (file)
@@ -1,12 +1,13 @@
+#include "blaster.qh"
 #ifndef IMPLEMENTATION
 CLASS(Blaster, Weapon)
-/* ammotype  */ //ATTRIB(Blaster, ammo_field, .int, ammo_none)
-/* impulse   */ ATTRIB(Blaster, impulse, int, 1)
+/* ammotype  */ //ATTRIB(Blaster, ammo_field, .int, ammo_none);
+/* impulse   */ ATTRIB(Blaster, impulse, int, 1);
 /* flags     */ ATTRIB(Blaster, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Blaster, bot_pickupbasevalue, float, 0);
 /* color     */ ATTRIB(Blaster, wpcolor, vector, '1 0.5 0.5');
 /* modelname */ ATTRIB(Blaster, mdl, string, "laser");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Blaster, m_model, Model, MDL_BLASTER_ITEM);
 #endif
 /* crosshair */ ATTRIB(Blaster, w_crosshair, string, "gfx/crosshairlaser");
@@ -56,9 +57,9 @@ REGISTER_WEAPON(BLASTER, blaster, NEW(Blaster));
 spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(this, WEP_BLASTER); }
 spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); }
 
-void W_Blaster_Touch(entity this)
+void W_Blaster_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        this.event_damage = func_null;
 
@@ -72,15 +73,15 @@ void W_Blaster_Touch(entity this)
                NULL,
                this.blaster_force,
                this.projectiledeathtype,
-               other
+               toucher
        );
 
-       remove(this);
+       delete(this);
 }
 
 void W_Blaster_Think(entity this)
 {
-       this.movetype = MOVETYPE_FLY;
+       set_movetype(this, MOVETYPE_FLY);
        setthink(this, SUB_Remove);
        this.nextthink = time + this.blaster_lifetime;
        CSQCProjectile(this, true, PROJECTILE_BLASTER, true);
@@ -88,6 +89,7 @@ void W_Blaster_Think(entity this)
 
 void W_Blaster_Attack(
        entity actor,
+       .entity weaponentity,
        float atk_deathtype,
        float atk_shotangle,
        float atk_damage,
@@ -101,7 +103,7 @@ void W_Blaster_Attack(
 {
        vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
 
-       W_SetupShot_Dir(actor, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage);
+       W_SetupShot_Dir(actor, weaponentity, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage);
        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        entity missile = new(blasterbolt);
@@ -137,6 +139,8 @@ void W_Blaster_Attack(
 
        settouch(missile, W_Blaster_Touch);
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags = MIF_SPLASH;
        missile.projectiledeathtype = atk_deathtype;
        setthink(missile, W_Blaster_Think);
@@ -171,6 +175,7 @@ METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponenti
         {
             W_Blaster_Attack(
                 actor,
+                weaponentity,
                 WEP_BLASTER.m_id,
                 WEP_CVAR_PRI(blaster, shotangle),
                 WEP_CVAR_PRI(blaster, damage),
@@ -202,6 +207,7 @@ METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponenti
                 {
                     W_Blaster_Attack(
                         actor,
+                        weaponentity,
                         WEP_BLASTER.m_id | HITTYPE_SECONDARY,
                         WEP_CVAR_SEC(blaster, shotangle),
                         WEP_CVAR_SEC(blaster, damage),
diff --git a/qcsrc/common/weapons/weapon/blaster.qh b/qcsrc/common/weapons/weapon/blaster.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index eb3e72f8ac0535e3c13d06f0d774a31fa2080202..58214bf390feed3d2f41e1ec5ec24c847b02c10f 100644 (file)
@@ -1,12 +1,13 @@
+#include "crylink.qh"
 #ifndef IMPLEMENTATION
 CLASS(Crylink, Weapon)
-/* ammotype  */ ATTRIB(Crylink, ammo_field, .int, ammo_cells)
-/* impulse   */ ATTRIB(Crylink, impulse, int, 6)
+/* ammotype  */ ATTRIB(Crylink, ammo_field, .int, ammo_cells);
+/* impulse   */ ATTRIB(Crylink, impulse, int, 6);
 /* flags     */ ATTRIB(Crylink, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Crylink, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Crylink, wpcolor, vector, '1 0.5 1');
 /* modelname */ ATTRIB(Crylink, mdl, string, "crylink");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Crylink, m_model, Model, MDL_CRYLINK_ITEM);
 #endif
 /* crosshair */ ATTRIB(Crylink, w_crosshair, string, "gfx/crosshaircrylink");
@@ -114,11 +115,11 @@ void W_Crylink_Dequeue(entity e)
 void W_Crylink_Reset(entity this)
 {
        W_Crylink_Dequeue(this);
-       remove(this);
+       delete(this);
 }
 
 // force projectile to explode
-void W_Crylink_LinkExplode(entity e, entity e2)
+void W_Crylink_LinkExplode(entity e, entity e2, entity directhitentity)
 {
        float a;
 
@@ -132,12 +133,13 @@ void W_Crylink_LinkExplode(entity e, entity e2)
 
        float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
 
-       RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, other);
+       RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius),
+                               NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, directhitentity);
 
-       W_Crylink_LinkExplode(e.queuenext, e2);
+       W_Crylink_LinkExplode(e.queuenext, e2, directhitentity);
 
        e.classname = "spike_oktoremove";
-       remove(e);
+       delete(e);
 }
 
 // adjust towards center
@@ -261,13 +263,13 @@ void W_Crylink_LinkJoinEffect_Think(entity this)
                                        NULL,
                                        WEP_CVAR_BOTH(crylink, isprimary, joinexplode_force) * n,
                                        e.projectiledeathtype,
-                                       other
+                                       NULL
                                );
                                Send_Effect(EFFECT_CRYLINK_JOINEXPLODE, this.origin, '0 0 0', n);
                        }
                }
        }
-       remove(this);
+       delete(this);
 }
 
 float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad)
@@ -293,17 +295,17 @@ float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad)
 }
 
 // NO bounce protection, as bounces are limited!
-void W_Crylink_Touch(entity this)
+void W_Crylink_Touch(entity this, entity toucher)
 {
        float finalhit;
        float f;
        float isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        float a;
        a = bound(0, 1 - (time - this.fade_time) * this.fade_rate, 1);
 
-       finalhit = ((this.cnt <= 0) || (other.takedamage != DAMAGE_NO));
+       finalhit = ((this.cnt <= 0) || (toucher.takedamage != DAMAGE_NO));
        if(finalhit)
                f = 1;
        else
@@ -311,22 +313,22 @@ void W_Crylink_Touch(entity this)
        if(a)
                f *= a;
 
-       float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * f, this.projectiledeathtype, other);
+       float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * f, this.projectiledeathtype, toucher);
 
        if(totaldamage && ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(this, WEP_CVAR_BOTH(crylink, isprimary, radius)))))
        {
                if(this == this.realowner.crylink_lastgroup)
                        this.realowner.crylink_lastgroup = NULL;
-               W_Crylink_LinkExplode(this.queuenext, this);
+               W_Crylink_LinkExplode(this.queuenext, this, toucher);
                this.classname = "spike_oktoremove";
-               remove(this);
+               delete(this);
                return;
        }
        else if(finalhit)
        {
                // just unlink
                W_Crylink_Dequeue(this);
-               remove(this);
+               delete(this);
                return;
        }
        this.cnt = this.cnt - 1;
@@ -341,10 +343,10 @@ void W_Crylink_Touch(entity this)
 void W_Crylink_Fadethink(entity this)
 {
        W_Crylink_Dequeue(this);
-       remove(this);
+       delete(this);
 }
 
-void W_Crylink_Attack(Weapon thiswep, entity actor)
+void W_Crylink_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
        float counter, shots;
        entity proj, prevproj, firstproj;
@@ -359,7 +361,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor)
        if(WEP_CVAR_PRI(crylink, joinexplode))
                maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage);
 
-       W_SetupShot(actor, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg);
+       W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg);
        forward = v_forward;
        right = v_right;
        up = v_up;
@@ -394,7 +396,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor)
 
                prevproj = proj;
 
-               proj.movetype = MOVETYPE_BOUNCEMISSILE;
+               set_movetype(proj, MOVETYPE_BOUNCEMISSILE);
                PROJECTILE_MAKETRIGGER(proj);
                proj.projectiledeathtype = WEP_CRYLINK.m_id;
                //proj.gravity = 0.001;
@@ -438,6 +440,8 @@ void W_Crylink_Attack(Weapon thiswep, entity actor)
                //proj.glow_size = 20;
 
                proj.flags = FL_PROJECTILE;
+               IL_PUSH(g_projectiles, proj);
+               IL_PUSH(g_bot_dodge, proj);
                proj.missile_flags = MIF_SPLASH;
 
                CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
@@ -452,7 +456,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor)
        }
 }
 
-void W_Crylink_Attack2(Weapon thiswep, entity actor)
+void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
 {
        float counter, shots;
        entity proj, prevproj, firstproj;
@@ -467,7 +471,7 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor)
        if(WEP_CVAR_SEC(crylink, joinexplode))
                maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage);
 
-       W_SetupShot(actor, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg);
+       W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg);
        forward = v_forward;
        right = v_right;
        up = v_up;
@@ -502,7 +506,7 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor)
 
                prevproj = proj;
 
-               proj.movetype = MOVETYPE_BOUNCEMISSILE;
+               set_movetype(proj, MOVETYPE_BOUNCEMISSILE);
                PROJECTILE_MAKETRIGGER(proj);
                proj.projectiledeathtype = WEP_CRYLINK.m_id | HITTYPE_SECONDARY;
                //proj.gravity = 0.001;
@@ -553,6 +557,8 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor)
                //proj.glow_size = 20;
 
                proj.flags = FL_PROJECTILE;
+               IL_PUSH(g_projectiles, proj);
+               IL_PUSH(g_bot_dodge, proj);
         proj.missile_flags = MIF_SPLASH;
 
                CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
@@ -585,7 +591,7 @@ METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentit
         if(actor.crylink_waitrelease != 1)
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire)))
         {
-            W_Crylink_Attack(thiswep, actor);
+            W_Crylink_Attack(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
         }
     }
@@ -595,7 +601,7 @@ METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentit
         if(actor.crylink_waitrelease != 2)
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire)))
         {
-            W_Crylink_Attack2(thiswep, actor);
+            W_Crylink_Attack2(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
         }
     }
@@ -652,7 +658,7 @@ METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(Crylink, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND_RELOAD);
 }
 METHOD(Crylink, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/crylink.qh b/qcsrc/common/weapons/weapon/crylink.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index f2ca4265545d9dd31dce6d1650978699f069764a..7e63e760ec96d4d879a6813e6e436232aef965f2 100644 (file)
@@ -1,12 +1,13 @@
+#include "devastator.qh"
 #ifndef IMPLEMENTATION
 CLASS(Devastator, Weapon)
-/* ammotype  */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets)
-/* impulse   */ ATTRIB(Devastator, impulse, int, 9)
+/* ammotype  */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets);
+/* impulse   */ ATTRIB(Devastator, impulse, int, 9);
 /* flags     */ ATTRIB(Devastator, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Devastator, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 /* color     */ ATTRIB(Devastator, wpcolor, vector, '1 1 0');
 /* modelname */ ATTRIB(Devastator, mdl, string, "rl");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Devastator, m_model, Model, MDL_DEVASTATOR_ITEM);
 #endif
 /* crosshair */ ATTRIB(Devastator, w_crosshair, string, "gfx/crosshairrocketlauncher");
@@ -39,6 +40,7 @@ CLASS(Devastator, Weapon)
         P(class, prefix, remote_edgedamage, float, NONE) \
         P(class, prefix, remote_force, float, NONE) \
         P(class, prefix, remote_jump_damage, float, NONE) \
+        P(class, prefix, remote_jump_force, float, NONE) \
         P(class, prefix, remote_jump_radius, float, NONE) \
         P(class, prefix, remote_jump_velocity_z_add, float, NONE) \
         P(class, prefix, remote_jump_velocity_z_max, float, NONE) \
@@ -61,7 +63,7 @@ ENDCLASS(Devastator)
 REGISTER_WEAPON(DEVASTATOR, devastator, NEW(Devastator));
 
 #ifdef SVQC
-.float rl_release;
+.float rl_release[MAX_WEAPONSLOTS];
 .float rl_detonate_later;
 #endif
 #endif
@@ -76,20 +78,25 @@ void W_Devastator_Unregister(entity this)
 {
        if(this.realowner && this.realowner.lastrocket == this)
        {
-               this.realowner.lastrocket = NULL;
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(this.realowner.(weaponentity).lastrocket == this)
+                               this.realowner.(weaponentity).lastrocket = NULL;
+               }
                // this.realowner.rl_release = 1;
        }
 }
 
-void W_Devastator_Explode(entity this)
+void W_Devastator_Explode(entity this, entity directhitentity)
 {
        W_Devastator_Unregister(this);
 
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
 
        this.event_damage = func_null;
@@ -105,7 +112,7 @@ void W_Devastator_Explode(entity this)
                NULL,
                WEP_CVAR(devastator, force),
                this.projectiledeathtype,
-               other
+               directhitentity
        );
 
        Weapon thiswep = WEP_DEVASTATOR;
@@ -120,7 +127,12 @@ void W_Devastator_Explode(entity this)
                        PS(this.realowner).m_switchweapon = w_getbestweapon(this.realowner);
                }
        }
-       remove(this);
+       delete(this);
+}
+
+void W_Devastator_Explode_think(entity this)
+{
+       W_Devastator_Explode(this, NULL);
 }
 
 void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity)
@@ -130,50 +142,56 @@ void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity)
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       float handled_as_rocketjump = false;
+       bool handled_as_rocketjump = false;
+       entity head = NULL;
 
-       entity head = WarpZone_FindRadius(
-               this.origin,
-               WEP_CVAR(devastator, remote_jump_radius),
-               false
-       );
-
-       while(head)
+       if(WEP_CVAR(devastator, remote_jump_radius))
        {
-               if(head.takedamage && (head == this.realowner))
+               head = WarpZone_FindRadius(
+                       this.origin,
+                       WEP_CVAR(devastator, remote_jump_radius),
+                       false
+               );
+
+               while(head)
                {
-                       float distance_to_head = vlen(this.origin - head.WarpZone_findradius_nearest);
-                       if(distance_to_head <= WEP_CVAR(devastator, remote_jump_radius))
+                       if(head.takedamage && (head == this.realowner))
                        {
-                               // we handled this as a rocketjump :)
-                               handled_as_rocketjump = true;
-
-                               // modify velocity
-                               head.velocity_x *= 0.9;
-                               head.velocity_y *= 0.9;
-                               head.velocity_z = bound(
-                                       WEP_CVAR(devastator, remote_jump_velocity_z_min),
-                                       head.velocity.z + WEP_CVAR(devastator, remote_jump_velocity_z_add),
-                                       WEP_CVAR(devastator, remote_jump_velocity_z_max)
-                               );
-
-                               // now do the damage
-                               RadiusDamage(
-                                       this,
-                                       head,
-                                       WEP_CVAR(devastator, remote_jump_damage),
-                                       WEP_CVAR(devastator, remote_jump_damage),
-                                       WEP_CVAR(devastator, remote_jump_radius),
-                                       NULL,
-                                       head,
-                                       0,
-                                       this.projectiledeathtype | HITTYPE_BOUNCE,
-                                       NULL
-                               );
-                               break;
+                               if(vdist(this.origin - head.WarpZone_findradius_nearest, <=, WEP_CVAR(devastator, remote_jump_radius)))
+                               {
+                                       // we handled this as a rocketjump :)
+                                       handled_as_rocketjump = true;
+
+                                       // modify velocity
+                                       if(WEP_CVAR(devastator, remote_jump_velocity_z_add))
+                                       {
+                                               head.velocity_x *= 0.9;
+                                               head.velocity_y *= 0.9;
+                                               head.velocity_z = bound(
+                                                       WEP_CVAR(devastator, remote_jump_velocity_z_min),
+                                                       head.velocity.z + WEP_CVAR(devastator, remote_jump_velocity_z_add),
+                                                       WEP_CVAR(devastator, remote_jump_velocity_z_max)
+                                               );
+                                       }
+
+                                       // now do the damage
+                                       RadiusDamage(
+                                               this,
+                                               head,
+                                               WEP_CVAR(devastator, remote_jump_damage),
+                                               WEP_CVAR(devastator, remote_jump_damage),
+                                               WEP_CVAR(devastator, remote_jump_radius),
+                                               NULL,
+                                               head,
+                                               (WEP_CVAR(devastator, remote_jump_force) ? WEP_CVAR(devastator, remote_jump_force) : 0),
+                                               this.projectiledeathtype | HITTYPE_BOUNCE,
+                                               NULL
+                                       );
+                                       break;
+                               }
                        }
+                       head = head.chain;
                }
-               head = head.chain;
        }
 
        RadiusDamage(
@@ -201,13 +219,13 @@ void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity)
                        PS(this.realowner).m_switchweapon = w_getbestweapon(this.realowner);
                }
        }
-       remove(this);
+       delete(this);
 }
 
 void W_Devastator_RemoteExplode(entity this, .entity weaponentity)
 {
        if(!IS_DEAD(this.realowner))
-       if(this.realowner.lastrocket)
+       if(this.realowner.(weaponentity).lastrocket)
        {
                if((this.spawnshieldtime >= 0)
                        ? (time >= this.spawnshieldtime) // timer
@@ -259,9 +277,8 @@ void W_Devastator_Think(entity this)
        this.nextthink = time;
        if(time > this.cnt)
        {
-               other = NULL;
                this.projectiledeathtype |= HITTYPE_BOUNCE;
-               W_Devastator_Explode(this);
+               W_Devastator_Explode(this, NULL);
                return;
        }
 
@@ -274,8 +291,11 @@ void W_Devastator_Think(entity this)
        // laser guided, or remote detonation
        if(PS(this.realowner).m_weapon == WEP_DEVASTATOR)
        {
-               if(this == this.realowner.lastrocket)
-               if(!this.realowner.rl_release)
+               .entity weaponentity = this.weaponentity_fld;
+               int slot = weaponslot(weaponentity);
+
+               if(this == this.realowner.(weaponentity).lastrocket)
+               if(!this.realowner.rl_release[slot])
                if(!PHYS_INPUT_BUTTON_ATCK2(this))
                if(WEP_CVAR(devastator, guiderate))
                if(time > this.pushltime)
@@ -311,7 +331,6 @@ void W_Devastator_Think(entity this)
                        }
                }
 
-               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
                if(this.rl_detonate_later)
                        W_Devastator_RemoteExplode(this, weaponentity);
        }
@@ -320,16 +339,16 @@ void W_Devastator_Think(entity this)
                UpdateCSQCProjectile(this);
 }
 
-void W_Devastator_Touch(entity this)
+void W_Devastator_Touch(entity this, entity toucher)
 {
-       if(WarpZone_Projectile_Touch(this))
+       if(WarpZone_Projectile_Touch(this, toucher))
        {
                if(wasfreed(this))
                        W_Devastator_Unregister(this);
                return;
        }
        W_Devastator_Unregister(this);
-       W_Devastator_Explode(this);
+       W_Devastator_Explode(this, toucher);
 }
 
 void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -344,19 +363,20 @@ void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float d
        this.angles = vectoangles(this.velocity);
 
        if(this.health <= 0)
-               W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode);
+               W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode_think);
 }
 
-void W_Devastator_Attack(Weapon thiswep, entity actor)
+void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR(devastator, ammo));
 
-       W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        entity missile = WarpZone_RefSys_SpawnSameRefSys(actor);
+       missile.weaponentity_fld = weaponentity;
        missile.owner = missile.realowner = actor;
-       actor.lastrocket = missile;
+       actor.(weaponentity).lastrocket = missile;
        if(WEP_CVAR(devastator, detonatedelay) >= 0)
                missile.spawnshieldtime = time + WEP_CVAR(devastator, detonatedelay);
        else
@@ -371,8 +391,9 @@ void W_Devastator_Attack(Weapon thiswep, entity actor)
        missile.health = WEP_CVAR(devastator, health);
        missile.event_damage = W_Devastator_Damage;
        missile.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, missile);
 
-       missile.movetype = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(missile);
        missile.projectiledeathtype = WEP_DEVASTATOR.m_id;
        setsize(missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
@@ -386,6 +407,8 @@ void W_Devastator_Attack(Weapon thiswep, entity actor)
        missile.nextthink = time;
        missile.cnt = time + WEP_CVAR(devastator, lifetime);
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags = MIF_SPLASH;
 
        CSQCProjectile(missile, WEP_CVAR(devastator, guiderate) == 0 && WEP_CVAR(devastator, speedaccel) == 0, PROJECTILE_ROCKET, false); // because of fly sound
@@ -395,50 +418,20 @@ void W_Devastator_Attack(Weapon thiswep, entity actor)
        setmodel(flash, MDL_DEVASTATOR_MUZZLEFLASH); // precision set below
        SUB_SetFade(flash, time, 0.1);
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
-       W_AttachToShotorg(actor, flash, '5 0 0');
+       W_AttachToShotorg(actor, weaponentity, flash, '5 0 0');
 
        // common properties
        MUTATOR_CALLHOOK(EditProjectile, actor, missile);
 }
 
-#if 0
 METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
 {
-    entity this = actor;
     // aim and decide to fire if appropriate
     PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false);
     if(skill >= 2) // skill 0 and 1 bots won't detonate rockets!
     {
         // decide whether to detonate rockets
-        entity missile, targetlist, targ;
-        targetlist = findchainfloat(bot_attack, true);
-        for(missile = NULL; (missile = find(missile, classname, "rocket")); ) if(missile.realowner == actor)
-        {
-            targ = targetlist;
-            while(targ)
-            {
-                if(targ != missile.realowner && vlen(targ.origin - missile.origin) < WEP_CVAR(devastator, radius))
-                {
-                    PHYS_INPUT_BUTTON_ATCK2(actor) = true;
-                    break;
-                }
-                targ = targ.chain;
-            }
-        }
-
-        if(PHYS_INPUT_BUTTON_ATCK2(actor)) PHYS_INPUT_BUTTON_ATCK(actor) = false;
-    }
-}
-#else
-METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
-{
-    // aim and decide to fire if appropriate
-    PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false);
-    if(skill >= 2) // skill 0 and 1 bots won't detonate rockets!
-    {
-        // decide whether to detonate rockets
-        entity targetlist, targ;
-        float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+        float edgedamage, coredamage, edgeradius, recipricoledgeradius;
         float selfdamage, teamdamage, enemydamage;
         edgedamage = WEP_CVAR(devastator, edgedamage);
         coredamage = WEP_CVAR(devastator, damage);
@@ -447,25 +440,21 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
         selfdamage = 0;
         teamdamage = 0;
         enemydamage = 0;
-        targetlist = findchainfloat(bot_attack, true);
-        FOREACH_ENTITY_ENT(realowner, actor,
+        IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket",
         {
-            if(it.classname != "rocket") continue;
-
-            targ = targetlist;
-            while(targ)
+            entity rocket = it;
+            IL_EACH(g_bot_targets, it.bot_attack,
             {
-                d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - it.origin);
-                d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
-                // count potential damage according to type of target
-                if(targ == actor)
-                    selfdamage = selfdamage + d;
-                else if(targ.team == actor.team && teamplay)
-                    teamdamage = teamdamage + d;
-                else if(bot_shouldattack(actor, targ))
-                    enemydamage = enemydamage + d;
-                targ = targ.chain;
-            }
+               float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - rocket.origin);
+               d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+               // count potential damage according to type of target
+               if(it == actor)
+                       selfdamage = selfdamage + d;
+               else if(SAME_TEAM(it, actor))
+                       teamdamage = teamdamage + d;
+               else if(bot_shouldattack(actor, it))
+                       enemydamage = enemydamage + d;
+            });
         });
         float desirabledamage;
         desirabledamage = enemydamage;
@@ -474,36 +463,33 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
         if(teamplay && actor.team)
             desirabledamage = desirabledamage - teamdamage;
 
-        FOREACH_ENTITY_ENT(realowner, actor,
+        makevectors(actor.v_angle);
+        IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket",
         {
-            if(it.classname != "rocket") continue;
-
-            makevectors(it.v_angle);
-            targ = targetlist;
             if(skill > 9) // normal players only do this for the target they are tracking
             {
-                targ = targetlist;
-                while(targ)
-                {
-                    if(
-                        (v_forward * normalize(it.origin - targ.origin)< 0.1)
-                        && desirabledamage > 0.1*coredamage
-                    ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
-                    targ = targ.chain;
-                }
-            }
-            else
-            {
-                float distance; distance= bound(300,vlen(actor.origin-actor.enemy.origin),30000);
+                   entity rocket = it;
+                   IL_EACH(g_bot_targets, it.bot_attack,
+                   {
+                       if((v_forward * normalize(rocket.origin - it.origin) < 0.1)
+                           && desirabledamage > 0.1 * coredamage
+                           ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+                   });
+               }
+               else
+               {
                 //As the distance gets larger, a correct detonation gets near imposible
                 //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player
-                if(v_forward * normalize(it.origin - actor.enemy.origin)< 0.1)
-                    if(IS_PLAYER(actor.enemy))
-                        if(desirabledamage >= 0.1*coredamage)
-                            if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
-                                PHYS_INPUT_BUTTON_ATCK2(actor) = true;
-            // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
-            }
+                if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1)
+                       && IS_PLAYER(actor.enemy)
+                       && (desirabledamage >= 0.1 * coredamage)
+                       )
+                {
+                       float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000);
+                       if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1))
+                               PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+                }
+               }
         });
         // if we would be doing at X percent of the core damage, detonate it
         // but don't fire a new shot at the same time!
@@ -516,38 +502,38 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
         if(PHYS_INPUT_BUTTON_ATCK2(actor)) PHYS_INPUT_BUTTON_ATCK(actor) = false;
     }
 }
-#endif
+
 METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
 {
     if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload
         thiswep.wr_reload(thiswep, actor, weaponentity);
     } else {
+       int slot = weaponslot(weaponentity);
         if(fire & 1)
         {
-            if(actor.rl_release || WEP_CVAR(devastator, guidestop))
+            if(actor.rl_release[slot] || WEP_CVAR(devastator, guidestop))
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire)))
             {
-                W_Devastator_Attack(thiswep, actor);
+                W_Devastator_Attack(thiswep, actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
-                actor.rl_release = 0;
+                actor.rl_release[slot] = 0;
             }
         }
         else
-            actor.rl_release = 1;
+            actor.rl_release[slot] = 1;
 
         if(fire & 2)
         if(PS(actor).m_switchweapon == WEP_DEVASTATOR)
         {
-            entity rock;
             bool rockfound = false;
-            for(rock = NULL; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == actor)
+            IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket",
             {
-                if(!rock.rl_detonate_later)
+                if(!it.rl_detonate_later)
                 {
-                    rock.rl_detonate_later = true;
+                    it.rl_detonate_later = true;
                     rockfound = true;
                 }
-            }
+            });
             if(rockfound)
                 sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
         }
@@ -555,7 +541,8 @@ METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponen
 }
 METHOD(Devastator, wr_setup, void(entity thiswep, entity actor))
 {
-    actor.rl_release = 1;
+    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       actor.rl_release[slot] = 1;
 }
 METHOD(Devastator, wr_checkammo1, bool(entity thiswep, entity actor))
 {
@@ -599,12 +586,16 @@ METHOD(Devastator, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(Devastator, wr_resetplayer, void(entity thiswep, entity actor))
 {
-    actor.lastrocket = NULL; // stop rocket guiding, no revenge from the grave!
-    actor.rl_release = 0;
+    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+    {
+       .entity weaponentity = weaponentities[slot];
+       actor.(weaponentity).lastrocket = NULL; // stop rocket guiding, no revenge from the grave!
+       actor.rl_release[slot] = 0;
+    }
 }
 METHOD(Devastator, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, WEP_CVAR(devastator, ammo), SND_RELOAD);
+    W_Reload(actor, weaponentity, WEP_CVAR(devastator, ammo), SND_RELOAD);
 }
 METHOD(Devastator, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/devastator.qh b/qcsrc/common/weapons/weapon/devastator.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 09cb77c8bd9a5b2774d8178f48bccc195bead691..319f0d1d701ca936491e65d271e248ebedaf8202 100644 (file)
@@ -1,12 +1,13 @@
+#include "electro.qh"
 #ifndef IMPLEMENTATION
 CLASS(Electro, Weapon)
-/* ammotype  */ ATTRIB(Electro, ammo_field, .int, ammo_cells)
-/* impulse   */ ATTRIB(Electro, impulse, int, 5)
+/* ammotype  */ ATTRIB(Electro, ammo_field, .int, ammo_cells);
+/* impulse   */ ATTRIB(Electro, impulse, int, 5);
 /* flags     */ ATTRIB(Electro, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Electro, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Electro, wpcolor, vector, '0 0.5 1');
 /* modelname */ ATTRIB(Electro, mdl, string, "electro");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Electro, m_model, Model, MDL_ELECTRO_ITEM);
 #endif
 /* crosshair */ ATTRIB(Electro, w_crosshair, string, "gfx/crosshairelectro");
@@ -50,6 +51,7 @@ CLASS(Electro, Weapon)
                P(class, prefix, speed_up, float, SEC) \
                P(class, prefix, speed_z, float, SEC) \
                P(class, prefix, spread, float, BOTH) \
+               P(class, prefix, stick, float, SEC) \
                P(class, prefix, switchdelay_drop, float, NONE) \
                P(class, prefix, switchdelay_raise, float, NONE) \
                P(class, prefix, touchexplode, float, SEC) \
@@ -141,22 +143,22 @@ void W_Electro_ExplodeCombo(entity this)
                NULL
        );
 
-       remove(this);
+       delete(this);
 }
 
-void W_Electro_Explode(entity this)
+void W_Electro_Explode(entity this, entity directhitentity)
 {
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       if(this.movetype == MOVETYPE_BOUNCE)
+       if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway?
        {
                RadiusDamage(
                        this,
@@ -168,7 +170,7 @@ void W_Electro_Explode(entity this)
                        NULL,
                        WEP_CVAR_SEC(electro, force),
                        this.projectiledeathtype,
-                       other
+                       directhitentity
                );
        }
        else
@@ -184,26 +186,30 @@ void W_Electro_Explode(entity this)
                        NULL,
                        WEP_CVAR_PRI(electro, force),
                        this.projectiledeathtype,
-                       other
+                       directhitentity
                );
        }
 
-       remove(this);
+       delete(this);
 }
 
 void W_Electro_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_Electro_Explode(this);
+       W_Electro_Explode(this, trigger);
 }
 
-void W_Electro_TouchExplode(entity this)
+void W_Electro_TouchExplode(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       W_Electro_Explode(this);
+       PROJECTILE_TOUCH(this, toucher);
+       W_Electro_Explode(this, toucher);
 }
 
+
+void sys_phys_update_single(entity this);
+
 void W_Electro_Bolt_Think(entity this)
 {
+       // sys_phys_update_single(this);
        if(time >= this.ltime)
        {
                this.use(this, NULL, NULL);
@@ -252,9 +258,10 @@ void W_Electro_Bolt_Think(entity this)
                        { this.nextthink = min(time + WEP_CVAR_PRI(electro, midaircombo_interval), this.ltime); }
        }
        else { this.nextthink = this.ltime; }
+       // this.nextthink = time;
 }
 
-void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
+void W_Electro_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity proj;
 
@@ -262,6 +269,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
 
        W_SetupShot_ProjectileSize(
                actor,
+               weaponentity,
                '0 0 -3',
                '0 0 -3',
                false,
@@ -285,29 +293,77 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
        proj.projectiledeathtype = WEP_ELECTRO.m_id;
        setorigin(proj, w_shotorg);
 
-       proj.movetype = MOVETYPE_FLY;
+       // if (IS_CSQC)
+       set_movetype(proj, MOVETYPE_FLY);
        W_SetupProjVelocity_PRI(proj, electro);
        proj.angles = vectoangles(proj.velocity);
        settouch(proj, W_Electro_TouchExplode);
        setsize(proj, '0 0 -3', '0 0 -3');
        proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.missile_flags = MIF_SPLASH;
 
        CSQCProjectile(proj, true, PROJECTILE_ELECTRO_BEAM, true);
 
        MUTATOR_CALLHOOK(EditProjectile, actor, proj);
+       // proj.com_phys_pos = proj.origin;
+       // proj.com_phys_vel = proj.velocity;
+}
+
+void W_Electro_Orb_Stick(entity this, entity to)
+{
+       entity newproj = spawn();
+       newproj.classname = this.classname;
+
+       newproj.bot_dodge = this.bot_dodge;
+       newproj.bot_dodgerating = this.bot_dodgerating;
+
+       newproj.owner = this.owner;
+       newproj.realowner = this.realowner;
+       setsize(newproj, this.mins, this.maxs);
+       setorigin(newproj, this.origin);
+       setmodel(newproj, MDL_PROJECTILE_ELECTRO);
+       newproj.angles = vectoangles(-trace_plane_normal); // face against the surface
+
+       newproj.takedamage = this.takedamage;
+       newproj.damageforcescale = this.damageforcescale;
+       newproj.health = this.health;
+       newproj.event_damage = this.event_damage;
+       newproj.spawnshieldtime = this.spawnshieldtime;
+       newproj.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, newproj);
+
+       set_movetype(newproj, MOVETYPE_NONE); // lock the orb in place
+       newproj.projectiledeathtype = this.projectiledeathtype;
+
+       settouch(newproj, func_null);
+       setthink(newproj, getthink(this));
+       newproj.nextthink = this.nextthink;
+       newproj.use = this.use;
+       newproj.flags = this.flags;
+       IL_PUSH(g_projectiles, newproj);
+       IL_PUSH(g_bot_dodge, newproj);
+
+       delete(this);
+
+       if(to)
+               SetMovetypeFollow(this, to);
 }
 
-void W_Electro_Orb_Touch(entity this)
+void W_Electro_Orb_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       if(other.takedamage == DAMAGE_AIM)
-               { if(WEP_CVAR_SEC(electro, touchexplode)) { W_Electro_Explode(this); } }
+       PROJECTILE_TOUCH(this, toucher);
+       if(toucher.takedamage == DAMAGE_AIM)
+               { if(WEP_CVAR_SEC(electro, touchexplode)) { W_Electro_Explode(this, toucher); } }
        else
        {
                //UpdateCSQCProjectile(this);
                spamsound(this, CH_SHOTS, SND(ELECTRO_BOUNCE), VOL_BASE, ATTEN_NORM);
                this.projectiledeathtype |= HITTYPE_BOUNCE;
+
+               if(WEP_CVAR_SEC(electro, stick))
+                       W_Electro_Orb_Stick(this, toucher);
        }
 }
 
@@ -353,14 +409,15 @@ void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float
        }
 }
 
-void W_Electro_Attack_Orb(Weapon thiswep, entity actor)
+void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity)
 {
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(electro, ammo));
 
        W_SetupShot_ProjectileSize(
                actor,
-               '0 0 -4',
-               '0 0 -4',
+               weaponentity,
+               '-4 -4 -4',
+               '4 4 4',
                false,
                2,
                SND_ELECTRO_FIRE2,
@@ -385,16 +442,20 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor)
 
        //proj.glow_size = 50;
        //proj.glow_color = 45;
-       proj.movetype = MOVETYPE_BOUNCE;
+       set_movetype(proj, MOVETYPE_BOUNCE);
        W_SetupProjVelocity_UP_SEC(proj, electro);
        settouch(proj, W_Electro_Orb_Touch);
-       setsize(proj, '0 0 -4', '0 0 -4');
+       setsize(proj, '-4 -4 -4', '4 4 4');
        proj.takedamage = DAMAGE_YES;
        proj.damageforcescale = WEP_CVAR_SEC(electro, damageforcescale);
        proj.health = WEP_CVAR_SEC(electro, health);
        proj.event_damage = W_Electro_Orb_Damage;
        proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
        proj.damagedbycontents = (WEP_CVAR_SEC(electro, damagedbycontents));
+       if(proj.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, proj);
 
        proj.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
        proj.bouncestop = WEP_CVAR_SEC(electro, bouncestop);
@@ -419,7 +480,7 @@ void W_Electro_CheckAttack(Weapon thiswep, entity actor, .entity weaponentity, i
        if(PHYS_INPUT_BUTTON_ATCK2(actor))
        if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1))
        {
-               W_Electro_Attack_Orb(WEP_ELECTRO, actor);
+               W_Electro_Attack_Orb(WEP_ELECTRO, actor, weaponentity);
                actor.electro_count -= 1;
                weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
                return;
@@ -479,7 +540,7 @@ METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentit
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire)))
         {
-                W_Electro_Attack_Bolt(thiswep, actor);
+                W_Electro_Attack_Bolt(thiswep, actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
         }
     }
@@ -488,7 +549,7 @@ METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentit
         if(time >= actor.electro_secondarytime)
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(electro, refire)))
         {
-            W_Electro_Attack_Orb(thiswep, actor);
+            W_Electro_Attack_Orb(thiswep, actor, weaponentity);
             actor.electro_count = WEP_CVAR_SEC(electro, count);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
             actor.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor(actor);
@@ -522,7 +583,7 @@ METHOD(Electro, wr_resetplayer, void(entity thiswep, entity actor))
 }
 METHOD(Electro, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND_RELOAD);
 }
 METHOD(Electro, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/electro.qh b/qcsrc/common/weapons/weapon/electro.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d07324701ad11da57d48d92ee4909e6d6a57de43..ae1e48bb2c3e7f511af59d27ba3235a2434f07f5 100644 (file)
@@ -1,12 +1,13 @@
+#include "fireball.qh"
 #ifndef IMPLEMENTATION
 CLASS(Fireball, Weapon)
-/* ammotype  */ //ATTRIB(Fireball, ammo_field, .int, ammo_none)
-/* impulse   */ ATTRIB(Fireball, impulse, int, 9)
+/* ammotype  */ //ATTRIB(Fireball, ammo_field, .int, ammo_none);
+/* impulse   */ ATTRIB(Fireball, impulse, int, 9);
 /* flags     */ ATTRIB(Fireball, spawnflags, int, WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Fireball, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Fireball, wpcolor, vector, '1 0.5 0');
 /* modelname */ ATTRIB(Fireball, mdl, string, "fireball");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Fireball, m_model, Model, MDL_FIREBALL_ITEM);
 #endif
 /* crosshair */ ATTRIB(Fireball, w_crosshair, string, "gfx/crosshairfireball");
@@ -55,14 +56,14 @@ REGISTER_WEAPON(FIREBALL, fireball, NEW(Fireball));
 #ifdef SVQC
 .float bot_primary_fireballmooth; // whatever a mooth is
 .vector fireball_impactvec;
-.float fireball_primarytime;
+.float fireball_primarytime[MAX_WEAPONSLOTS];
 #endif
 #endif
 #ifdef IMPLEMENTATION
 #ifdef SVQC
 spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(this, WEP_FIREBALL); }
 
-void W_Fireball_Explode(entity this)
+void W_Fireball_Explode(entity this, entity directhitentity)
 {
        entity e;
        float dist;
@@ -75,7 +76,7 @@ void W_Fireball_Explode(entity this)
 
        // 1. dist damage
        d = (this.realowner.health + this.realowner.armorvalue);
-       RadiusDamage(this, this.realowner, WEP_CVAR_PRI(fireball, damage), WEP_CVAR_PRI(fireball, edgedamage), WEP_CVAR_PRI(fireball, radius), NULL, NULL, WEP_CVAR_PRI(fireball, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR_PRI(fireball, damage), WEP_CVAR_PRI(fireball, edgedamage), WEP_CVAR_PRI(fireball, radius), NULL, NULL, WEP_CVAR_PRI(fireball, force), this.projectiledeathtype, directhitentity);
        if(this.realowner.health + this.realowner.armorvalue >= d)
        if(!this.cnt)
        {
@@ -109,18 +110,23 @@ void W_Fireball_Explode(entity this)
                }
        }
 
-       remove(this);
+       delete(this);
+}
+
+void W_Fireball_Explode_think(entity this)
+{
+       W_Fireball_Explode(this, NULL);
 }
 
 void W_Fireball_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_Fireball_Explode(this);
+       W_Fireball_Explode(this, trigger);
 }
 
-void W_Fireball_TouchExplode(entity this)
+void W_Fireball_TouchExplode(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       W_Fireball_Explode(this);
+       PROJECTILE_TOUCH(this, toucher);
+       W_Fireball_Explode(this, toucher);
 }
 
 void W_Fireball_LaserPlay(entity this, float dt, float dist, float damage, float edgedamage, float burntime)
@@ -144,7 +150,7 @@ void W_Fireball_LaserPlay(entity this, float dt, float dist, float damage, float
                if(d < dist)
                {
                        e.fireball_impactvec = p;
-                       RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+                       RandomSelection_AddEnt(e, 1 / (1 + d), !Fire_IsBurning(e));
                }
        }
        if(RandomSelection_chosen_ent)
@@ -163,7 +169,7 @@ void W_Fireball_Think(entity this)
        {
                this.cnt = 1;
                this.projectiledeathtype |= HITTYPE_SPLASH;
-               W_Fireball_Explode(this);
+               W_Fireball_Explode(this, NULL);
                return;
        }
 
@@ -184,19 +190,17 @@ void W_Fireball_Damage(entity this, entity inflictor, entity attacker, float dam
        if(this.health <= 0)
        {
                this.cnt = 1;
-               W_PrepareExplosionByDamage(this, attacker, W_Fireball_Explode);
+               W_PrepareExplosionByDamage(this, attacker, W_Fireball_Explode_think);
        }
 }
 
-void W_Fireball_Attack1(entity actor)
+void W_Fireball_Attack1(entity actor, .entity weaponentity)
 {
-       entity proj;
-
-       W_SetupShot_ProjectileSize(actor, '-16 -16 -16', '16 16 16', false, 2, SND_FIREBALL_FIRE2, CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-16 -16 -16', '16 16 16', false, 2, SND_FIREBALL_FIRE2, CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage));
 
        Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
-       proj = new(plasma_prim);
+       entity proj = new(plasma_prim);
        proj.owner = proj.realowner = actor;
        proj.bot_dodge = true;
        proj.bot_dodgerating = WEP_CVAR_PRI(fireball, damage);
@@ -213,12 +217,14 @@ void W_Fireball_Attack1(entity actor)
        proj.projectiledeathtype = WEP_FIREBALL.m_id;
        setorigin(proj, w_shotorg);
 
-       proj.movetype = MOVETYPE_FLY;
+       set_movetype(proj, MOVETYPE_FLY);
        W_SetupProjVelocity_PRI(proj, fireball);
        proj.angles = vectoangles(proj.velocity);
        settouch(proj, W_Fireball_TouchExplode);
        setsize(proj, '-16 -16 -16', '16 16 16');
        proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
     proj.missile_flags = MIF_SPLASH | MIF_PROXY;
 
        CSQCProjectile(proj, true, PROJECTILE_FIREBALL, true);
@@ -226,40 +232,40 @@ void W_Fireball_Attack1(entity actor)
        MUTATOR_CALLHOOK(EditProjectile, actor, proj);
 }
 
-void W_Fireball_AttackEffect(entity actor, float i, vector f_diff)
+void W_Fireball_AttackEffect(entity actor, .entity weaponentity, float i, vector f_diff)
 {
-       W_SetupShot_ProjectileSize(actor, '-16 -16 -16', '16 16 16', false, 0, SND_Null, 0, 0);
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-16 -16 -16', '16 16 16', false, 0, SND_Null, 0, 0);
        w_shotorg += f_diff.x * v_up + f_diff.y * v_right;
        Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 }
 
 void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       W_Fireball_Attack1(actor);
+       W_Fireball_Attack1(actor, weaponentity);
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready);
 }
 
 void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       W_Fireball_AttackEffect(actor, 0, '+1.25 +3.75 0');
+       W_Fireball_AttackEffect(actor, weaponentity, 0, '+1.25 +3.75 0');
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4);
 }
 
 void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       W_Fireball_AttackEffect(actor, 0, '-1.25 +3.75 0');
+       W_Fireball_AttackEffect(actor, weaponentity, 0, '-1.25 +3.75 0');
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3);
 }
 
 void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       W_Fireball_AttackEffect(actor, 1, '+1.25 -3.75 0');
+       W_Fireball_AttackEffect(actor, weaponentity, 1, '+1.25 -3.75 0');
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2);
 }
 
 void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       W_Fireball_AttackEffect(actor, 0, '-1.25 -3.75 0');
+       W_Fireball_AttackEffect(actor, weaponentity, 0, '-1.25 -3.75 0');
        sound(actor, CH_WEAPON_SINGLE, SND_FIREBALL_PREFIRE2, VOL_BASE, ATTEN_NORM);
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
 }
@@ -268,7 +274,7 @@ void W_Fireball_Firemine_Think(entity this)
 {
        if(time > this.pushltime)
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -290,19 +296,19 @@ void W_Fireball_Firemine_Think(entity this)
        this.nextthink = time + 0.1;
 }
 
-void W_Fireball_Firemine_Touch(entity this)
+void W_Fireball_Firemine_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       if(other.takedamage == DAMAGE_AIM)
-       if(Fire_AddDamage(other, this.realowner, WEP_CVAR_SEC(fireball, damage), WEP_CVAR_SEC(fireball, damagetime), this.projectiledeathtype) >= 0)
+       PROJECTILE_TOUCH(this, toucher);
+       if(toucher.takedamage == DAMAGE_AIM)
+       if(Fire_AddDamage(toucher, this.realowner, WEP_CVAR_SEC(fireball, damage), WEP_CVAR_SEC(fireball, damagetime), this.projectiledeathtype) >= 0)
        {
-               remove(this);
+               delete(this);
                return;
        }
        this.projectiledeathtype |= HITTYPE_BOUNCE;
 }
 
-void W_Fireball_Attack2(entity actor)
+void W_Fireball_Attack2(entity actor, .entity weaponentity)
 {
        entity proj;
        vector f_diff;
@@ -325,7 +331,7 @@ void W_Fireball_Attack2(entity actor)
                        f_diff = '+1.25 +3.75 0';
                        break;
        }
-       W_SetupShot_ProjectileSize(actor, '-4 -4 -4', '4 4 4', false, 2, SND_FIREBALL_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 2, SND_FIREBALL_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage));
        traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, actor);
        w_shotorg = trace_endpos;
 
@@ -335,7 +341,7 @@ void W_Fireball_Attack2(entity actor)
        proj.owner = proj.realowner = actor;
        proj.bot_dodge = true;
        proj.bot_dodgerating = WEP_CVAR_SEC(fireball, damage);
-       proj.movetype = MOVETYPE_BOUNCE;
+       set_movetype(proj, MOVETYPE_BOUNCE);
        proj.projectiledeathtype = WEP_FIREBALL.m_id | HITTYPE_SECONDARY;
        settouch(proj, W_Fireball_Firemine_Touch);
        PROJECTILE_MAKETRIGGER(proj);
@@ -349,6 +355,8 @@ void W_Fireball_Attack2(entity actor)
 
        proj.angles = vectoangles(proj.velocity);
        proj.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, proj);
+       IL_PUSH(g_bot_dodge, proj);
     proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
 
        CSQCProjectile(proj, true, PROJECTILE_FIREMINE, true);
@@ -381,18 +389,19 @@ METHOD(Fireball, wr_think, void(entity thiswep, entity actor, .entity weaponenti
 {
     if(fire & 1)
     {
-        if(time >= actor.fireball_primarytime)
+       int slot = weaponslot(weaponentity);
+        if(time >= actor.fireball_primarytime[slot])
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(fireball, refire)))
         {
             W_Fireball_Attack1_Frame0(thiswep, actor, weaponentity, fire);
-            actor.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor(actor);
+            actor.fireball_primarytime[slot] = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor(actor);
         }
     }
     else if(fire & 2)
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(fireball, refire)))
         {
-            W_Fireball_Attack2(actor);
+            W_Fireball_Attack2(actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready);
         }
     }
@@ -411,7 +420,8 @@ METHOD(Fireball, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(Fireball, wr_resetplayer, void(entity thiswep, entity actor))
 {
-    actor.fireball_primarytime = time;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       actor.fireball_primarytime[slot] = time;
 }
 METHOD(Fireball, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/fireball.qh b/qcsrc/common/weapons/weapon/fireball.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index d41ad95b9302cbee75425bfe38ffce8f331e414d..684ee75d377540a15de2911269dbf6ccf078d982 100644 (file)
@@ -1,12 +1,13 @@
+#include "hagar.qh"
 #ifndef IMPLEMENTATION
 CLASS(Hagar, Weapon)
-/* ammotype  */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets)
-/* impulse   */ ATTRIB(Hagar, impulse, int, 8)
+/* ammotype  */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets);
+/* impulse   */ ATTRIB(Hagar, impulse, int, 8);
 /* flags     */ ATTRIB(Hagar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Hagar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Hagar, wpcolor, vector, '1 1 0.5');
 /* modelname */ ATTRIB(Hagar, mdl, string, "hagar");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Hagar, m_model, Model, MDL_HAGAR_ITEM);
 #endif
 /* crosshair */ ATTRIB(Hagar, w_crosshair, string, "gfx/crosshairhagar");
@@ -63,30 +64,30 @@ spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); }
 
 // NO bounce protection, as bounces are limited!
 
-void W_Hagar_Explode(entity this)
+void W_Hagar_Explode(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
-       RadiusDamage(this, this.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), NULL, NULL, WEP_CVAR_PRI(hagar, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), NULL, NULL, WEP_CVAR_PRI(hagar, force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
 }
 
 void W_Hagar_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_Hagar_Explode(this);
+       W_Hagar_Explode(this, trigger);
 }
 
-void W_Hagar_Explode2(entity this)
+void W_Hagar_Explode2(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
-       RadiusDamage(this, this.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), NULL, NULL, WEP_CVAR_SEC(hagar, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), NULL, NULL, WEP_CVAR_SEC(hagar, force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
 }
 
 void W_Hagar_Explode2_use(entity this, entity actor, entity trigger)
 {
-       W_Hagar_Explode2(this);
+       W_Hagar_Explode2(this, trigger);
 }
 
 void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -113,18 +114,18 @@ void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage
                W_PrepareExplosionByDamage(this, attacker, getthink(this));
 }
 
-void W_Hagar_Touch(entity this)
+void W_Hagar_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       this.use(this, NULL, NULL);
+       PROJECTILE_TOUCH(this, toucher);
+       this.use(this, NULL, toucher);
 }
 
-void W_Hagar_Touch2(entity this)
+void W_Hagar_Touch2(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
-       if(this.cnt > 0 || other.takedamage == DAMAGE_AIM) {
-               this.use(this, NULL, NULL);
+       if(this.cnt > 0 || toucher.takedamage == DAMAGE_AIM) {
+               this.use(this, NULL, toucher);
        } else {
                this.cnt++;
                Send_Effect(EFFECT_HAGAR_BOUNCE, this.origin, this.velocity, 1);
@@ -134,13 +135,13 @@ void W_Hagar_Touch2(entity this)
        }
 }
 
-void W_Hagar_Attack(Weapon thiswep, entity actor)
+void W_Hagar_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity missile;
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(hagar, ammo));
 
-       W_SetupShot(actor, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage));
+       W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage));
 
        Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
@@ -154,6 +155,7 @@ void W_Hagar_Attack(Weapon thiswep, entity actor)
        missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale);
        missile.event_damage = W_Hagar_Damage;
        missile.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, missile);
 
        settouch(missile, W_Hagar_Touch);
        missile.use = W_Hagar_Explode_use;
@@ -164,11 +166,13 @@ void W_Hagar_Attack(Weapon thiswep, entity actor)
        setorigin(missile, w_shotorg);
        setsize(missile, '0 0 0', '0 0 0');
 
-       missile.movetype = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        W_SetupProjVelocity_PRI(missile, hagar);
 
        missile.angles = vectoangles(missile.velocity);
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags = MIF_SPLASH;
 
        CSQCProjectile(missile, true, PROJECTILE_HAGAR, true);
@@ -176,13 +180,13 @@ void W_Hagar_Attack(Weapon thiswep, entity actor)
        MUTATOR_CALLHOOK(EditProjectile, actor, missile);
 }
 
-void W_Hagar_Attack2(Weapon thiswep, entity actor)
+void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity missile;
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo));
 
-       W_SetupShot(actor, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
+       W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
 
        Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
@@ -196,6 +200,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor)
        missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
        missile.event_damage = W_Hagar_Damage;
        missile.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, missile);
 
        settouch(missile, W_Hagar_Touch2);
        missile.cnt = 0;
@@ -207,11 +212,13 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor)
        setorigin(missile, w_shotorg);
        setsize(missile, '0 0 0', '0 0 0');
 
-       missile.movetype = MOVETYPE_BOUNCEMISSILE;
+       set_movetype(missile, MOVETYPE_BOUNCEMISSILE);
        W_SetupProjVelocity_SEC(missile, hagar);
 
        missile.angles = vectoangles(missile.velocity);
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags = MIF_SPLASH;
 
        CSQCProjectile(missile, true, PROJECTILE_HAGAR_BOUNCING, true);
@@ -234,7 +241,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity)
 
        weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire));
 
-       W_SetupShot(actor, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
+       W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
        Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        forward = v_forward;
@@ -255,6 +262,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity)
                missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
                missile.event_damage = W_Hagar_Damage;
                missile.damagedbycontents = true;
+               IL_PUSH(g_damagedbycontents, missile);
 
                settouch(missile, W_Hagar_Touch); // not bouncy
                missile.use = W_Hagar_Explode2_use;
@@ -264,7 +272,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity)
                missile.projectiledeathtype = WEP_HAGAR.m_id | HITTYPE_SECONDARY;
                setorigin(missile, w_shotorg);
                setsize(missile, '0 0 0', '0 0 0');
-               missile.movetype = MOVETYPE_FLY;
+               set_movetype(missile, MOVETYPE_FLY);
                missile.missile_flags = MIF_SPLASH;
 
                // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar)
@@ -288,6 +296,8 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity)
 
                missile.angles = vectoangles(missile.velocity);
                missile.flags = FL_PROJECTILE;
+               IL_PUSH(g_projectiles, missile);
+               IL_PUSH(g_bot_dodge, missile);
 
                CSQCProjectile(missile, true, PROJECTILE_HAGAR, true);
 
@@ -409,7 +419,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity)
 
 void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock)
+       if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock || PS(actor).m_switchweapon != WEP_HAGAR)
        {
                w_ready(thiswep, actor, weaponentity, fire);
                return;
@@ -423,7 +433,7 @@ void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int
                return;
        }
 
-       W_Hagar_Attack(thiswep, actor);
+       W_Hagar_Attack(thiswep, actor, weaponentity);
 
        int slot = weaponslot(weaponentity);
        ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor(actor);
@@ -463,19 +473,18 @@ METHOD(Hagar, wr_think, void(entity thiswep, entity actor, .entity weaponentity,
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire)))
         {
-            W_Hagar_Attack2(thiswep, actor);
+            W_Hagar_Attack2(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
         }
     }
 }
-METHOD(Hagar, wr_gonethink, void(entity thiswep, entity actor))
+METHOD(Hagar, wr_gonethink, void(entity thiswep, entity actor, .entity weaponentity))
 {
     // we lost the weapon and want to prepare switching away
     if(actor.hagar_load)
     {
-        .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-        actor.(weaponentity).state = WS_READY;
-        W_Hagar_Attack2_Load_Release(actor, weaponentity);
+       actor.(weaponentity).state = WS_READY;
+       W_Hagar_Attack2_Load_Release(actor, weaponentity);
     }
 }
 METHOD(Hagar, wr_setup, void(entity thiswep, entity actor))
@@ -504,17 +513,16 @@ METHOD(Hagar, wr_resetplayer, void(entity thiswep, entity actor))
 {
     actor.hagar_load = 0;
 }
-METHOD(Hagar, wr_playerdeath, void(entity thiswep, entity actor))
+METHOD(Hagar, wr_playerdeath, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    .entity weaponentity = weaponentities[0]; // TODO: unhardcode
     // if we have any rockets loaded when we die, release them
     if(actor.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
-        W_Hagar_Attack2_Load_Release(actor, weaponentity);
+       W_Hagar_Attack2_Load_Release(actor, weaponentity);
 }
 METHOD(Hagar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
     if(!actor.hagar_load) // require releasing loaded rockets first
-        W_Reload(actor, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND_RELOAD);
+        W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND_RELOAD);
 }
 METHOD(Hagar, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/hagar.qh b/qcsrc/common/weapons/weapon/hagar.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c176e84c677ee90da7b1ee6e23ffc2c3d1cf4305..79bf08193ec05b9b38e3fa1ac1be46ca102043f3 100644 (file)
@@ -1,12 +1,13 @@
+#include "hlac.qh"
 #ifndef IMPLEMENTATION
 CLASS(HLAC, Weapon)
-/* ammotype  */ ATTRIB(HLAC, ammo_field, .int, ammo_cells)
-/* impulse   */ ATTRIB(HLAC, impulse, int, 6)
+/* ammotype  */ ATTRIB(HLAC, ammo_field, .int, ammo_cells);
+/* impulse   */ ATTRIB(HLAC, impulse, int, 6);
 /* flags     */ ATTRIB(HLAC, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(HLAC, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(HLAC, wpcolor, vector, '0 1 0');
 /* modelname */ ATTRIB(HLAC, mdl, string, "hlac");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(HLAC, m_model, Model, MDL_HLAC_ITEM);
 #endif
 /* crosshair */ ATTRIB(HLAC, w_crosshair, string, "gfx/crosshairhlac");
@@ -54,22 +55,22 @@ REGISTER_WEAPON(HLAC, hlac, NEW(HLAC));
 #ifdef SVQC
 spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(this, WEP_HLAC); }
 
-void W_HLAC_Touch(entity this)
+void W_HLAC_Touch(entity this, entity toucher)
 {
        float isprimary;
 
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        this.event_damage = func_null;
 
        isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
 
-       RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(hlac, isprimary, damage), WEP_CVAR_BOTH(hlac, isprimary, edgedamage), WEP_CVAR_BOTH(hlac, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(hlac, isprimary, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(hlac, isprimary, damage), WEP_CVAR_BOTH(hlac, isprimary, edgedamage), WEP_CVAR_BOTH(hlac, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(hlac, isprimary, force), this.projectiledeathtype, toucher);
 
-       remove(this);
+       delete(this);
 }
 
-void W_HLAC_Attack(Weapon thiswep, entity actor)
+void W_HLAC_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity missile;
     float spread;
@@ -81,7 +82,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor)
     if(actor.crouch)
         spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod);
 
-       W_SetupShot(actor, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage));
+       W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage));
        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
        if(!autocvar_g_norecoil)
        {
@@ -95,7 +96,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor)
 
     missile.bot_dodgerating = WEP_CVAR_PRI(hlac, damage);
 
-       missile.movetype = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(missile);
 
        setorigin(missile, w_shotorg);
@@ -110,6 +111,8 @@ void W_HLAC_Attack(Weapon thiswep, entity actor)
     missile.nextthink = time + WEP_CVAR_PRI(hlac, lifetime);
 
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.projectiledeathtype = WEP_HLAC.m_id;
 
        CSQCProjectile(missile, true, PROJECTILE_HLAC, true);
@@ -117,7 +120,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor)
        MUTATOR_CALLHOOK(EditProjectile, actor, missile);
 }
 
-void W_HLAC_Attack2(entity actor)
+void W_HLAC_Attack2(entity actor, .entity weaponentity)
 {
        entity missile;
     float spread;
@@ -128,7 +131,7 @@ void W_HLAC_Attack2(entity actor)
     if(actor.crouch)
         spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod);
 
-       W_SetupShot(actor, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage));
+       W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage));
        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        missile = new(hlacbolt);
@@ -137,7 +140,7 @@ void W_HLAC_Attack2(entity actor)
 
     missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage);
 
-       missile.movetype = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(missile);
 
        setorigin(missile, w_shotorg);
@@ -152,6 +155,8 @@ void W_HLAC_Attack2(entity actor)
     missile.nextthink = time + WEP_CVAR_SEC(hlac, lifetime);
 
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags = MIF_SPLASH;
        missile.projectiledeathtype = WEP_HLAC.m_id | HITTYPE_SECONDARY;
 
@@ -181,7 +186,7 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int
 
                int slot = weaponslot(weaponentity);
                ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(actor);
-               W_HLAC_Attack(WEP_HLAC, actor);
+               W_HLAC_Attack(WEP_HLAC, actor, weaponentity);
                actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
        }
@@ -191,14 +196,14 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int
        }
 }
 
-void W_HLAC_Attack2_Frame(Weapon thiswep, entity actor)
+void W_HLAC_Attack2_Frame(Weapon thiswep, entity actor, .entity weaponentity)
 {
     float i;
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hlac, ammo));
 
     for(i=WEP_CVAR_SEC(hlac, shots);i>0;--i)
-        W_HLAC_Attack2(actor);
+        W_HLAC_Attack2(actor, weaponentity);
 
        if(!autocvar_g_norecoil)
        {
@@ -220,7 +225,7 @@ METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity,
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(hlac, refire)))
         {
             actor.misc_bulletcounter = 0;
-            W_HLAC_Attack(thiswep, actor);
+            W_HLAC_Attack(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
         }
     }
@@ -229,7 +234,7 @@ METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity,
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hlac, refire)))
         {
-            W_HLAC_Attack2_Frame(thiswep, actor);
+            W_HLAC_Attack2_Frame(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
         }
     }
@@ -248,7 +253,7 @@ METHOD(HLAC, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(HLAC, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), SND_RELOAD);
 }
 METHOD(HLAC, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/hlac.qh b/qcsrc/common/weapons/weapon/hlac.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 5963b3bc7db84b7d86b4c0a4473f096df8a78e0c..f5dd96e4c3c6b22ebf31c95f09a09f3b3e7fa03c 100644 (file)
@@ -1,12 +1,13 @@
+#include "hook.qh"
 #ifndef IMPLEMENTATION
 CLASS(Hook, Weapon)
-/* ammotype  */ ATTRIB(Hook, ammo_field, .int, ammo_fuel)
-/* impulse   */ ATTRIB(Hook, impulse, int, 0)
+/* ammotype  */ ATTRIB(Hook, ammo_field, .int, ammo_fuel);
+/* impulse   */ ATTRIB(Hook, impulse, int, 0);
 /* flags     */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Hook, bot_pickupbasevalue, float, 0);
 /* color     */ ATTRIB(Hook, wpcolor, vector, '0 0.5 0');
 /* modelname */ ATTRIB(Hook, mdl, string, "hookgun");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Hook, m_model, Model, MDL_HOOK_ITEM);
 #endif
 /* crosshair */ ATTRIB(Hook, w_crosshair, string, "gfx/crosshairhook");
@@ -14,7 +15,7 @@ CLASS(Hook, Weapon)
 /* wepimg    */ ATTRIB(Hook, model2, string, "weaponhook");
 /* refname   */ ATTRIB(Hook, netname, string, "hook");
 /* wepname   */ ATTRIB(Hook, m_name, string, _("Grappling Hook"));
-       ATTRIB(Hook, ammo_factor, float, 1)
+       ATTRIB(Hook, ammo_factor, float, 1);
 
 #define X(BEGIN, P, END, class, prefix) \
        BEGIN(class) \
@@ -96,7 +97,7 @@ void W_Hook_ExplodeThink(entity this)
        if(dt < this.dmg_duration)
                this.nextthink = time + 0.05; // soon
        else
-               remove(this);
+               delete(this);
 }
 
 void W_Hook_Explode2(entity this)
@@ -115,7 +116,7 @@ void W_Hook_Explode2(entity this)
        this.dmg_duration = WEP_CVAR_SEC(hook, duration);
        this.teleport_time = time;
        this.dmg_last = 1;
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
 }
 
 void W_Hook_Explode2_use(entity this, entity actor, entity trigger)
@@ -137,22 +138,22 @@ void W_Hook_Damage(entity this, entity inflictor, entity attacker, float damage,
                W_PrepareExplosionByDamage(this, this.realowner, W_Hook_Explode2);
 }
 
-void W_Hook_Touch2(entity this)
+void W_Hook_Touch2(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
        this.use(this, NULL, NULL);
 }
 
-void W_Hook_Attack2(Weapon thiswep, entity actor)
+void W_Hook_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
 {
        //W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb)
-       W_SetupShot(actor, false, 4, SND_HOOKBOMB_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
+       W_SetupShot(actor, weaponentity, false, 4, SND_HOOKBOMB_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
 
        entity gren = new(hookbomb);
        gren.owner = gren.realowner = actor;
        gren.bot_dodge = true;
        gren.bot_dodgerating = WEP_CVAR_SEC(hook, damage);
-       gren.movetype = MOVETYPE_TOSS;
+       set_movetype(gren, MOVETYPE_TOSS);
        PROJECTILE_MAKETRIGGER(gren);
        gren.projectiledeathtype = WEP_HOOK.m_id | HITTYPE_SECONDARY;
        setorigin(gren, w_shotorg);
@@ -168,6 +169,7 @@ void W_Hook_Attack2(Weapon thiswep, entity actor)
        gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale);
        gren.event_damage = W_Hook_Damage;
        gren.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
 
        gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed);
@@ -179,6 +181,8 @@ void W_Hook_Attack2(Weapon thiswep, entity actor)
 
        gren.angles = '0 0 0';
        gren.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, gren);
+       IL_PUSH(g_bot_dodge, gren);
 
        CSQCProjectile(gren, true, PROJECTILE_HOOKBOMB, true);
 
@@ -207,7 +211,7 @@ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity,
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hook, refire)))
         {
-            W_Hook_Attack2(thiswep, actor);
+            W_Hook_Attack2(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
         }
     }
@@ -380,13 +384,16 @@ void Draw_GrapplingHook(entity this)
                        break;
        }
 
-       if((this.owner.sv_entnum == player_localentnum - 1) && autocvar_chase_active <= 0)
+       if((this.owner.sv_entnum == player_localentnum - 1))
        {
                switch(this.HookType)
                {
                        default:
                        case NET_ENT_CLIENT_HOOK:
-                               a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
+                               if(autocvar_chase_active > 0)
+                                       a = csqcplayer.origin;
+                               else
+                                       a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
                                b = this.origin;
                                break;
                        case NET_ENT_CLIENT_ARC_BEAM:
@@ -441,6 +448,10 @@ void Draw_GrapplingHook(entity this)
                        break;
        }
 
+       MUTATOR_CALLHOOK(DrawGrapplingHook, this, tex, rgb, t);
+       tex = M_ARGV(1, string);
+       rgb = M_ARGV(2, vector);
+
        Draw_GrapplingHook_trace_callback_tex = tex;
        Draw_GrapplingHook_trace_callback_rnd = offset;
        Draw_GrapplingHook_trace_callback_rgb = rgb;
@@ -537,6 +548,7 @@ NET_HANDLE(ENT_CLIENT_HOOK, bool bIsNew)
        if(bIsNew || !this.teleport_time)
        {
                this.draw = Draw_GrapplingHook;
+               IL_PUSH(g_drawables, this);
                this.entremove = Remove_GrapplingHook;
 
                switch(this.HookType)
diff --git a/qcsrc/common/weapons/weapon/hook.qh b/qcsrc/common/weapons/weapon/hook.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 331e2dd92925c8d768351e8cf572390cc6323706..81da28ddb05b3741fa0d68f180829f8b49380130 100644 (file)
@@ -1,12 +1,13 @@
+#include "machinegun.qh"
 #ifndef IMPLEMENTATION
 CLASS(MachineGun, Weapon)
-/* ammotype  */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails)
-/* impulse   */ ATTRIB(MachineGun, impulse, int, 3)
+/* ammotype  */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails);
+/* impulse   */ ATTRIB(MachineGun, impulse, int, 3);
 /* flags     */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 /* rating    */ ATTRIB(MachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(MachineGun, wpcolor, vector, '1 1 0');
 /* modelname */ ATTRIB(MachineGun, mdl, string, "uzi");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(MachineGun, m_model, Model, MDL_MACHINEGUN_ITEM);
 #endif
 /* crosshair */ ATTRIB(MachineGun, w_crosshair, string, "gfx/crosshairuzi");
@@ -88,27 +89,29 @@ void W_MachineGun_MuzzleFlash_Think(entity this)
 
 }
 
-void W_MachineGun_MuzzleFlash(entity actor)
+void W_MachineGun_MuzzleFlash(entity actor, .entity weaponentity)
 {
-       if(actor.muzzle_flash == NULL)
-               actor.muzzle_flash = spawn();
+       entity wepent = actor.(weaponentity);
+
+       if(wepent.muzzle_flash == NULL)
+               wepent.muzzle_flash = spawn();
 
        // muzzle flash for 1st person view
-       setmodel(actor.muzzle_flash, MDL_MACHINEGUN_MUZZLEFLASH); // precision set below
-
-       actor.muzzle_flash.scale = 0.75;
-       setthink(actor.muzzle_flash, W_MachineGun_MuzzleFlash_Think);
-       actor.muzzle_flash.nextthink = time + 0.02;
-       actor.muzzle_flash.frame = 2;
-       actor.muzzle_flash.alpha = 0.75;
-       actor.muzzle_flash.angles_z = random() * 180;
-       actor.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
-       actor.muzzle_flash.owner = actor.muzzle_flash.realowner = actor;
+       setmodel(wepent.muzzle_flash, MDL_MACHINEGUN_MUZZLEFLASH); // precision set below
+
+       wepent.muzzle_flash.scale = 0.75;
+       setthink(wepent.muzzle_flash, W_MachineGun_MuzzleFlash_Think);
+       wepent.muzzle_flash.nextthink = time + 0.02;
+       wepent.muzzle_flash.frame = 2;
+       wepent.muzzle_flash.alpha = 0.75;
+       wepent.muzzle_flash.angles_z = random() * 180;
+       wepent.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       wepent.muzzle_flash.owner = wepent.muzzle_flash.realowner = wepent;
 }
 
 void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity weaponentity)
 {
-       W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
+       W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
        if(!autocvar_g_norecoil)
        {
                actor.punchangle_x = random() - 0.5;
@@ -125,12 +128,15 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we
 
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
-       W_MachineGun_MuzzleFlash(actor);
-       W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
+       W_MachineGun_MuzzleFlash(actor, weaponentity);
+       W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
 
        // casing code
        if(autocvar_g_casings >= 2)
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+       {
+               makevectors(actor.v_angle); // for some reason, this is lost
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+       }
 
        if(actor.misc_bulletcounter == 1)
                W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo));
@@ -184,7 +190,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity
 
        W_DecreaseAmmo(WEP_MACHINEGUN, actor, WEP_CVAR(machinegun, sustained_ammo));
 
-       W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
+       W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
        if(!autocvar_g_norecoil)
        {
                actor.punchangle_x = random() - 0.5;
@@ -198,11 +204,14 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity
 
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
-       W_MachineGun_MuzzleFlash(actor);
-       W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
+       W_MachineGun_MuzzleFlash(actor, weaponentity);
+       W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
 
        if(autocvar_g_casings >= 2) // casing code
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+       {
+               makevectors(actor.v_angle); // for some reason, this is lost
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+       }
 
        int slot = weaponslot(weaponentity);
        ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
@@ -211,7 +220,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity
 
 void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
-       W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
+       W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
        if(!autocvar_g_norecoil)
        {
                actor.punchangle_x = random() - 0.5;
@@ -222,11 +231,14 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit
 
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
-       W_MachineGun_MuzzleFlash(actor);
-       W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
+       W_MachineGun_MuzzleFlash(actor, weaponentity);
+       W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
 
        if(autocvar_g_casings >= 2) // casing code
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+       {
+               makevectors(actor.v_angle); // for some reason, this is lost
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+       }
 
        actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
        if(actor.misc_bulletcounter == 0)
@@ -336,7 +348,7 @@ METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(MachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD);
 }
 METHOD(MachineGun, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/machinegun.qh b/qcsrc/common/weapons/weapon/machinegun.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index a406e750980e2749c656682821f49618a1de6079..7df6984baabb4b584570b7b7b39a958efae4c33f 100644 (file)
@@ -1,12 +1,13 @@
+#include "minelayer.qh"
 #ifndef IMPLEMENTATION
 CLASS(MineLayer, Weapon)
-/* ammotype  */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets)
-/* impulse   */ ATTRIB(MineLayer, impulse, int, 4)
+/* ammotype  */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets);
+/* impulse   */ ATTRIB(MineLayer, impulse, int, 4);
 /* flags     */ ATTRIB(MineLayer, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(MineLayer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 /* color     */ ATTRIB(MineLayer, wpcolor, vector, '0.75 1 0');
 /* modelname */ ATTRIB(MineLayer, mdl, string, "minelayer");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(MineLayer, m_model, Model, MDL_MINELAYER_ITEM);
 #endif
 /* crosshair */ ATTRIB(MineLayer, w_crosshair, string, "gfx/crosshairminelayer");
@@ -70,6 +71,7 @@ void W_MineLayer_Stick(entity this, entity to)
        // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
 
        entity newmine = spawn();
+       IL_PUSH(g_mines, newmine);
        newmine.classname = this.classname;
 
        newmine.bot_dodge = this.bot_dodge;
@@ -90,8 +92,9 @@ void W_MineLayer_Stick(entity this, entity to)
        newmine.event_damage = this.event_damage;
        newmine.spawnshieldtime = this.spawnshieldtime;
        newmine.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, newmine);
 
-       newmine.movetype = MOVETYPE_NONE; // lock the mine in place
+       set_movetype(newmine, MOVETYPE_NONE); // lock the mine in place
        newmine.projectiledeathtype = this.projectiledeathtype;
 
        newmine.mine_time = this.mine_time;
@@ -101,26 +104,28 @@ void W_MineLayer_Stick(entity this, entity to)
        newmine.nextthink = time;
        newmine.cnt = this.cnt;
        newmine.flags = this.flags;
+       IL_PUSH(g_projectiles, newmine);
+       IL_PUSH(g_bot_dodge, newmine);
 
-       remove(this);
+       delete(this);
 
        if(to)
                SetMovetypeFollow(newmine, to);
 }
 
-void W_MineLayer_Explode(entity this)
+void W_MineLayer_Explode(entity this, entity directhitentity)
 {
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), NULL, NULL, WEP_CVAR(minelayer, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), NULL, NULL, WEP_CVAR(minelayer, force), this.projectiledeathtype, directhitentity);
 
        if(PS(this.realowner).m_weapon == WEP_MINE_LAYER)
        {
@@ -129,13 +134,19 @@ void W_MineLayer_Explode(entity this)
                if(!w.wr_checkammo1(w, own))
                {
                        own.cnt = WEP_MINE_LAYER.m_id;
-                       int slot = 0; // TODO: unhardcode
+                       .entity weaponentity = this.weaponentity_fld;
+                       int slot = weaponslot(weaponentity);
                        ATTACK_FINISHED(own, slot) = time;
                        PS(own).m_switchweapon = w_getbestweapon(own);
                }
        }
        this.realowner.minelayer_mines -= 1;
-       remove(this);
+       delete(this);
+}
+
+void W_MineLayer_Explode_think(entity this)
+{
+       W_MineLayer_Explode(this, NULL);
 }
 
 void W_MineLayer_DoRemoteExplode(entity this)
@@ -143,7 +154,7 @@ void W_MineLayer_DoRemoteExplode(entity this)
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       if(this.movetype == MOVETYPE_NONE || this.movetype == MOVETYPE_FOLLOW)
+       if(this.move_movetype == MOVETYPE_NONE || this.move_movetype == MOVETYPE_FOLLOW)
                this.velocity = this.mine_orientation; // particle fx and decals need .velocity
 
        RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), NULL, NULL, WEP_CVAR(minelayer, remote_force), this.projectiledeathtype | HITTYPE_BOUNCE, NULL);
@@ -155,13 +166,14 @@ void W_MineLayer_DoRemoteExplode(entity this)
                if(!w.wr_checkammo1(w, own))
                {
                        own.cnt = WEP_MINE_LAYER.m_id;
-                       int slot = 0; // TODO: unhardcode
+                       .entity weaponentity = this.weaponentity_fld;
+                       int slot = weaponslot(weaponentity);
                        ATTACK_FINISHED(own, slot) = time;
                        PS(own).m_switchweapon = w_getbestweapon(own);
                }
        }
        this.realowner.minelayer_mines -= 1;
-       remove(this);
+       delete(this);
 }
 
 void W_MineLayer_RemoteExplode(entity this)
@@ -192,15 +204,16 @@ void W_MineLayer_ProximityExplode(entity this)
        }
 
        this.mine_time = 0;
-       W_MineLayer_Explode(this);
+       W_MineLayer_Explode(this, NULL);
 }
 
 int W_MineLayer_Count(entity e)
 {
        int minecount = 0;
-       entity mine;
-       for(mine = NULL; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e)
+       IL_EACH(g_mines, it.realowner == e,
+       {
                minecount += 1;
+       });
 
        return minecount;
 }
@@ -211,12 +224,12 @@ void W_MineLayer_Think(entity this)
 
        this.nextthink = time;
 
-       if(this.movetype == MOVETYPE_FOLLOW)
+       if(this.move_movetype == MOVETYPE_FOLLOW)
        {
                if(LostMovetypeFollow(this))
                {
                        UnsetMovetypeFollow(this);
-                       this.movetype = MOVETYPE_NONE;
+                       set_movetype(this, MOVETYPE_NONE);
                }
        }
 
@@ -234,9 +247,8 @@ void W_MineLayer_Think(entity this)
        // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
        if(!IS_PLAYER(this.realowner) || IS_DEAD(this.realowner) || STAT(FROZEN, this.realowner))
        {
-               other = NULL;
                this.projectiledeathtype |= HITTYPE_BOUNCE;
-               W_MineLayer_Explode(this);
+               W_MineLayer_Explode(this, NULL);
                return;
        }
 
@@ -268,26 +280,26 @@ void W_MineLayer_Think(entity this)
                W_MineLayer_RemoteExplode(this);
 }
 
-void W_MineLayer_Touch(entity this)
+void W_MineLayer_Touch(entity this, entity toucher)
 {
-       if(this.movetype == MOVETYPE_NONE || this.movetype == MOVETYPE_FOLLOW)
+       if(this.move_movetype == MOVETYPE_NONE || this.move_movetype == MOVETYPE_FOLLOW)
                return; // we're already a stuck mine, why do we get called? TODO does this even happen?
 
-       if(WarpZone_Projectile_Touch(this))
+       if(WarpZone_Projectile_Touch(this, toucher))
        {
                if(wasfreed(this))
                        this.realowner.minelayer_mines -= 1;
                return;
        }
 
-       if(other && IS_PLAYER(other) && !IS_DEAD(other))
+       if(toucher && IS_PLAYER(toucher) && !IS_DEAD(toucher))
        {
                // hit a player
                // don't stick
        }
        else
        {
-               W_MineLayer_Stick(this, other);
+               W_MineLayer_Stick(this, toucher);
        }
 }
 
@@ -305,10 +317,10 @@ void W_MineLayer_Damage(entity this, entity inflictor, entity attacker, float da
        this.angles = vectoangles(this.velocity);
 
        if(this.health <= 0)
-               W_PrepareExplosionByDamage(this, attacker, W_MineLayer_Explode);
+               W_PrepareExplosionByDamage(this, attacker, W_MineLayer_Explode_think);
 }
 
-void W_MineLayer_Attack(Weapon thiswep, entity actor)
+void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity mine;
        entity flash;
@@ -327,10 +339,12 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor)
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR(minelayer, ammo));
 
-       W_SetupShot_ProjectileSize(actor, '-4 -4 -4', '4 4 4', false, 5, SND_MINE_FIRE, CH_WEAPON_A, WEP_CVAR(minelayer, damage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 5, SND_MINE_FIRE, CH_WEAPON_A, WEP_CVAR(minelayer, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        mine = WarpZone_RefSys_SpawnSameRefSys(actor);
+       mine.weaponentity_fld = weaponentity;
+       IL_PUSH(g_mines, mine);
        mine.owner = mine.realowner = actor;
        if(WEP_CVAR(minelayer, detonatedelay) >= 0)
                mine.spawnshieldtime = time + WEP_CVAR(minelayer, detonatedelay);
@@ -345,8 +359,9 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor)
        mine.health = WEP_CVAR(minelayer, health);
        mine.event_damage = W_MineLayer_Damage;
        mine.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, mine);
 
-       mine.movetype = MOVETYPE_TOSS;
+       set_movetype(mine, MOVETYPE_TOSS);
        PROJECTILE_MAKETRIGGER(mine);
        mine.projectiledeathtype = WEP_MINE_LAYER.m_id;
        setsize(mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot
@@ -360,6 +375,8 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor)
        mine.nextthink = time;
        mine.cnt = (WEP_CVAR(minelayer, lifetime) - WEP_CVAR(minelayer, lifetime_countdown));
        mine.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, mine);
+       IL_PUSH(g_bot_dodge, mine);
        mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY;
 
        if(mine.cnt > 0) { mine.cnt += time; }
@@ -371,7 +388,7 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor)
        setmodel(flash, MDL_MINELAYER_MUZZLEFLASH); // precision set below
        SUB_SetFade(flash, time, 0.1);
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
-       W_AttachToShotorg(actor, flash, '5 0 0');
+       W_AttachToShotorg(actor, weaponentity, flash, '5 0 0');
 
        // common properties
 
@@ -380,24 +397,23 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor)
        actor.minelayer_mines = W_MineLayer_Count(actor);
 }
 
-float W_MineLayer_PlacedMines(entity this, float detonate)
+bool W_MineLayer_PlacedMines(entity this, bool detonate)
 {
-       entity mine;
-       float minfound = 0;
+       bool minfound = false;
 
-       for(mine = NULL; (mine = find(mine, classname, "mine")); ) if(mine.realowner == this)
+       IL_EACH(g_mines, it.realowner == this,
        {
                if(detonate)
                {
-                       if(!mine.minelayer_detonate)
+                       if(!it.minelayer_detonate)
                        {
-                               mine.minelayer_detonate = true;
-                               minfound = 1;
+                               it.minelayer_detonate = true;
+                               minfound = true;
                        }
                }
                else
-                       minfound = 1;
-       }
+                       minfound = true;
+       });
        return minfound;
 }
 
@@ -411,8 +427,7 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor))
     if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
     {
         // decide whether to detonate mines
-        entity targetlist, targ;
-        float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+        float edgedamage, coredamage, edgeradius, recipricoledgeradius;
         float selfdamage, teamdamage, enemydamage;
         edgedamage = WEP_CVAR(minelayer, edgedamage);
         coredamage = WEP_CVAR(minelayer, damage);
@@ -421,31 +436,24 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor))
         selfdamage = 0;
         teamdamage = 0;
         enemydamage = 0;
-        targetlist = findchainfloat(bot_attack, true);
-        entity mine = find(NULL, classname, "mine");
-        while(mine)
+
+        IL_EACH(g_mines, it.realowner == actor,
         {
-            if(mine.realowner != actor)
-            {
-                mine = find(mine, classname, "mine");
-                continue;
-            }
-            targ = targetlist;
-            while(targ)
-            {
-                d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin);
-                d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+               entity mine = it;
+               IL_EACH(g_bot_targets, it.bot_attack,
+               {
+                       float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - mine.origin);
+                       d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
                 // count potential damage according to type of target
-                if(targ == actor)
+                if(it == actor)
                     selfdamage = selfdamage + d;
-                else if(targ.team == actor.team && teamplay)
+                else if(SAME_TEAM(it, actor))
                     teamdamage = teamdamage + d;
-                else if(bot_shouldattack(actor, targ))
+                else if(bot_shouldattack(actor, it))
                     enemydamage = enemydamage + d;
-                targ = targ.chain;
-            }
-            mine = find(mine, classname, "mine");
-        }
+               });
+        });
+
         float desirabledamage;
         desirabledamage = enemydamage;
         if(time > actor.invincible_finished && time > actor.spawnshieldtime)
@@ -453,41 +461,35 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor))
         if(teamplay && actor.team)
             desirabledamage = desirabledamage - teamdamage;
 
-        mine = find(NULL, classname, "mine");
-        while(mine)
+        makevectors(actor.v_angle);
+        IL_EACH(g_mines, it.realowner == actor,
         {
-            if(mine.realowner != actor)
-            {
-                mine = find(mine, classname, "mine");
-                continue;
-            }
-            makevectors(mine.v_angle);
-            targ = targetlist;
             if(skill > 9) // normal players only do this for the target they are tracking
             {
-                targ = targetlist;
-                while(targ)
-                {
-                    if(
-                        (v_forward * normalize(mine.origin - targ.origin)< 0.1)
-                        && desirabledamage > 0.1*coredamage
-                    ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
-                    targ = targ.chain;
-                }
-            }else{
-                float distance; distance= bound(300,vlen(actor.origin-actor.enemy.origin),30000);
+                   entity mine = it;
+                   IL_EACH(g_bot_targets, it.bot_attack,
+                   {
+                       if((v_forward * normalize(mine.origin - it.origin) < 0.1)
+                           && desirabledamage > 0.1 * coredamage
+                           ) PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+                   });
+               }
+               else
+               {
                 //As the distance gets larger, a correct detonation gets near imposible
                 //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
-                if(v_forward * normalize(mine.origin - actor.enemy.origin)< 0.1)
-                    if(IS_PLAYER(actor.enemy))
-                        if(desirabledamage >= 0.1*coredamage)
-                            if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
-                                PHYS_INPUT_BUTTON_ATCK2(actor) = true;
-            // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
-            }
-
-            mine = find(mine, classname, "mine");
-        }
+                if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1)
+                       && IS_PLAYER(actor.enemy)
+                       && (desirabledamage >= 0.1 * coredamage)
+                       )
+                {
+                       float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000);
+                       if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1))
+                               PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+                }
+               }
+        });
+
         // if we would be doing at X percent of the core damage, detonate it
         // but don't fire a new shot at the same time!
         if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
@@ -512,7 +514,7 @@ METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponent
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(minelayer, refire)))
         {
-            W_MineLayer_Attack(thiswep, actor);
+            W_MineLayer_Attack(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
         }
     }
@@ -548,7 +550,7 @@ METHOD(MineLayer, wr_resetplayer, void(entity thiswep, entity actor))
 }
 METHOD(MineLayer, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, WEP_CVAR(minelayer, ammo), SND_RELOAD);
+    W_Reload(actor, weaponentity, WEP_CVAR(minelayer, ammo), SND_RELOAD);
 }
 METHOD(MineLayer, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/minelayer.qh b/qcsrc/common/weapons/weapon/minelayer.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 5f56f19db19b2080d7255318562d150ee2be1787..89ff5bbd8852eef69e0c497a679da28c5bde1293 100644 (file)
@@ -1,12 +1,13 @@
+#include "mortar.qh"
 #ifndef IMPLEMENTATION
 CLASS(Mortar, Weapon)
-/* ammotype  */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets)
-/* impulse   */ ATTRIB(Mortar, impulse, int, 4)
+/* ammotype  */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets);
+/* impulse   */ ATTRIB(Mortar, impulse, int, 4);
 /* flags     */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Mortar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Mortar, wpcolor, vector, '1 0 0');
 /* modelname */ ATTRIB(Mortar, mdl, string, "gl");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Mortar, m_model, Model, MDL_MORTAR_ITEM);
 #endif
 /* crosshair */ ATTRIB(Mortar, w_crosshair, string, "gfx/crosshairgrenadelauncher");
@@ -64,54 +65,54 @@ REGISTER_WEAPON(MORTAR, mortar, NEW(Mortar));
 spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(this, WEP_MORTAR); }
 spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); }
 
-void W_Mortar_Grenade_Explode(entity this)
+void W_Mortar_Grenade_Explode(entity this, entity directhitentity)
 {
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       if(this.movetype == MOVETYPE_NONE)
+       if(this.move_movetype == MOVETYPE_NONE)
                this.velocity = this.oldvelocity;
 
-       RadiusDamage(this, this.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), NULL, NULL, WEP_CVAR_PRI(mortar, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), NULL, NULL, WEP_CVAR_PRI(mortar, force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
 }
 
 void W_Mortar_Grenade_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_Mortar_Grenade_Explode(this);
+       W_Mortar_Grenade_Explode(this, trigger);
 }
 
-void W_Mortar_Grenade_Explode2(entity this)
+void W_Mortar_Grenade_Explode2(entity this, entity directhitentity)
 {
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       if(this.movetype == MOVETYPE_NONE)
+       if(this.move_movetype == MOVETYPE_NONE)
                this.velocity = this.oldvelocity;
 
-       RadiusDamage(this, this.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), NULL, NULL, WEP_CVAR_SEC(mortar, force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), NULL, NULL, WEP_CVAR_SEC(mortar, force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
 }
 
 void W_Mortar_Grenade_Explode2_use(entity this, entity actor, entity trigger)
 {
-       W_Mortar_Grenade_Explode2(this);
+       W_Mortar_Grenade_Explode2(this, trigger);
 }
 
 void W_Mortar_Grenade_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -133,21 +134,20 @@ void W_Mortar_Grenade_Think1(entity this)
        this.nextthink = time;
        if(time > this.cnt)
        {
-               other = NULL;
                this.projectiledeathtype |= HITTYPE_BOUNCE;
-               W_Mortar_Grenade_Explode(this);
+               W_Mortar_Grenade_Explode(this, NULL);
                return;
        }
        if(this.gl_detonate_later && this.gl_bouncecnt >= WEP_CVAR_PRI(mortar, remote_minbouncecnt))
-               W_Mortar_Grenade_Explode(this);
+               W_Mortar_Grenade_Explode(this, NULL);
 }
 
-void W_Mortar_Grenade_Touch1(entity this)
+void W_Mortar_Grenade_Touch1(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       if(other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
+       PROJECTILE_TOUCH(this, toucher);
+       if(toucher.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
        {
-               this.use(this, NULL, NULL);
+               this.use(this, NULL, toucher);
        }
        else if(WEP_CVAR_PRI(mortar, type) == 1) // bounce
        {
@@ -156,14 +156,14 @@ void W_Mortar_Grenade_Touch1(entity this)
                this.projectiledeathtype |= HITTYPE_BOUNCE;
                this.gl_bouncecnt += 1;
        }
-       else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
+       else if(WEP_CVAR_PRI(mortar, type) == 2 && (!toucher || (toucher.takedamage != DAMAGE_AIM && toucher.move_movetype == MOVETYPE_NONE))) // stick
        {
                spamsound(this, CH_SHOTS, SND(GRENADE_STICK), VOL_BASE, ATTN_NORM);
 
                // let it stick whereever it is
                this.oldvelocity = this.velocity;
                this.velocity = '0 0 0';
-               this.movetype = MOVETYPE_NONE; // also disables gravity
+               set_movetype(this, MOVETYPE_NONE); // also disables gravity
                this.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
                UpdateCSQCProjectile(this);
 
@@ -174,12 +174,12 @@ void W_Mortar_Grenade_Touch1(entity this)
        }
 }
 
-void W_Mortar_Grenade_Touch2(entity this)
+void W_Mortar_Grenade_Touch2(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
-       if(other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
+       PROJECTILE_TOUCH(this, toucher);
+       if(toucher.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
        {
-               this.use(this, NULL, NULL);
+               this.use(this, NULL, toucher);
        }
        else if(WEP_CVAR_SEC(mortar, type) == 1) // bounce
        {
@@ -192,14 +192,14 @@ void W_Mortar_Grenade_Touch2(entity this)
                        this.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce);
 
        }
-       else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
+       else if(WEP_CVAR_SEC(mortar, type) == 2 && (!toucher || (toucher.takedamage != DAMAGE_AIM && toucher.move_movetype == MOVETYPE_NONE))) // stick
        {
                spamsound(this, CH_SHOTS, SND(GRENADE_STICK), VOL_BASE, ATTN_NORM);
 
                // let it stick whereever it is
                this.oldvelocity = this.velocity;
                this.velocity = '0 0 0';
-               this.movetype = MOVETYPE_NONE; // also disables gravity
+               set_movetype(this, MOVETYPE_NONE); // also disables gravity
                this.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
                UpdateCSQCProjectile(this);
 
@@ -210,22 +210,20 @@ void W_Mortar_Grenade_Touch2(entity this)
        }
 }
 
-void W_Mortar_Attack(Weapon thiswep, entity actor)
+void W_Mortar_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
-       entity gren;
-
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(mortar, ammo));
 
-       W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage));
        w_shotdir = v_forward; // no TrueAim for grenades please
 
        Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
-       gren = new(grenade);
+       entity gren = new(grenade);
        gren.owner = gren.realowner = actor;
        gren.bot_dodge = true;
        gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage);
-       gren.movetype = MOVETYPE_BOUNCE;
+       set_movetype(gren, MOVETYPE_BOUNCE);
        gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
        gren.bouncestop = WEP_CVAR(mortar, bouncestop);
        PROJECTILE_MAKETRIGGER(gren);
@@ -244,11 +242,14 @@ void W_Mortar_Attack(Weapon thiswep, entity actor)
        gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale);
        gren.event_damage = W_Mortar_Grenade_Damage;
        gren.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_UP_PRI(gren, mortar);
 
        gren.angles = vectoangles(gren.velocity);
        gren.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, gren);
+       IL_PUSH(g_bot_dodge, gren);
 
        if(WEP_CVAR_PRI(mortar, type) == 0 || WEP_CVAR_PRI(mortar, type) == 2)
                CSQCProjectile(gren, true, PROJECTILE_GRENADE, true);
@@ -258,13 +259,13 @@ void W_Mortar_Attack(Weapon thiswep, entity actor)
        MUTATOR_CALLHOOK(EditProjectile, actor, gren);
 }
 
-void W_Mortar_Attack2(Weapon thiswep, entity actor)
+void W_Mortar_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity gren;
 
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(mortar, ammo));
 
-       W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage));
        w_shotdir = v_forward; // no TrueAim for grenades please
 
        Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
@@ -273,7 +274,7 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor)
        gren.owner = gren.realowner = actor;
        gren.bot_dodge = true;
        gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage);
-       gren.movetype = MOVETYPE_BOUNCE;
+       set_movetype(gren, MOVETYPE_BOUNCE);
        gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
        gren.bouncestop = WEP_CVAR(mortar, bouncestop);
        PROJECTILE_MAKETRIGGER(gren);
@@ -291,11 +292,14 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor)
        gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale);
        gren.event_damage = W_Mortar_Grenade_Damage;
        gren.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_UP_SEC(gren, mortar);
 
        gren.angles = vectoangles(gren.velocity);
        gren.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, gren);
+       IL_PUSH(g_bot_dodge, gren);
 
        if(WEP_CVAR_SEC(mortar, type) == 0 || WEP_CVAR_SEC(mortar, type) == 2)
                CSQCProjectile(gren, true, PROJECTILE_GRENADE, true);
@@ -352,7 +356,7 @@ METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(mortar, refire)))
         {
-            W_Mortar_Attack(thiswep, actor);
+            W_Mortar_Attack(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
         }
     }
@@ -361,21 +365,20 @@ METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity
         if(WEP_CVAR_SEC(mortar, remote_detonateprimary))
         {
             bool nadefound = false;
-            entity nade;
-            for(nade = NULL; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == actor)
+            IL_EACH(g_projectiles, it.realowner == actor && it.classname == "grenade",
             {
-                if(!nade.gl_detonate_later)
+                if(!it.gl_detonate_later)
                 {
-                    nade.gl_detonate_later = true;
+                    it.gl_detonate_later = true;
                     nadefound = true;
                 }
-            }
+            });
             if(nadefound)
                 sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
         }
         else if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(mortar, refire)))
         {
-            W_Mortar_Attack2(thiswep, actor);
+            W_Mortar_Attack2(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
         }
     }
@@ -394,7 +397,7 @@ METHOD(Mortar, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(Mortar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), SND_RELOAD); // WEAPONTODO
+    W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), SND_RELOAD); // WEAPONTODO
 }
 METHOD(Mortar, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/mortar.qh b/qcsrc/common/weapons/weapon/mortar.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 3b047dc8c9d017ee2de7790a85ef1b3e6feb3be4..b998bc9ee8d41d42c7ee51b976cbe6cc4e63f7d3 100644 (file)
@@ -1,12 +1,13 @@
+#include "porto.qh"
 #ifndef IMPLEMENTATION
 CLASS(PortoLaunch, Weapon)
-/* ammotype  */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none)
-/* impulse   */ ATTRIB(PortoLaunch, impulse, int, 0)
+/* ammotype  */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none);
+/* impulse   */ ATTRIB(PortoLaunch, impulse, int, 0);
 /* flags     */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON);
 /* rating    */ ATTRIB(PortoLaunch, bot_pickupbasevalue, float, 0);
 /* color     */ ATTRIB(PortoLaunch, wpcolor, vector, '0.5 0.5 0.5');
 /* modelname */ ATTRIB(PortoLaunch, mdl, string, "porto");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(PortoLaunch, m_model, Model, MDL_PORTO_ITEM);
 #endif
 /* crosshair */ ATTRIB(PortoLaunch, w_crosshair, string, "gfx/crosshairporto");
@@ -62,7 +63,7 @@ void W_Porto_Success(entity this)
        }
 
        this.realowner.porto_current = NULL;
-       remove(this);
+       delete(this);
 }
 
 string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
@@ -89,6 +90,7 @@ void W_Porto_Fail(entity this, float failhard)
                if(move_out_of_solid(this))
                {
                        this.flags = FL_ITEM;
+                       IL_PUSH(g_items, this);
                        this.velocity = trigger_push_calculatevelocity(this.origin, this.realowner, 128);
                        tracetoss(this, this);
                        if(vdist(trace_endpos - this.realowner.origin, <, 128))
@@ -98,7 +100,7 @@ void W_Porto_Fail(entity this, float failhard)
                        }
                }
        }
-       remove(this);
+       delete(this);
 }
 
 void W_Porto_Remove(entity p)
@@ -113,25 +115,26 @@ void W_Porto_Think(entity this)
 {
        trace_plane_normal = '0 0 0';
        if(this.realowner.playerid != this.playerid)
-               remove(this);
+               delete(this);
        else
                W_Porto_Fail(this, 0);
 }
 
-void W_Porto_Touch(entity this)
+void W_Porto_Touch(entity this, entity toucher)
 {
        vector norm;
 
        // do not use PROJECTILE_TOUCH here
        // FIXME but DO handle warpzones!
 
-       if(other.classname == "portal")
+       if(toucher.classname == "portal")
                return; // handled by the portal
 
        norm = trace_plane_normal;
        if(trace_ent.iscreature)
        {
-               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * STAT(PL_MIN, NULL).z, MOVE_WORLDONLY, this);
+               // TODO: why not use entity size?
+               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_CONST.z, MOVE_WORLDONLY, this);
                if(trace_fraction >= 1)
                        return;
                if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
@@ -143,7 +146,7 @@ void W_Porto_Touch(entity this)
        if(this.realowner.playerid != this.playerid)
        {
                sound(this, CH_SHOTS, SND_PORTO_UNSUPPORTED, VOL_BASE, ATTEN_NORM);
-               remove(this);
+               delete(this);
        }
        else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
        {
@@ -240,11 +243,11 @@ void W_Porto_Touch(entity this)
        }
 }
 
-void W_Porto_Attack(entity actor, float type)
+void W_Porto_Attack(entity actor, .entity weaponentity, float type)
 {
        entity gren;
 
-       W_SetupShot(actor, false, 4, SND_PORTO_FIRE, CH_WEAPON_A, 0);
+       W_SetupShot(actor, weaponentity, false, 4, SND_PORTO_FIRE, CH_WEAPON_A, 0);
        // always shoot from the eye
        w_shotdir = v_forward;
        w_shotorg = actor.origin + actor.view_ofs + ((w_shotorg - actor.origin - actor.view_ofs) * v_forward) * v_forward;
@@ -257,7 +260,7 @@ void W_Porto_Attack(entity actor, float type)
        gren.playerid = actor.playerid;
        gren.bot_dodge = true;
        gren.bot_dodgerating = 200;
-       gren.movetype = MOVETYPE_BOUNCEMISSILE;
+       set_movetype(gren, MOVETYPE_BOUNCEMISSILE);
        PROJECTILE_MAKETRIGGER(gren);
        gren.effects = EF_RED;
        gren.scale = 4;
@@ -275,6 +278,8 @@ void W_Porto_Attack(entity actor, float type)
 
        gren.angles = vectoangles(gren.velocity);
        gren.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, gren);
+       IL_PUSH(g_bot_dodge, gren);
 
        gren.portal_id = time;
        actor.porto_current = gren;
@@ -309,7 +314,7 @@ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weapone
         if(!actor.porto_forbidden)
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire)))
         {
-            W_Porto_Attack(actor, 0);
+            W_Porto_Attack(actor, weaponentity, 0);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
         }
 
@@ -318,7 +323,7 @@ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weapone
         if(!actor.porto_forbidden)
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(porto, refire)))
         {
-            W_Porto_Attack(actor, 1);
+            W_Porto_Attack(actor, weaponentity, 1);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
         }
     }
@@ -351,7 +356,7 @@ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weapone
         if(!actor.porto_forbidden)
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire)))
         {
-            W_Porto_Attack(actor, -1);
+            W_Porto_Attack(actor, weaponentity, -1);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
         }
     }
@@ -377,7 +382,7 @@ METHOD(PortoLaunch, wr_resetplayer, void(entity thiswep, entity actor))
 #endif
 #ifdef CSQC
 METHOD(PortoLaunch, wr_impacteffect, void(entity this, entity actor)) {
-    LOG_WARNING("Since when does Porto send DamageInfo?\n");
+    LOG_WARN("Since when does Porto send DamageInfo?");
 }
 #endif
 #endif
diff --git a/qcsrc/common/weapons/weapon/porto.qh b/qcsrc/common/weapons/weapon/porto.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 13e44531029ae8b1c47d7adb2146a8e238653a67..478a3152536945aedae9ccbd9a53c369e52b8065 100644 (file)
@@ -1,12 +1,13 @@
+#include "rifle.qh"
 #ifndef IMPLEMENTATION
 CLASS(Rifle, Weapon)
-/* ammotype  */ ATTRIB(Rifle, ammo_field, .int, ammo_nails)
-/* impulse   */ ATTRIB(Rifle, impulse, int, 7)
+/* ammotype  */ ATTRIB(Rifle, ammo_field, .int, ammo_nails);
+/* impulse   */ ATTRIB(Rifle, impulse, int, 7);
 /* flags     */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 /* rating    */ ATTRIB(Rifle, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Rifle, wpcolor, vector, '0.5 1 0');
 /* modelname */ ATTRIB(Rifle, mdl, string, "campingrifle");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Rifle, m_model, Model, MDL_RIFLE_ITEM);
 #endif
 /* crosshair */ ATTRIB(Rifle, w_crosshair, string, "gfx/crosshairrifle");
@@ -56,13 +57,13 @@ spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(this, WEP_RIFLE); }
 spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); }
 spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); }
 
-void W_Rifle_FireBullet(Weapon thiswep, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, Sound pSound, entity actor)
+void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, Sound pSound, entity actor)
 {
        float i;
 
        W_DecreaseAmmo(thiswep, actor, pAmmo);
 
-       W_SetupShot(actor, true, 2, pSound, CH_WEAPON_A, pDamage * pShots);
+       W_SetupShot(actor, weaponentity, true, 2, pSound, CH_WEAPON_A, pDamage * pShots);
 
        Send_Effect(EFFECT_RIFLE_MUZZLEFLASH, w_shotorg, w_shotdir * 2000, 1);
 
@@ -76,20 +77,23 @@ void W_Rifle_FireBullet(Weapon thiswep, float pSpread, float pDamage, float pFor
                fireBullet(actor, w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE));
 
        if(autocvar_g_casings >= 2)
-               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+    {
+        makevectors(actor.v_angle); // for some reason, this is lost
+               SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+    }
 }
 
-void W_Rifle_Attack(entity actor)
+void W_Rifle_Attack(entity actor, .entity weaponentity)
 {
-       W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND_CAMPINGRIFLE_FIRE, actor);
+       W_Rifle_FireBullet(WEP_RIFLE, weaponentity, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND_CAMPINGRIFLE_FIRE, actor);
 }
 
-void W_Rifle_Attack2(entity actor)
+void W_Rifle_Attack2(entity actor, .entity weaponentity)
 {
-       W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND_CAMPINGRIFLE_FIRE2, actor);
+       W_Rifle_FireBullet(WEP_RIFLE, weaponentity, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND_CAMPINGRIFLE_FIRE2, actor);
 }
 
-.void(entity actor) rifle_bullethail_attackfunc;
+.void(entity actor, .entity weaponentity) rifle_bullethail_attackfunc;
 .WFRAME rifle_bullethail_frame;
 .float rifle_bullethail_animtime;
 .float rifle_bullethail_refire;
@@ -107,7 +111,7 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent
                PS(actor).m_switchweapon = sw;
        if(r)
        {
-               actor.rifle_bullethail_attackfunc(actor);
+               actor.rifle_bullethail_attackfunc(actor, weaponentity);
                weapon_thinkf(actor, weaponentity, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
        }
        else
@@ -116,10 +120,10 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent
        }
 }
 
-void W_Rifle_BulletHail(entity actor, .entity weaponentity, float mode, void(entity actor) AttackFunc, WFRAME fr, float animtime, float refire)
+void W_Rifle_BulletHail(entity actor, .entity weaponentity, float mode, void(entity actor, .entity weaponentity) AttackFunc, WFRAME fr, float animtime, float refire)
 {
        // if we get here, we have at least one bullet to fire
-       AttackFunc(actor);
+       AttackFunc(actor, weaponentity);
        if(mode)
        {
                // continue hail
@@ -214,7 +218,7 @@ METHOD(Rifle, wr_resetplayer, void(entity thiswep, entity actor))
 }
 METHOD(Rifle, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), SND_RELOAD);
 }
 METHOD(Rifle, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/rifle.qh b/qcsrc/common/weapons/weapon/rifle.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index a39c3958f14c1a8f357c55c9231fe739629ffa22..0e73adf2d3bd23da383640b9b6f875851ddeb150 100644 (file)
@@ -1,12 +1,13 @@
+#include "seeker.qh"
 #ifndef IMPLEMENTATION
 CLASS(Seeker, Weapon)
-/* ammotype  */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets)
-/* impulse   */ ATTRIB(Seeker, impulse, int, 8)
+/* ammotype  */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets);
+/* impulse   */ ATTRIB(Seeker, impulse, int, 8);
 /* flags     */ ATTRIB(Seeker, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Seeker, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 /* color     */ ATTRIB(Seeker, wpcolor, vector, '0.5 1 0');
 /* modelname */ ATTRIB(Seeker, mdl, string, "seeker");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Seeker, m_model, Model, MDL_SEEKER_ITEM);
 #endif
 /* crosshair */ ATTRIB(Seeker, w_crosshair, string, "gfx/crosshairseeker");
@@ -94,19 +95,24 @@ spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(this, WEP_SEEKER); }
 // ============================
 // Begin: Missile functions, these are general functions to be manipulated by other code
 // ============================
-void W_Seeker_Missile_Explode(entity this)
+void W_Seeker_Missile_Explode(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
-       RadiusDamage(this, this.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), NULL, NULL, WEP_CVAR(seeker, missile_force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), NULL, NULL, WEP_CVAR(seeker, missile_force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
 }
 
-void W_Seeker_Missile_Touch(entity this)
+void W_Seeker_Missile_Explode_think(entity this)
 {
-       PROJECTILE_TOUCH(this);
+       W_Seeker_Missile_Explode(this, NULL);
+}
+
+void W_Seeker_Missile_Touch(entity this, entity toucher)
+{
+       PROJECTILE_TOUCH(this, toucher);
 
-       W_Seeker_Missile_Explode(this);
+       W_Seeker_Missile_Explode(this, toucher);
 }
 
 void W_Seeker_Missile_Think(entity this)
@@ -120,7 +126,7 @@ void W_Seeker_Missile_Think(entity this)
        if(time > this.cnt)
        {
                this.projectiledeathtype |= HITTYPE_SPLASH;
-               W_Seeker_Missile_Explode(this);
+               W_Seeker_Missile_Explode(this, NULL);
        }
 
        spd = vlen(this.velocity);
@@ -146,7 +152,7 @@ void W_Seeker_Missile_Think(entity this)
                // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
                if(WEP_CVAR(seeker, missile_smart) && (dist > WEP_CVAR(seeker, missile_smart_mindist)))
                {
-                       // Is it a better idea (shorter distance) to trace to the target itthis?
+                       // Is it a better idea (shorter distance) to trace to the target itself?
                        if( vdist(this.origin + olddir * this.wait, <, dist))
                                traceline(this.origin, this.origin + olddir * this.wait, false, this);
                        else
@@ -178,7 +184,7 @@ void W_Seeker_Missile_Think(entity this)
                        {
                                if(this.autoswitch <= time)
                                {
-                                       W_Seeker_Missile_Explode(this);
+                                       W_Seeker_Missile_Explode(this, NULL);
                                        this.autoswitch = 0;
                                }
                        }
@@ -220,7 +226,7 @@ void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, flo
                this.health = this.health - damage;
 
        if(this.health <= 0)
-               W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode);
+               W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode_think);
 }
 
 /*
@@ -239,29 +245,27 @@ void W_Seeker_Missile_Animate(entity this)
                this.nextthink       = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
 
                if(autocvar_g_balance_seeker_missile_proxy)
-                       this.movetype    = MOVETYPE_BOUNCEMISSILE;
+                       this.move_movetype    = MOVETYPE_BOUNCEMISSILE;
                else
-                       this.movetype    = MOVETYPE_FLYMISSILE;
+                       this.move_movetype    = MOVETYPE_FLYMISSILE;
        }
 
        UpdateCSQCProjectile(this);
 }
 */
 
-void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m_target)
+void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, vector f_diff, entity m_target)
 {
-       entity missile;
-
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, missile_ammo));
 
        makevectors(actor.v_angle);
-       W_SetupShot_ProjectileSize(actor, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0);
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0);
        w_shotorg += f_diff;
        Send_Effect(EFFECT_SEEKER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
 
        //actor.detornator         = false;
 
-       missile                 = new(seeker_missile);
+       entity missile                 = new(seeker_missile);
        missile.owner           = missile.realowner = actor;
        missile.bot_dodge       = true;
        missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage);
@@ -278,6 +282,7 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m
        missile.health          = WEP_CVAR(seeker, missile_health);
        missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale);
        missile.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, missile);
        //missile.think           = W_Seeker_Missile_Animate; // csqc projectiles.
 
        if(missile.enemy != NULL)
@@ -288,8 +293,10 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m
 
        setorigin(missile, w_shotorg);
        setsize(missile, '-4 -4 -4', '4 4 4');
-       missile.movetype    = MOVETYPE_FLYMISSILE;
-       missile.flags       = FL_PROJECTILE;
+       set_movetype(missile, MOVETYPE_FLYMISSILE);
+       missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
 
        W_SetupProjVelocity_UP_PRE(missile, seeker, missile_);
@@ -304,21 +311,26 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m
 // ============================
 // Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
 // ============================
-void W_Seeker_Flac_Explode(entity this)
+void W_Seeker_Flac_Explode(entity this, entity directhitentity)
 {
        this.event_damage = func_null;
 
-       RadiusDamage(this, this.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), NULL, NULL, WEP_CVAR(seeker, flac_force), this.projectiledeathtype, other);
+       RadiusDamage(this, this.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), NULL, NULL, WEP_CVAR(seeker, flac_force), this.projectiledeathtype, directhitentity);
 
-       remove(this);
+       delete(this);
+}
+
+void W_Seeker_Flac_Touch(entity this, entity toucher)
+{
+       W_Seeker_Flac_Explode(this, toucher);
 }
 
 void W_Seeker_Flac_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_Seeker_Flac_Explode(this);
+       W_Seeker_Flac_Explode(this, trigger);
 }
 
-void W_Seeker_Fire_Flac(Weapon thiswep, entity actor)
+void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity)
 {
        entity missile;
        vector f_diff;
@@ -343,7 +355,7 @@ void W_Seeker_Fire_Flac(Weapon thiswep, entity actor)
                        f_diff = '+1.25 +3.75 0';
                        break;
        }
-       W_SetupShot_ProjectileSize(actor, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage));
        w_shotorg += f_diff;
 
        Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
@@ -352,15 +364,17 @@ void W_Seeker_Fire_Flac(Weapon thiswep, entity actor)
        missile.owner                   = missile.realowner = actor;
        missile.bot_dodge               = true;
        missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage);
-       settouch(missile, W_Seeker_Flac_Explode);
+       settouch(missile, W_Seeker_Flac_Touch);
        missile.use                     = W_Seeker_Flac_Explode_use;
        setthink(missile, adaptor_think2use_hittype_splash);
        missile.nextthink               = time + WEP_CVAR(seeker, flac_lifetime) + WEP_CVAR(seeker, flac_lifetime_rand);
        missile.solid                   = SOLID_BBOX;
-       missile.movetype                = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        missile.projectiledeathtype = WEP_SEEKER.m_id;
        missile.projectiledeathtype = WEP_SEEKER.m_id | HITTYPE_SECONDARY;
-       missile.flags                           = FL_PROJECTILE;
+       missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        missile.missile_flags       = MIF_SPLASH;
 
        // csqc projectiles
@@ -389,7 +403,7 @@ entity W_Seeker_Tagged_Info(entity isowner, entity istarget)
        return NULL;
 }
 
-void W_Seeker_Attack(entity actor)
+void W_Seeker_Attack(entity actor, .entity weaponentity)
 {
        entity tracker, closest_target;
 
@@ -409,7 +423,7 @@ void W_Seeker_Attack(entity actor)
        if((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target)))
                closest_target = NULL;
 
-       W_Seeker_Fire_Missile(WEP_SEEKER, actor, '0 0 0', closest_target);
+       W_Seeker_Fire_Missile(WEP_SEEKER, actor, weaponentity, '0 0 0', closest_target);
 }
 
 void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack
@@ -421,7 +435,7 @@ void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seek
        Weapon thiswep = WEP_SEEKER;
        if((!(this.realowner.items & IT_UNLIMITED_AMMO) && this.realowner.(thiswep.ammo_field) < WEP_CVAR(seeker, missile_ammo)) || (this.cnt <= -1) || (IS_DEAD(this.realowner)) || (PS(this.realowner).m_switchweapon != WEP_SEEKER))
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -431,22 +445,23 @@ void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seek
 
        oldenemy = own.enemy;
        own.enemy = this.enemy;
+       .entity weaponentity = this.weaponentity_fld;
 
        c = own.cnt % 4;
        switch(c)
        {
                case 0:
-                       W_Seeker_Fire_Missile(WEP_SEEKER, own, '-1.25 -3.75 0', own.enemy);
+                       W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO
                        break;
                case 1:
-                       W_Seeker_Fire_Missile(WEP_SEEKER, own, '+1.25 -3.75 0', own.enemy);
+                       W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO
                        break;
                case 2:
-                       W_Seeker_Fire_Missile(WEP_SEEKER, own, '-1.25 +3.75 0', own.enemy);
+                       W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO
                        break;
                case 3:
                default:
-                       W_Seeker_Fire_Missile(WEP_SEEKER, own, '+1.25 +3.75 0', own.enemy);
+                       W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO
                        break;
        }
 
@@ -462,7 +477,7 @@ void W_Seeker_Tracker_Think(entity this)
                if(this)
                {
                        WaypointSprite_Kill(this.tag_target.wps_tag_tracker);
-                       remove(this);
+                       delete(this);
                }
                return;
        }
@@ -478,9 +493,9 @@ void W_Seeker_Tag_Explode(entity this)
 {
        //if(other==this.realowner)
        //    return;
-       Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, other.species, this);
+       Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this);
 
-       remove(this);
+       delete(this);
 }
 
 void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -492,13 +507,13 @@ void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float d
                W_Seeker_Tag_Explode(this);
 }
 
-void W_Seeker_Tag_Touch(entity this)
+void W_Seeker_Tag_Touch(entity this, entity toucher)
 {
        vector dir;
        vector org2;
        entity e;
 
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        dir     = normalize(this.realowner.origin - this.origin);
        org2    = findbetterlocation(this.origin, 8);
@@ -506,37 +521,38 @@ void W_Seeker_Tag_Touch(entity this)
        te_knightspike(org2);
 
        this.event_damage = func_null;
-       Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, other.species, this);
+       Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
 
-       if(other.takedamage == DAMAGE_AIM && !IS_DEAD(other))
+       if(toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
        {
                // check to see if this person is already tagged by me
-               entity tag = W_Seeker_Tagged_Info(this.realowner, other);
+               entity tag = W_Seeker_Tagged_Info(this.realowner, toucher);
 
                if(tag != NULL)
                {
-                       if(other.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first
-                               WaypointSprite_Kill(other.wps_tag_tracker);
+                       if(toucher.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first
+                               WaypointSprite_Kill(toucher.wps_tag_tracker);
 
                        tag.tag_time = time;
                }
                else
                {
-                       //sprint(this.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n"));
+                       //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
                        e             = new(tag_tracker);
+                       e.weaponentity_fld = this.weaponentity_fld;
                        e.cnt         = WEP_CVAR(seeker, missile_count);
                        e.owner       = this.owner;
                        e.realowner   = this.realowner;
 
                        if(WEP_CVAR(seeker, type) == 1)
                        {
-                               e.tag_target  = other;
+                               e.tag_target  = toucher;
                                e.tag_time    = time;
                                setthink(e, W_Seeker_Tracker_Think);
                        }
                        else
                        {
-                               e.enemy     = other;
+                               e.enemy     = toucher;
                                setthink(e, W_Seeker_Vollycontroller_Think);
                        }
 
@@ -545,30 +561,30 @@ void W_Seeker_Tag_Touch(entity this)
 
                if(WEP_CVAR(seeker, type) == 1)
                {
-                       WaypointSprite_Spawn(WP_Seeker, WEP_CVAR(seeker, tag_tracker_lifetime), 0, other, '0 0 64', this.realowner, 0, other, wps_tag_tracker, true, RADARICON_TAGGED);
-                       WaypointSprite_UpdateRule(other.wps_tag_tracker, 0, SPRITERULE_DEFAULT);
+                       WaypointSprite_Spawn(WP_Seeker, WEP_CVAR(seeker, tag_tracker_lifetime), 0, toucher, '0 0 64', this.realowner, 0, toucher, wps_tag_tracker, true, RADARICON_TAGGED);
+                       WaypointSprite_UpdateRule(toucher.wps_tag_tracker, 0, SPRITERULE_DEFAULT);
                }
        }
 
-       remove(this);
+       delete(this);
        return;
 }
 
-void W_Seeker_Fire_Tag(Weapon thiswep, entity actor)
+void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
 {
-       entity missile;
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, tag_ammo));
 
-       W_SetupShot_ProjectileSize(actor, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count));
+       W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count));
 
-       missile                 = new(seeker_tag);
+       entity missile          = new(seeker_tag);
+       missile.weaponentity_fld = weaponentity;
        missile.owner           = missile.realowner = actor;
        missile.bot_dodge       = true;
        missile.bot_dodgerating = 50;
        settouch(missile, W_Seeker_Tag_Touch);
        setthink(missile, SUB_Remove);
        missile.nextthink       = time + WEP_CVAR(seeker, tag_lifetime);
-       missile.movetype        = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        missile.solid           = SOLID_BBOX;
 
        missile.takedamage       = DAMAGE_YES;
@@ -579,10 +595,12 @@ void W_Seeker_Fire_Tag(Weapon thiswep, entity actor)
        setorigin(missile, w_shotorg);
        setsize(missile, '-2 -2 -2', '2 2 2');
 
-       missile.flags       = FL_PROJECTILE;
+       missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
        //missile.missile_flags = MIF_..?;
 
-       missile.movetype    = MOVETYPE_FLY;
+       set_movetype(missile, MOVETYPE_FLY);
        W_SetupProjVelocity_PRE(missile, seeker, tag_);
        missile.angles = vectoangles(missile.velocity);
 
@@ -615,7 +633,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, missile_refire)))
             {
-                W_Seeker_Attack(actor);
+                W_Seeker_Attack(actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
             }
         }
@@ -623,7 +641,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire)))
             {
-                W_Seeker_Fire_Tag(thiswep, actor);
+                W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
             }
         }
@@ -635,7 +653,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire)))
             {
-                W_Seeker_Fire_Tag(thiswep, actor);
+                W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
             }
         }
@@ -643,7 +661,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, flac_refire)))
             {
-                W_Seeker_Fire_Flac(thiswep, actor);
+                W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
             }
         }
@@ -681,7 +699,7 @@ METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND_RELOAD);
 }
 METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/seeker.qh b/qcsrc/common/weapons/weapon/seeker.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 786671c31fd5f4d1c871fde31046e81ba0d85645..28cbe69bbe5a1a04a7a52f400127070ff5d49732 100644 (file)
@@ -1,17 +1,18 @@
+#include "shockwave.qh"
 #ifndef IMPLEMENTATION
 CLASS(Shockwave, Weapon)
-/* ammotype  */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none)
-/* impulse   */ ATTRIB(Shockwave, impulse, int, 2)
-/* flags     */ ATTRIB(Shockwave, spawnflags, int, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_FLAG_MUTATORBLOCKED);
+/* ammotype  */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none);
+/* impulse   */ ATTRIB(Shockwave, impulse, int, 2);
+/* flags     */ ATTRIB(Shockwave, spawnflags, int, WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_TYPE_MELEE_SEC);
 /* rating    */ ATTRIB(Shockwave, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW);
 /* color     */ ATTRIB(Shockwave, wpcolor, vector, '0.5 0.25 0');
 /* modelname */ ATTRIB(Shockwave, mdl, string, "shotgun");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Shockwave, m_model, Model, MDL_SHOCKWAVE_ITEM);
 #endif
 /* crosshair */ ATTRIB(Shockwave, w_crosshair, string, "gfx/crosshairshotgun");
 /* crosshair */ ATTRIB(Shockwave, w_crosshair_size, float, 0.7);
-/* wepimg    */ ATTRIB(Shockwave, model2, string, "weaponshotgun");
+/* wepimg    */ ATTRIB(Shockwave, model2, string, "weaponshockwave");
 /* refname   */ ATTRIB(Shockwave, netname, string, "shockwave");
 /* wepname   */ ATTRIB(Shockwave, m_name, string, _("Shockwave"));
 
@@ -122,7 +123,7 @@ void W_Shockwave_Melee_Think(entity this)
        // check to see if we can still continue, otherwise give up now
        if(IS_DEAD(this.realowner) && WEP_CVAR(shockwave, melee_no_doubleslap))
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -214,7 +215,7 @@ void W_Shockwave_Melee_Think(entity this)
                        }
                        else
                        {
-                               remove(this);
+                               delete(this);
                                return;
                        }
                }
@@ -223,7 +224,7 @@ void W_Shockwave_Melee_Think(entity this)
        if(time >= this.cnt + meleetime)
        {
                // melee is finished
-               remove(this);
+               delete(this);
                return;
        }
        else
@@ -243,7 +244,7 @@ void W_Shockwave_Melee(Weapon thiswep, entity actor, .entity weaponentity, int f
        meleetemp.owner = meleetemp.realowner = actor;
        setthink(meleetemp, W_Shockwave_Melee_Think);
        meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor(actor);
-       W_SetupShot_Range(actor, true, 0, SND_Null, 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range));
+       W_SetupShot_Range(actor, weaponentity, true, 0, SND_Null, 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range));
 }
 
 // SHOCKWAVE ATTACK MODE
@@ -355,7 +356,7 @@ void W_Shockwave_Send(entity actor)
        WriteByte(MSG_BROADCAST, etof(actor));
 }
 
-void W_Shockwave_Attack(entity actor)
+void W_Shockwave_Attack(entity actor, .entity weaponentity)
 {
        // declarations
        float multiplier, multiplier_from_accuracy, multiplier_from_distance;
@@ -366,7 +367,7 @@ void W_Shockwave_Attack(entity actor)
        float i, queue = 0;
 
        // set up the shot direction
-       W_SetupShot(actor, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage));
+       W_SetupShot(actor, weaponentity, true, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage));
        vector attack_endpos = (w_shotorg + (w_shotdir * WEP_CVAR(shockwave, blast_distance)));
        WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, actor);
        vector attack_hitpos = trace_endpos;
@@ -397,6 +398,22 @@ void W_Shockwave_Attack(entity actor)
                false
        );
 
+       float lag = ANTILAG_LATENCY(actor);
+       if(lag < 0.001)
+               lag = 0;
+       if (!IS_REAL_CLIENT(actor))
+               lag = 0;
+       if(autocvar_g_antilag == 0 || actor.cvar_cl_noantilag)
+               lag = 0; // only do hitscan, but no antilag
+       if(lag)
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag));
+               IL_EACH(g_monsters, it != actor,
+               {
+                       antilag_takeback(it, it, time - lag);
+               });
+       }
+
        while(head)
        {
                if(head.takedamage)
@@ -570,7 +587,7 @@ void W_Shockwave_Attack(entity actor)
                        vector nearest_on_line = (w_shotorg + a * w_shotdir);
                        vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line);
 
-                       if((vlen(head.WarpZone_findradius_dist) <= WEP_CVAR(shockwave, blast_distance))
+                       if((vdist(head.WarpZone_findradius_dist, <=, WEP_CVAR(shockwave, blast_distance)))
                                && (W_Shockwave_Attack_IsVisible(actor, head, nearest_on_line, w_shotorg, attack_endpos)))
                        {
                                // calculate importance of distance and accuracy for this attack
@@ -651,11 +668,8 @@ void W_Shockwave_Attack(entity actor)
                        final_force
                );
 
-               if(accuracy_isgooddamage(actor.realowner, head))
-               {
-                       LOG_INFO("wtf\n");
-                       accuracy_add(actor.realowner, WEP_SHOCKWAVE.m_id, 0, final_damage);
-               }
+               if(accuracy_isgooddamage(actor, head))
+                       accuracy_add(actor, WEP_SHOCKWAVE.m_id, 0, final_damage);
 
                #ifdef DEBUG_SHOCKWAVE
                LOG_INFO(sprintf(
@@ -670,6 +684,15 @@ void W_Shockwave_Attack(entity actor)
                shockwave_hit_force[i-1] = '0 0 0';
                shockwave_hit_damage[i-1] = 0;
        }
+
+       if(lag)
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it)));
+               IL_EACH(g_monsters, it != actor,
+               {
+                       antilag_restore(it, it);
+               });
+       }
 }
 
 METHOD(Shockwave, wr_aim, void(entity thiswep, entity actor))
@@ -687,7 +710,7 @@ METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponent
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(shockwave, blast_animtime)))
             {
-                W_Shockwave_Attack(actor);
+                W_Shockwave_Attack(actor, weaponentity);
                 actor.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor(actor);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready);
             }
@@ -696,7 +719,6 @@ METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponent
     else if(fire & 2)
     {
         //if(actor.clip_load >= 0) // we are not currently reloading
-        if(!actor.crouch) // no crouchmelee please
         if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(shockwave, melee_refire)))
         {
             // attempt forcing playback of the anim by switching to another anim (that we never play) here...
@@ -735,7 +757,7 @@ void Draw_Shockwave(entity this)
 {
        // fading/removal control
        float a = bound(0, (SW_MAXALPHA - ((time - this.sw_time) / SW_FADETIME)), SW_MAXALPHA);
-       if(a < ALPHA_MIN_VISIBLE) { remove(this); }
+       if(a < ALPHA_MIN_VISIBLE) { delete(this); }
 
        // WEAPONTODO: save this only once when creating the entity
        vector sw_color = entcs_GetColor(this.sv_entnum - 1); // GetTeamRGB(entcs_GetTeam(this.sv_entnum));
@@ -840,6 +862,7 @@ void Net_ReadShockwaveParticle()
        entity shockwave;
        shockwave = spawn();
        shockwave.draw = Draw_Shockwave;
+       IL_PUSH(g_drawables, shockwave);
 
        shockwave.sw_shotorg_x = ReadCoord(); shockwave.sw_shotorg_y = ReadCoord(); shockwave.sw_shotorg_z = ReadCoord();
        shockwave.sw_shotdir_x = ReadCoord(); shockwave.sw_shotdir_y = ReadCoord(); shockwave.sw_shotdir_z = ReadCoord();
diff --git a/qcsrc/common/weapons/weapon/shockwave.qh b/qcsrc/common/weapons/weapon/shockwave.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 34d2e462c724008eda9063150624d9547b3248dc..d45cf31cb3bf957ac556d8356d152ead0a2bf382 100644 (file)
@@ -1,12 +1,13 @@
+#include "shotgun.qh"
 #ifndef IMPLEMENTATION
 CLASS(Shotgun, Weapon)
-/* ammotype  */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells)
-/* impulse   */ ATTRIB(Shotgun, impulse, int, 2)
-/* flags     */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
+/* ammotype  */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells);
+/* impulse   */ ATTRIB(Shotgun, impulse, int, 2);
+/* flags     */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_TYPE_MELEE_SEC);
 /* rating    */ ATTRIB(Shotgun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW);
 /* color     */ ATTRIB(Shotgun, wpcolor, vector, '0.5 0.25 0');
 /* modelname */ ATTRIB(Shotgun, mdl, string, "shotgun");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Shotgun, m_model, Model, MDL_SHOTGUN_ITEM);
 #endif
 /* crosshair */ ATTRIB(Shotgun, w_crosshair, string, "gfx/crosshairshotgun");
@@ -58,31 +59,31 @@ REGISTER_WEAPON(SHOTGUN, shotgun, NEW(Shotgun));
 #ifdef SVQC
 spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); }
 
-void W_Shotgun_Attack(Weapon thiswep, entity actor, float isprimary)
+void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary)
 {
-       float   sc;
-       entity flash;
-
        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(shotgun, ammo));
 
-       W_SetupShot(actor, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets));
-       for(sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1)
+       W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets));
+       for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1)
                fireBullet(actor, w_shotorg, w_shotdir, WEP_CVAR_PRI(shotgun, spread), WEP_CVAR_PRI(shotgun, solidpenetration), WEP_CVAR_PRI(shotgun, damage), WEP_CVAR_PRI(shotgun, force), WEP_SHOTGUN.m_id, 0);
 
        Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo));
 
        // casing code
        if(autocvar_g_casings >= 1)
-               for(sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1)
-                       SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, actor);
+       {
+               makevectors(actor.v_angle); // for some reason, this is lost
+               //for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1)
+                       SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, actor, weaponentity);
+       }
 
        // muzzle flash for 1st person view
-       flash = spawn();
+       entity flash = spawn();
        setmodel(flash, MDL_SHOTGUN_MUZZLEFLASH); // precision set below
        setthink(flash, SUB_Remove);
        flash.nextthink = time + 0.06;
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
-       W_AttachToShotorg(actor, flash, '5 0 0');
+       W_AttachToShotorg(actor, weaponentity, flash, '5 0 0');
 }
 
 .float swing_prev;
@@ -110,7 +111,7 @@ void W_Shotgun_Melee_Think(entity this)
        // check to see if we can still continue, otherwise give up now
        if(IS_DEAD(this.realowner) && WEP_CVAR_SEC(shotgun, melee_no_doubleslap))
        {
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -163,7 +164,7 @@ void W_Shotgun_Melee_Think(entity this)
                        }
                        else
                        {
-                               remove(this);
+                               delete(this);
                                return;
                        }
                }
@@ -172,7 +173,7 @@ void W_Shotgun_Melee_Think(entity this)
        if(time >= this.cnt + meleetime)
        {
                // melee is finished
-               remove(this);
+               delete(this);
                return;
        }
        else
@@ -192,7 +193,7 @@ void W_Shotgun_Attack2(Weapon thiswep, entity actor, .entity weaponentity, int f
        meleetemp.realowner = actor;
        setthink(meleetemp, W_Shotgun_Melee_Think);
        meleetemp.nextthink = time + WEP_CVAR_SEC(shotgun, melee_delay) * W_WeaponRateFactor(actor);
-       W_SetupShot_Range(actor, true, 0, SND_Null, 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range));
+       W_SetupShot_Range(actor, weaponentity, true, 0, SND_Null, 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range));
 }
 
 // alternate secondary weapon frames
@@ -207,7 +208,7 @@ void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, .entity weaponentity
        }
 
        sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound
-       W_Shotgun_Attack(WEP_SHOTGUN, actor, true); // actually is secondary, but we trick the last shot into playing full reload sound
+       W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true); // actually is secondary, but we trick the last shot into playing full reload sound
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
 }
 void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire)
@@ -220,11 +221,11 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity
                return;
        }
 
-       W_Shotgun_Attack(WEP_SHOTGUN, actor, false);
+       W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false);
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
 }
 
-.float shotgun_primarytime;
+.float shotgun_primarytime[MAX_WEAPONSLOTS];
 
 METHOD(Shotgun, wr_aim, void(entity thiswep, entity actor))
 {
@@ -246,31 +247,32 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit
     {
         if(fire & 1)
         {
-            if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
+               int slot = weaponslot(weaponentity);
+            if(time >= actor.shotgun_primarytime[slot]) // handle refire separately so the secondary can be fired straight after a primary
             {
                 if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime)))
                 {
-                    W_Shotgun_Attack(thiswep, actor, true);
-                    actor.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor);
+                    W_Shotgun_Attack(thiswep, actor, weaponentity, true);
+                    actor.shotgun_primarytime[slot] = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor);
                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
                 }
             }
         }
         else if((fire & 2) && WEP_CVAR(shotgun, secondary) == 2)
         {
-            if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
+               int slot = weaponslot(weaponentity);
+            if(time >= actor.shotgun_primarytime[slot]) // handle refire separately so the secondary can be fired straight after a primary
             {
                 if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime)))
                 {
-                    W_Shotgun_Attack(thiswep, actor, false);
-                    actor.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(actor);
+                    W_Shotgun_Attack(thiswep, actor, weaponentity, false);
+                    actor.shotgun_primarytime[slot] = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(actor);
                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
                 }
             }
         }
     }
     if(actor.clip_load >= 0) // we are not currently reloading
-    if(!actor.crouch) // no crouchmelee please
     if(WEP_CVAR(shotgun, secondary) == 1)
     if(((fire & 1) && actor.(thiswep.ammo_field) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2))
     if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(shotgun, refire)))
@@ -281,7 +283,7 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit
 }
 METHOD(Shotgun, wr_setup, void(entity thiswep, entity actor))
 {
-    actor.ammo_field = ammo_none;
+    actor.ammo_field = ammo_shells;
 }
 METHOD(Shotgun, wr_checkammo1, bool(entity thiswep, entity actor))
 {
@@ -308,7 +310,7 @@ METHOD(Shotgun, wr_checkammo2, bool(entity thiswep, entity actor))
 }
 METHOD(Shotgun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, WEP_CVAR_PRI(shotgun, ammo), SND_RELOAD); // WEAPONTODO
+    W_Reload(actor, weaponentity, WEP_CVAR_PRI(shotgun, ammo), SND_RELOAD); // WEAPONTODO
 }
 METHOD(Shotgun, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/shotgun.qh b/qcsrc/common/weapons/weapon/shotgun.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c0e0ac89ba89814e3fd8fab83b8e0f8e30a00a41..e3e1f5228dc0d0839efeb1a4f9015f8d04f38ce1 100644 (file)
@@ -1,11 +1,12 @@
+#include "tuba.qh"
 #ifndef IMPLEMENTATION
 CLASS(Tuba, Weapon)
-/* impulse   */ ATTRIB(Tuba, impulse, int, 1)
+/* impulse   */ ATTRIB(Tuba, impulse, int, 1);
 /* flags     */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH);
 /* rating    */ ATTRIB(Tuba, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 /* color     */ ATTRIB(Tuba, wpcolor, vector, '0 1 0');
 /* modelname */ ATTRIB(Tuba, mdl, string, "tuba");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Tuba, m_model, Model, MDL_TUBA_ITEM);
 #endif
 /* crosshair */ ATTRIB(Tuba, w_crosshair, string, "gfx/crosshairtuba");
@@ -179,7 +180,7 @@ void W_Tuba_NoteOff(entity this)
                        }
                }
        }
-       remove(this);
+       delete(this);
 }
 
 int W_Tuba_GetNote(entity pl, int hittype)
@@ -309,12 +310,12 @@ void W_Tuba_NoteThink(entity this)
        });
 }
 
-void W_Tuba_NoteOn(entity actor, float hittype)
+void W_Tuba_NoteOn(entity actor, .entity weaponentity, float hittype)
 {
        vector o;
        float n;
 
-       W_SetupShot(actor, false, 2, SND_Null, 0, WEP_CVAR(tuba, damage));
+       W_SetupShot(actor, weaponentity, false, 2, SND_Null, 0, WEP_CVAR(tuba, damage));
 
        n = W_Tuba_GetNote(actor, hittype);
 
@@ -377,13 +378,13 @@ METHOD(Tuba, wr_think, void(Tuba this, entity actor, .entity weaponentity, int f
        if (fire & 1)
        if (weapon_prepareattack(this, actor, weaponentity, false, WEP_CVAR(tuba, refire)))
        {
-               W_Tuba_NoteOn(actor, 0);
+               W_Tuba_NoteOn(actor, weaponentity, 0);
                weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
        }
        if (fire & 2)
        if (weapon_prepareattack(this, actor, weaponentity, true, WEP_CVAR(tuba, refire)))
        {
-               W_Tuba_NoteOn(actor, HITTYPE_SECONDARY);
+               W_Tuba_NoteOn(actor, weaponentity, HITTYPE_SECONDARY);
                weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
        }
        if (actor.tuba_note)
@@ -395,10 +396,12 @@ METHOD(Tuba, wr_think, void(Tuba this, entity actor, .entity weaponentity, int f
        }
 }
 
+void tuba_instrument_send(entity this, int instr);
 METHOD(Tuba, wr_setup, void(Tuba this, entity actor))
 {
        actor.ammo_field = ammo_none;
        actor.tuba_instrument = 0;
+       tuba_instrument_send(actor, actor.tuba_instrument);
 }
 #endif
 
@@ -411,6 +414,7 @@ NET_HANDLE(tuba_instrument, bool)
        string s = (i == 0) ? "tuba" :
                   (i == 1) ? "akordeon" :
                              "kleinbottle" ;
+    viewmodel.tuba_instrument = i;
        CL_WeaponEntity_SetModel(viewmodel, s, true);
 }
 #endif
@@ -418,6 +422,8 @@ NET_HANDLE(tuba_instrument, bool)
 void tuba_instrument_send(entity this, int instr)
 {
        msg_entity = this;
+       if (!IS_REAL_CLIENT(this))
+               return;
        int chan = MSG_ONE;
        WriteHeader(chan, tuba_instrument);
        WriteByte(chan, instr);
@@ -448,7 +454,7 @@ METHOD(Tuba, wr_reload, void(Tuba this, entity actor, .entity weaponentity))
                                break;
                }
                tuba_instrument_send(actor, actor.tuba_instrument);
-               W_SetupShot(actor, false, 0, SND_Null, 0, 0);
+               W_SetupShot(actor, weaponentity, false, 0, SND_Null, 0, 0);
                Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1);
                actor.(weaponentity).state = WS_INUSE;
                weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, 0.5, w_ready);
@@ -481,6 +487,15 @@ METHOD(Tuba, wr_killmessage, Notification(Tuba this))
                return WEAPON_TUBA_MURDER;
 }
 
+#elif defined(CSQC)
+
+METHOD(Tuba, wr_viewmodel, string(Tuba this, entity wep))
+{
+       return (wep.tuba_instrument == 0) ? "tuba" :
+              (wep.tuba_instrument == 1) ? "akordeon" :
+                                           "kleinbottle";
+}
+
 #endif
 
 #ifdef CSQC
@@ -491,13 +506,6 @@ const int TUBA_MIN = -18;
 const int TUBA_MAX = 27;
 const int TUBA_INSTRUMENTS = 3;
 
-entityclass(Tuba);
-class(Tuba) .int note;
-class(Tuba) .bool tuba_attenuate;
-class(Tuba) .float tuba_volume;
-class(Tuba) .float tuba_volume_initial;
-class(Tuba) .int tuba_instrument;
-
 int Tuba_PitchStep;
 
 void tubasound(entity e, bool restart)
@@ -563,9 +571,9 @@ void Ent_TubaNote_Think(entity this)
                sound(this, CH_TUBA_SINGLE, SND_Null, 0, 0);
                if (this.enemy) {
                        sound(this.enemy, CH_TUBA_SINGLE, SND_Null, 0, 0);
-                       remove(this.enemy);
+                       delete(this.enemy);
                }
-               remove(this);
+               delete(this);
        } else {
                tubasound(this, 0);
        }
@@ -643,7 +651,7 @@ PRECACHE(Tuba)
        Tuba_PitchStep = autocvar_g_balance_tuba_pitchstep;
        if (Tuba_PitchStep) {
                if (!checkextension("DP_SND_SOUND7_WIP2") && !checkextension("DP_SND_SOUND7")) {
-                       LOG_WARNING("requested pitch shifting, but not supported by this engine build");
+                       LOG_WARN("requested pitch shifting, but not supported by this engine build");
                        Tuba_PitchStep = 0;
                }
        }
diff --git a/qcsrc/common/weapons/weapon/tuba.qh b/qcsrc/common/weapons/weapon/tuba.qh
new file mode 100644 (file)
index 0000000..ce6b6ed
--- /dev/null
@@ -0,0 +1,10 @@
+#pragma once
+
+#ifdef CSQC
+entityclass(Tuba);
+class(Tuba) .int note;
+class(Tuba) .bool tuba_attenuate;
+class(Tuba) .float tuba_volume;
+class(Tuba) .float tuba_volume_initial;
+class(Tuba) .int tuba_instrument;
+#endif
index 8b061b72a5016013e39b9936de8ce66b6f7a8564..91042b613b7a425c8fc354a395a2392108a4697e 100644 (file)
@@ -1,12 +1,13 @@
+#include "vaporizer.qh"
 #ifndef IMPLEMENTATION
 CLASS(Vaporizer, Weapon)
-/* ammotype  */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells)
-/* impulse   */ ATTRIB(Vaporizer, impulse, int, 7)
+/* ammotype  */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells);
+/* impulse   */ ATTRIB(Vaporizer, impulse, int, 7);
 /* flags     */ ATTRIB(Vaporizer, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN);
 /* rating    */ ATTRIB(Vaporizer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 /* color     */ ATTRIB(Vaporizer, wpcolor, vector, '0.5 1 1');
 /* modelname */ ATTRIB(Vaporizer, mdl, string, "minstanex");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Vaporizer, m_model, Model, MDL_VAPORIZER_ITEM);
 #endif
 /* crosshair */ ATTRIB(Vaporizer, w_crosshair, string, "gfx/crosshairminstanex");
@@ -111,7 +112,7 @@ void VaporizerBeam_Draw(entity this)
        //entity e = CSQCModel_server2csqc(this.sv_entnum - 1);
        //if (e == NULL)
        //{
-               rgb = colormapPaletteColor(stof(getplayerkeyvalue(this.sv_entnum - 1, "colors")) & 0x0F, true);
+               rgb = colormapPaletteColor(entcs_GetClientColors(this.sv_entnum - 1) & 0x0F, true);
                //rgb = '1 1 1';
        //}
        //else
@@ -140,6 +141,7 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
        setthink(this, SUB_Remove);
        this.nextthink = time + bound(0, autocvar_cl_vaporizerbeam_lifetime, 10);
        this.draw = VaporizerBeam_Draw;
+       if (isNew) IL_PUSH(g_drawables, this);
        this.drawmask = MASK_NORMAL;
 
        this.vorg1_x = ReadCoord(); this.vorg1_y = ReadCoord(); this.vorg1_z = ReadCoord();
@@ -157,7 +159,7 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
                WarpZone_TrailParticles(NULL, particleeffectnum(((this.cnt) ? EFFECT_VAPORIZER_HIT(this.team) : EFFECT_VAPORIZER(this.team))), this.vorg1, this.vorg2);
                this.draw = func_null;
                this.drawmask = MASK_NORMAL;
-               remove(this);
+               delete(this);
        }
 
        return true;
@@ -176,15 +178,15 @@ void W_RocketMinsta_Explosion(entity actor, vector loc)
        dmgent.owner = dmgent.realowner = actor;
        setorigin(dmgent, loc);
        RadiusDamage (dmgent, actor, autocvar_g_rm_damage, autocvar_g_rm_edgedamage, autocvar_g_rm_radius, NULL, NULL, autocvar_g_rm_force, WEP_DEVASTATOR.m_id | HITTYPE_SPLASH, other);
-       remove(dmgent);
+       delete(dmgent);
 }
 
-void W_Vaporizer_Attack(Weapon thiswep, entity actor)
+void W_Vaporizer_Attack(Weapon thiswep, entity actor, .entity weaponentity)
 {
        bool flying = IsFlying(actor); // do this BEFORE to make the trace values from FireRailgunBullet last
        float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage) > 0) ? WEP_CVAR_PRI(vaporizer, damage) : 10000);
 
-       W_SetupShot(actor, true, 0, SND_Null, CH_WEAPON_A, vaporizer_damage);
+       W_SetupShot(actor, weaponentity, true, 0, SND_Null, CH_WEAPON_A, vaporizer_damage);
        // handle sound separately so we can change the volume
        // added bonus: no longer plays the strength sound (strength gives no bonus to instakill anyway)
        sound (actor, CH_WEAPON_A, SND_MINSTANEXFIRE, VOL_BASE * 0.8, ATTEN_NORM);
@@ -213,35 +215,35 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor)
        W_DecreaseAmmo(thiswep, actor, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)));
 }
 
-void W_RocketMinsta_Laser_Explode (entity this)
+void W_RocketMinsta_Laser_Explode (entity this, entity directhitentity)
 {
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
-       RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, other);
-       remove(this);
+       RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, directhitentity);
+       delete(this);
 }
 
 void W_RocketMinsta_Laser_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_RocketMinsta_Laser_Explode(this);
+       W_RocketMinsta_Laser_Explode(this, trigger); // we probably don't want trigger used here, but this matches closest to old behaviour
 }
 
-void W_RocketMinsta_Laser_Touch (entity this)
+void W_RocketMinsta_Laser_Touch(entity this, entity toucher)
 {
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
        //W_RocketMinsta_Laser_Explode ();
-       RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, other);
-       remove(this);
+       RadiusDamage(this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, toucher);
+       delete(this);
 }
 
-void W_RocketMinsta_Attack2(entity actor)
+void W_RocketMinsta_Attack2(entity actor, .entity weaponentity)
 {
        makevectors(actor.v_angle);
 
@@ -253,7 +255,7 @@ void W_RocketMinsta_Attack2(entity actor)
 
        Weapon w = PS(actor).m_weapon;
        PS(actor).m_weapon = WEP_ELECTRO;
-       W_SetupShot_ProjectileSize (actor, '0 0 -3', '0 0 -3', false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, autocvar_g_rm_laser_damage);
+       W_SetupShot_ProjectileSize (actor, weaponentity, '0 0 -3', '0 0 -3', false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, autocvar_g_rm_laser_damage);
        PS(actor).m_weapon = w;
 
        Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
@@ -277,7 +279,7 @@ void W_RocketMinsta_Attack2(entity actor)
 
         //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed);
 
-        proj.movetype = MOVETYPE_BOUNCEMISSILE;
+        set_movetype(proj, MOVETYPE_BOUNCEMISSILE);
         //W_SETUPPROJECTILEVELOCITY(proj, g_balance_minstanex_laser);
                proj.velocity = (w_shotdir + (((counter + 0.5) / total) * 2 - 1) * v_right * (spread * (rndspread ? random() : 1))) * cvar("g_rm_laser_speed");
                proj.velocity_z = proj.velocity_z + cvar("g_rm_laser_zspread") * (random() - 0.5);
@@ -286,6 +288,8 @@ void W_RocketMinsta_Attack2(entity actor)
         settouch(proj, W_RocketMinsta_Laser_Touch);
         setsize(proj, '0 0 -3', '0 0 -3');
         proj.flags = FL_PROJECTILE;
+        IL_PUSH(g_projectiles, proj);
+        IL_PUSH(g_bot_dodge, proj);
         proj.missile_flags = MIF_SPLASH;
 
         CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true);
@@ -295,7 +299,7 @@ void W_RocketMinsta_Attack2(entity actor)
     }
 }
 
-void W_RocketMinsta_Attack3 (entity actor)
+void W_RocketMinsta_Attack3 (entity actor, .entity weaponentity)
 {
        makevectors(actor.v_angle);
 
@@ -305,7 +309,7 @@ void W_RocketMinsta_Attack3 (entity actor)
 
        Weapon w = PS(actor).m_weapon;
        PS(actor).m_weapon = WEP_ELECTRO;
-       W_SetupShot_ProjectileSize (actor, '0 0 -3', '0 0 -3', false, 2, SND_ELECTRO_FIRE2, CH_WEAPON_A, autocvar_g_rm_laser_damage);
+       W_SetupShot_ProjectileSize (actor, weaponentity, '0 0 -3', '0 0 -3', false, 2, SND_ELECTRO_FIRE2, CH_WEAPON_A, autocvar_g_rm_laser_damage);
        PS(actor).m_weapon = w;
 
        Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
@@ -329,13 +333,15 @@ void W_RocketMinsta_Attack3 (entity actor)
 
         //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed);
 
-        proj.movetype = MOVETYPE_BOUNCEMISSILE;
+        set_movetype(proj, MOVETYPE_BOUNCEMISSILE);
                proj.velocity = w_shotdir * autocvar_g_rm_laser_speed;
                proj.velocity = W_CalculateProjectileVelocity(actor, actor.velocity, proj.velocity, true);
         proj.angles = vectoangles(proj.velocity);
         settouch(proj, W_RocketMinsta_Laser_Touch);
         setsize(proj, '0 0 -3', '0 0 -3');
         proj.flags = FL_PROJECTILE;
+        IL_PUSH(g_projectiles, proj);
+        IL_PUSH(g_bot_dodge, proj);
         proj.missile_flags = MIF_SPLASH;
 
         CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true);
@@ -365,7 +371,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire)))
         {
-            W_Vaporizer_Attack(thiswep, actor);
+            W_Vaporizer_Attack(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready);
         }
     }
@@ -381,13 +387,13 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent
                 actor.jump_interval = time + autocvar_g_rm_laser_refire;
                 actor.jump_interval2 = time + autocvar_g_rm_laser_rapid_delay;
                 damage_goodhits = 0;
-                W_RocketMinsta_Attack2(actor);
+                W_RocketMinsta_Attack2(actor, weaponentity);
             }
             else if(rapid && actor.jump_interval2 <= time && actor.held_down)
             {
                 actor.jump_interval2 = time + autocvar_g_rm_laser_rapid_refire;
                 damage_goodhits = 0;
-                W_RocketMinsta_Attack3(actor);
+                W_RocketMinsta_Attack3(actor, weaponentity);
                 //weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_rm_laser_rapid_animtime, w_ready);
             }
         }
@@ -406,6 +412,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent
             PS(actor).m_weapon = WEP_BLASTER;
             W_Blaster_Attack(
                 actor,
+                weaponentity,
                 WEP_BLASTER.m_id | HITTYPE_SECONDARY,
                 WEP_CVAR_SEC(vaporizer, shotangle),
                 WEP_CVAR_SEC(vaporizer, damage),
@@ -459,7 +466,7 @@ METHOD(Vaporizer, wr_reload, void(entity thiswep, entity actor, .entity weaponen
     else
         used_ammo = vaporizer_ammo;
 
-    W_Reload(actor, used_ammo, SND_RELOAD);
+    W_Reload(actor, weaponentity, used_ammo, SND_RELOAD);
 }
 METHOD(Vaporizer, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/vaporizer.qh b/qcsrc/common/weapons/weapon/vaporizer.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 640cd2c402f325769a8cfb86c16f14b4e8a75429..bf3ce8185f2f33168e3da300b5722447151d14c3 100644 (file)
@@ -1,12 +1,13 @@
+#include "vortex.qh"
 #ifndef IMPLEMENTATION
 CLASS(Vortex, Weapon)
-/* ammotype  */ ATTRIB(Vortex, ammo_field, .int, ammo_cells)
-/* impulse   */ ATTRIB(Vortex, impulse, int, 7)
+/* ammotype  */ ATTRIB(Vortex, ammo_field, .int, ammo_cells);
+/* impulse   */ ATTRIB(Vortex, impulse, int, 7);
 /* flags     */ ATTRIB(Vortex, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 /* rating    */ ATTRIB(Vortex, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 /* color     */ ATTRIB(Vortex, wpcolor, vector, '0.5 1 1');
 /* modelname */ ATTRIB(Vortex, mdl, string, "nex");
-#ifndef MENUQC
+#ifdef GAMEQC
 /* model     */ ATTRIB(Vortex, m_model, Model, MDL_VORTEX_ITEM);
 #endif
 /* crosshair */ ATTRIB(Vortex, w_crosshair, string, "gfx/crosshairnex");
@@ -68,7 +69,7 @@ REGISTER_WEAPON(VORTEX, vortex, NEW(Vortex));
 REGISTER_STAT(WEP_CVAR_vortex_charge, bool, WEP_CVAR(vortex, charge))
 REGISTER_STAT(WEP_CVAR_vortex_charge_animlimit, float, WEP_CVAR(vortex, charge_animlimit))
 
-#if defined(CSQC)
+#if defined(GAMEQC)
 float autocvar_g_weapon_charge_colormod_red_full;
 float autocvar_g_weapon_charge_colormod_red_half;
 float autocvar_g_weapon_charge_colormod_green_full;
@@ -77,11 +78,11 @@ float autocvar_g_weapon_charge_colormod_blue_half;
 float autocvar_g_weapon_charge_colormod_green_half;
 float autocvar_g_weapon_charge_colormod_hdrmultiplier;
 
-METHOD(Vortex, wr_glow, vector(Vortex this))
+METHOD(Vortex, wr_glow, vector(Vortex this, entity actor))
 {
-       if (!STAT(WEP_CVAR_vortex_charge)) return '0 0 0';
-       float charge = STAT(VORTEX_CHARGE);
-       float animlimit = STAT(WEP_CVAR_vortex_charge_animlimit);
+       if (!STAT(WEP_CVAR_vortex_charge, actor)) return '0 0 0';
+       float charge = STAT(VORTEX_CHARGE, actor);
+       float animlimit = STAT(WEP_CVAR_vortex_charge_animlimit, actor);
        vector g;
        g.x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, charge / animlimit);
        g.y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, charge / animlimit);
@@ -157,7 +158,7 @@ MUTATOR_HOOKFUNCTION(vortex_charge, GetPressedKeys)
        }
 }
 
-void W_Vortex_Attack(Weapon thiswep, entity actor, float issecondary)
+void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float issecondary)
 {
        float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge;
 
@@ -184,7 +185,7 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, float issecondary)
        mydmg *= charge;
        myforce *= charge;
 
-       W_SetupShot(actor, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg);
+       W_SetupShot(actor, weaponentity, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg);
        if(charge > WEP_CVAR(vortex, charge_animlimit) && WEP_CVAR(vortex, charge_animlimit)) // if the Vortex is overcharged, we play an extra sound
        {
                sound(actor, CH_WEAPON_B, SND_NEXCHARGE, VOL_BASE * (charge - 0.5 * WEP_CVAR(vortex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(vortex, charge_animlimit)), ATTN_NORM);
@@ -243,7 +244,7 @@ METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity
         {
             if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vortex, refire)))
             {
-                W_Vortex_Attack(thiswep, actor, 0);
+                W_Vortex_Attack(thiswep, actor, weaponentity, 0);
                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready);
             }
         }
@@ -315,7 +316,7 @@ METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity
             {
                 if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(vortex, refire)))
                 {
-                    W_Vortex_Attack(thiswep, actor, 1);
+                    W_Vortex_Attack(thiswep, actor, weaponentity, 1);
                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready);
                 }
             }
@@ -358,7 +359,7 @@ METHOD(Vortex, wr_resetplayer, void(entity thiswep, entity actor))
 }
 METHOD(Vortex, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
 {
-    W_Reload(actor, min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), SND_RELOAD);
+    W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), SND_RELOAD);
 }
 METHOD(Vortex, wr_suicidemessage, Notification(entity thiswep))
 {
diff --git a/qcsrc/common/weapons/weapon/vortex.qh b/qcsrc/common/weapons/weapon/vortex.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index f424d1a5de4dcfec5323851cabb41a712eed6adb..29d797585efbe10117995db27925b739109abae6 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef CSPROGSDEFS_H
-#define CSPROGSDEFS_H
+#pragma once
 
 #pragma noref 1
 
@@ -44,5 +43,3 @@
 #define use use1
 .void(entity this, entity actor, entity trigger) use;
 #define touch move_touch
-
-#endif
index d279ced478d0c48df33ffc0cc8e43837687c73ee..b49bb6f57ad72e7ab0ff6bed1b20925d6e26362a 100644 (file)
@@ -18,6 +18,7 @@
 bool CSQC_InputEvent(int eventtype, int x, int y);
 
 void CSQC_UpdateView(int width, int height);
+// catch commands registered with registercommand
 bool CSQC_ConsoleCommand(string cmd);
 bool CSQC_Parse_TempEntity();
 bool CSQC_Parse_StuffCmd(string msg);
@@ -54,6 +55,21 @@ float sound_starttime;
 
 # SVQC
 
+Main loop:
+* SV_Physics()
+    * StartFrame()
+    * if (force_retouch)
+        * foreach entity:
+            * .touch()
+    * foreach client:
+        * PlayerPreThink()
+        * .think()
+        * PlayerPostThink()
+    * foreach nonclient:
+        * .think()
+    * EndFrame()
+
+
 ```
 
 .entity clientcamera;
@@ -198,6 +214,10 @@ void SV_PlayerPhysics();
 //   self
 void SV_ParseClientCommand(string cmd);
 
+// qcstatus server field
+string worldstatus;
+.string clientstatus;
+
 ```
 
 # MENUQC
@@ -229,3 +249,85 @@ void URI_Get_Callback(int id, int status, string data);
 void GameCommand(string cmd);
 
 ```
+
+# Misc
+
+## Trace
+
+### tracebox
+
+    void tracebox(vector v1, vector min, vector max, vector v2, int tryents, entity ignoreentity);
+
+attempt to move an object from v1 to v2 of given size
+
+tryents:
+ * MOVE_NORMAL (0)
+ * MOVE_NOMONSTERS (1): ignore monsters
+ * MOVE_MISSILE (2): +15 to every extent
+ * MOVE_WORLDONLY (3): ignore everything except bsp
+ * MOVE_HITMODEL (4): hit model, not bbox
+
+### traceline
+
+    void traceline(vector v1, vector v2, int tryents, entity ignoreentity);
+
+degenerate case of tracebox when min and max are equal
+
+### result globals
+
+    bool trace_allsolid;
+
+trace never left solid
+
+    bool trace_startsolid;
+
+trace started inside solid
+
+    float trace_fraction;
+
+distance before collision: 0..1, 1 if no collision
+
+    vector trace_endpos;
+
+v1 + (v2 - v1) * trace_fraction
+
+    vector trace_plane_normal;
+
+normalized plane normal, '0 0 0' if no collision.
+May be present if edges touch without clipping, use `trace_fraction < 1` as a determinant instead
+
+    float trace_plane_dist;
+
+
+
+    entity trace_ent;
+
+entity hit, if any
+
+    bool trace_inopen;
+
+
+
+    bool trace_inwater;
+
+
+
+    int trace_dpstartcontents;
+
+DPCONTENTS_ value at start position of trace
+
+    int trace_dphitcontents;
+
+DPCONTENTS_ value of impacted surface (not contents at impact point, just contents of the surface that was hit)
+
+    int trace_dphitq3surfaceflags;
+
+Q3SURFACEFLAG_ value of impacted surface
+
+    string trace_dphittexturename;
+
+texture name of impacted surface
+
+    int trace_networkentity;
+
+
index ff9214844b3e5c181878b7ef31c40621e85a59e3..2ed0f9063889af40f2270740e069f47d29f62789 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef DPEXTENSIONS_H
-#define DPEXTENSIONS_H
+#pragma once
 
 #pragma noref 1
 
@@ -64,5 +63,3 @@ int() _buf_create = #460;
 #define buf_create _buf_create
 
 #pragma noref 0
-
-#endif
index f4c49f143b5c119cd3dbb1a7c434a53d701bf681..0267be882462d75055d6bf8bf6ed5b153bc7493c 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef KEYCODES_H
-#define KEYCODES_H
+#pragma once
 
 #pragma noref 1
 
@@ -10,5 +9,3 @@
 //#undef float
 
 #pragma noref 0
-
-#endif
index c0e6d3ba728e738b5cc7f431e2025501df0278e4..036d87ff2fa8e7a213932d69fc66e84e30700a94 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef MENUDEFS_H
-#define MENUDEFS_H
+#pragma once
 
 #pragma noref 1
 
@@ -40,6 +39,6 @@ int(string s1, string s2, int len) _strncasecmp = #230;
 int() _buf_create = #440;
 #define buf_create _buf_create
 
-#pragma noref 0
+bool(entity ent) wasfreed = #353;
 
-#endif
+#pragma noref 0
index db8752d2bb8075d2f9611c3c776830c6d682540d..16fd93450332e44fa0952d7003f6c50fb1fc6b03 100644 (file)
@@ -6,7 +6,9 @@
 #undef error
 #undef movetogoal
 #undef objerror
+#undef remove
 #undef walkmove
+#undef setcolor
 
 #ifdef MENUQC
        #define NULL (0, null_entity)
index b24d0120a49e56a0bee597379b3522e454013949..63cebbc1acd6e0cd74ece6de92ad524cbac82f1e 100644 (file)
@@ -6,4 +6,6 @@
 #define error builtin_error
 #define movetogoal builtin_movetogoal
 #define objerror builtin_objerror
+#define remove builtin_remove
 #define walkmove builtin_walkmove
+#define setcolor builtin_setcolor
index e2668a54b7cc5173d2aad78b1b4681f0a54fbc30..ccdf9bc0c4293353a7e8392b059831e313474958 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef PROGSDEFS_H
-#define PROGSDEFS_H
+#pragma once
 
 #pragma noref 1
 
@@ -30,5 +29,3 @@ MACRO_END
 
 #define use use1
 .void(entity this, entity actor, entity trigger) use;
-
-#endif
index 21060c82ef428625bcafe4986ff652b230773f4b..2f49d97bb88b184360d72b378a602d1f03f395da 100644 (file)
@@ -2599,3 +2599,12 @@ void coverage() = #642;  // Reports a coverage event. The engine counts for each
 float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513;
 //description:
 //use -1 as buffer handle to justs end delim as postdata
+
+//DP_USERMOVETYPES
+//idea: divVerent
+//darkplaces implementation: Mario
+//movetype definitions:
+float MOVETYPE_USER_FIRST = 128;
+float MOVETYPE_USER_LAST = 191;
+//description:
+//user defined movetypes can be added between the start and end points, without producing unknown movetype warnings
diff --git a/qcsrc/ecs/README.md b/qcsrc/ecs/README.md
new file mode 100644 (file)
index 0000000..d49094f
--- /dev/null
@@ -0,0 +1,47 @@
+# Xonotic entity component system
+
+## guidelines
+
+* avoid #if and #ifdef
+* avoid string
+* avoid declaring entity fields outside of components
+* uncrustify relentlessly
+* shared code in $file, prog specific code uses prefix: { client: cl_, server: sv_, menu: ui_ }. $file must exist
+* component naming =~ com_$component_$name
+* system naming =~ sys_$system_$name
+* event naming =~ evt_$component_$name
+* global naming =~ g_$name
+* cvar naming =~ xon_$name
+
+## components
+
+    COMPONENT($component);
+    .int com_$component_$property;
+
+## entities
+
+    entity e = new(foo);
+    e.com_$component = true;
+    e.com_$component_$property = 42;
+
+## systems
+
+    SYSTEM($system, 30, 10);
+    sys_$system_update(entity this, float dt) {
+        code;
+    }
+
+## events
+
+### declaring
+
+    EVENT($component_$name, (entity this));
+
+### emitting
+
+    emit($event, it);
+
+### listening
+
+    entity listener = new_pure(someListener);
+    subscribe(listener, $event, void(entity this) { code; });
diff --git a/qcsrc/ecs/_mod.inc b/qcsrc/ecs/_mod.inc
new file mode 100644 (file)
index 0000000..48b7069
--- /dev/null
@@ -0,0 +1,6 @@
+// generated file; do not modify
+#include <ecs/main.qc>
+
+#include <ecs/components/_mod.inc>
+#include <ecs/events/_mod.inc>
+#include <ecs/systems/_mod.inc>
diff --git a/qcsrc/ecs/_mod.qh b/qcsrc/ecs/_mod.qh
new file mode 100644 (file)
index 0000000..0d1ff44
--- /dev/null
@@ -0,0 +1,6 @@
+// generated file; do not modify
+#include <ecs/main.qh>
+
+#include <ecs/components/_mod.qh>
+#include <ecs/events/_mod.qh>
+#include <ecs/systems/_mod.qh>
diff --git a/qcsrc/ecs/components/_mod.inc b/qcsrc/ecs/components/_mod.inc
new file mode 100644 (file)
index 0000000..0975871
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <ecs/components/input.qc>
+#include <ecs/components/physics.qc>
diff --git a/qcsrc/ecs/components/_mod.qh b/qcsrc/ecs/components/_mod.qh
new file mode 100644 (file)
index 0000000..64fad31
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <ecs/components/input.qh>
+#include <ecs/components/physics.qh>
diff --git a/qcsrc/ecs/components/input.qc b/qcsrc/ecs/components/input.qc
new file mode 100644 (file)
index 0000000..13b3413
--- /dev/null
@@ -0,0 +1,3 @@
+#include "input.qh"
+
+void com_in_interpolate(entity it, float a) { }
diff --git a/qcsrc/ecs/components/input.qh b/qcsrc/ecs/components/input.qh
new file mode 100644 (file)
index 0000000..bc1c66a
--- /dev/null
@@ -0,0 +1,7 @@
+#pragma once
+
+COMPONENT(in);
+.vector com_in_move, com_in_move_prev;
+.vector com_in_angles;
+.bool   com_in_jump;
+.bool   com_in_crouch;
diff --git a/qcsrc/ecs/components/physics.qc b/qcsrc/ecs/components/physics.qc
new file mode 100644 (file)
index 0000000..5094ad1
--- /dev/null
@@ -0,0 +1,10 @@
+#include "physics.qh"
+
+bool autocvar_xon_com_phys_interpolate = true;
+
+void com_phys_interpolate(entity it, float a)
+{
+       if (!autocvar_xon_com_phys_interpolate) a = 1;
+       it.origin = it.com_phys_pos_prev * (1 - a) + it.com_phys_pos * a;
+       it.angles = it.com_phys_ang_prev * (1 - a) + it.com_phys_ang * a;  // TODO: slerp, not lerp
+}
diff --git a/qcsrc/ecs/components/physics.qh b/qcsrc/ecs/components/physics.qh
new file mode 100644 (file)
index 0000000..f150a29
--- /dev/null
@@ -0,0 +1,26 @@
+#pragma once
+
+COMPONENT(phys);
+.vector com_phys_pos, com_phys_pos_prev;
+.vector com_phys_ang, com_phys_ang_prev;
+.vector com_phys_vel;
+.float com_phys_vel_max;
+.float com_phys_vel_max_air;
+.float com_phys_vel_max_air_strafe;
+.vector com_phys_acc;
+.float com_phys_acc_rate;
+.float com_phys_acc_rate_air;
+.float com_phys_acc_rate_air_strafe;
+.float com_phys_acc_rate_air_stop;
+.float com_phys_friction;
+
+.vector com_phys_gravity;
+.float com_phys_gravity_factor;
+// TODO: remove
+.bool com_phys_ground;
+.bool com_phys_air;
+.bool com_phys_ladder;
+.bool com_phys_vel_2d;
+.bool com_phys_water;
+.bool com_phys_friction_air;
+.bool move_qcphysics;
diff --git a/qcsrc/ecs/events/_mod.inc b/qcsrc/ecs/events/_mod.inc
new file mode 100644 (file)
index 0000000..c139d72
--- /dev/null
@@ -0,0 +1,2 @@
+// generated file; do not modify
+#include <ecs/events/physics.qc>
diff --git a/qcsrc/ecs/events/_mod.qh b/qcsrc/ecs/events/_mod.qh
new file mode 100644 (file)
index 0000000..1e8c791
--- /dev/null
@@ -0,0 +1,2 @@
+// generated file; do not modify
+#include <ecs/events/physics.qh>
diff --git a/qcsrc/ecs/events/physics.qc b/qcsrc/ecs/events/physics.qc
new file mode 100644 (file)
index 0000000..909f45c
--- /dev/null
@@ -0,0 +1 @@
+#include "physics.qh"
diff --git a/qcsrc/ecs/events/physics.qh b/qcsrc/ecs/events/physics.qh
new file mode 100644 (file)
index 0000000..df32c55
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+EVENT(phys_land, (entity this));
diff --git a/qcsrc/ecs/lib.qh b/qcsrc/ecs/lib.qh
new file mode 100644 (file)
index 0000000..2d48e57
--- /dev/null
@@ -0,0 +1,57 @@
+#pragma once
+
+/** Components always interpolate from the previous state */
+#define COMPONENT(com) \
+       void com_##com##_interpolate(entity it, float a); \
+       .bool com_##com
+
+#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body)
+
+
+#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T
+
+#define emit(T, ...) \
+       MACRO_BEGIN \
+       FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \
+       MACRO_END
+
+#define subscribe(listener, T, fn) \
+       MACRO_BEGIN \
+       listener.evt_##T = (fn); \
+       listener.evt_##T##_listener = true; \
+       MACRO_END
+
+
+/**
+ * framelimit 0 is no limit, interpolation does not apply
+ * framerate below minfps will result in less than 100% speed
+ */
+#define SYSTEM(sys, frameLimit, minfps) \
+       void sys_##sys##_update(entity this, float dt); \
+       noref float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
+       noref float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
+
+#define SYSTEM_UPDATE(sys) \
+       MACRO_BEGIN \
+       static float t = 0; \
+       float dt = autocvar_xon_sys_##sys##_dt; \
+       float minfps = autocvar_xon_sys_##sys##_minfps; \
+       static float accumulator = 0; \
+       float a = 0; \
+       if (dt) { \
+               accumulator += min(frametime, 1 / (minfps)); \
+       } else { \
+               accumulator += frametime; \
+               dt = accumulator; \
+               a = 1; \
+       } \
+       while (accumulator >= dt) \
+       { \
+               time = t; \
+               FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
+               t += dt; \
+               accumulator -= dt; \
+       } \
+       if (!a) a = accumulator / dt; \
+       FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
+       MACRO_END
diff --git a/qcsrc/ecs/main.qc b/qcsrc/ecs/main.qc
new file mode 100644 (file)
index 0000000..29be3f3
--- /dev/null
@@ -0,0 +1,12 @@
+#include "main.qh"
+
+#include "components/_mod.qh"
+#include "events/_mod.qh"
+#include "systems/_mod.qh"
+
+void systems_update()
+{
+       float realtime = time;
+       SYSTEM_UPDATE(phys);
+       time = realtime;
+}
diff --git a/qcsrc/ecs/main.qh b/qcsrc/ecs/main.qh
new file mode 100644 (file)
index 0000000..724cb1e
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "lib.qh"
+
+void systems_update();
diff --git a/qcsrc/ecs/systems/_mod.inc b/qcsrc/ecs/systems/_mod.inc
new file mode 100644 (file)
index 0000000..a5ada6c
--- /dev/null
@@ -0,0 +1,9 @@
+// generated file; do not modify
+#include <ecs/systems/input.qc>
+#include <ecs/systems/physics.qc>
+#ifdef CSQC
+    #include <ecs/systems/cl_physics.qc>
+#endif
+#ifdef SVQC
+    #include <ecs/systems/sv_physics.qc>
+#endif
diff --git a/qcsrc/ecs/systems/_mod.qh b/qcsrc/ecs/systems/_mod.qh
new file mode 100644 (file)
index 0000000..869aefd
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <ecs/systems/input.qh>
+#include <ecs/systems/physics.qh>
diff --git a/qcsrc/ecs/systems/cl_physics.qc b/qcsrc/ecs/systems/cl_physics.qc
new file mode 100644 (file)
index 0000000..f74e9c2
--- /dev/null
@@ -0,0 +1,29 @@
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+       this.team = myteam + 1; // is this correct?
+       PHYS_WATERJUMP_TIME(this) -= dt;
+       this.movement = PHYS_INPUT_MOVEVALUES(this);
+       this.items = STAT(ITEMS, this);
+       this.spectatorspeed = STAT(SPECTATORSPEED, this);
+       if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
+               UNSET_JUMP_HELD(this);           // canjump = true
+       PM_ClientMovement_UpdateStatus(this);
+}
+
+bool sys_phys_override(entity this, float dt)
+{
+       // no vehicle prediction
+       return hud != HUD_NORMAL;
+}
+
+void sys_phys_monitor(entity this, float dt) {}
+
+void sys_phys_ai(entity this) {}
+
+void sys_phys_pregame_hold(entity this) {}
+
+void sys_phys_spectator_control(entity this) {}
+
+void sys_phys_fixspeed(entity this, float maxspeed_mod) {}
diff --git a/qcsrc/ecs/systems/input.qc b/qcsrc/ecs/systems/input.qc
new file mode 100644 (file)
index 0000000..eac3625
--- /dev/null
@@ -0,0 +1,7 @@
+#include "input.qh"
+
+void sys_in_update(entity this, float dt)
+{
+       this.com_in_jump = PHYS_INPUT_BUTTON_JUMP(this);
+       this.com_in_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
+}
diff --git a/qcsrc/ecs/systems/input.qh b/qcsrc/ecs/systems/input.qh
new file mode 100644 (file)
index 0000000..fc3f11a
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+SYSTEM(in, 30, 10);
diff --git a/qcsrc/ecs/systems/physics.qc b/qcsrc/ecs/systems/physics.qc
new file mode 100644 (file)
index 0000000..0c18e58
--- /dev/null
@@ -0,0 +1,491 @@
+#include "physics.qh"
+#include "input.qh"
+
+.int disableclientprediction;
+
+void sys_phys_simulate(entity this, float dt);
+void sys_phys_simulate_simple(entity this, float dt);
+
+void sys_phys_update(entity this, float dt)
+{
+       if (!IS_CLIENT(this)) {
+               sys_phys_simulate_simple(this, dt);
+               return;
+       }
+       sys_in_update(this, dt);
+
+       sys_phys_fix(this, dt);
+       if (sys_phys_override(this, dt)) { return; } sys_phys_monitor(this, dt);
+
+       this.buttons_old = PHYS_INPUT_BUTTON_MASK(this);
+       this.movement_old = this.movement;
+       this.v_angle_old = this.v_angle;
+
+       sys_phys_ai(this);
+
+       sys_phys_pregame_hold(this);
+
+       if (IS_SVQC) {
+               if (this.move_movetype == MOVETYPE_NONE) { return; }
+               // when we get here, disableclientprediction cannot be 2
+               this.disableclientprediction = (this.move_qcphysics) ? -1 : 0;
+       }
+
+       viewloc_PlayerPhysics(this);
+
+       PM_check_frozen(this);
+
+       PM_check_blocked(this);
+
+       float maxspeed_mod = (!this.in_swamp) ? 1 : this.swamp_slowdown;  // cvar("g_balance_swamp_moverate");
+
+// conveyors: first fix velocity
+       if (this.conveyor.state) { this.velocity -= this.conveyor.movedir; }
+       MUTATOR_CALLHOOK(PlayerPhysics, this, dt);
+
+       if (!IS_PLAYER(this)) {
+               sys_phys_spectator_control(this);
+               maxspeed_mod = this.spectatorspeed;
+       }
+       sys_phys_fixspeed(this, maxspeed_mod);
+
+       if (IS_DEAD(this)) {
+               // handle water here
+               vector midpoint = ((this.absmin + this.absmax) * 0.5);
+               if (pointcontents(midpoint) == CONTENT_WATER) {
+                       this.velocity = this.velocity * 0.5;
+
+                       // do we want this?
+                       // if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
+                       // { this.velocity_z = 70; }
+               }
+               goto end;
+       }
+
+       PM_check_slick(this);
+
+       if (IS_SVQC && !PHYS_FIXANGLE(this)) { this.angles = '0 1 0' * this.v_angle.y; }
+       if (IS_PLAYER(this)) {
+               if (IS_ONGROUND(this)) {
+                       PM_check_hitground(this);
+                       PM_Footsteps(this);
+               } else if (IsFlying(this)) {
+                       this.wasFlying = true;
+               }
+               CheckPlayerJump(this);
+       }
+
+       if (this.flags & FL_WATERJUMP) {
+               this.velocity_x = this.movedir.x;
+               this.velocity_y = this.movedir.y;
+               if (this.waterlevel == WATERLEVEL_NONE
+                   || time > PHYS_TELEPORT_TIME(this)
+                   || PHYS_WATERJUMP_TIME(this) <= 0
+                  ) {
+                       this.flags &= ~FL_WATERJUMP;
+                       PHYS_TELEPORT_TIME(this) = 0;
+                       PHYS_WATERJUMP_TIME(this) = 0;
+               }
+       } else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod, dt)) {
+               // handled
+       } else if (this.move_movetype == MOVETYPE_NOCLIP
+           || this.move_movetype == MOVETYPE_FLY
+           || this.move_movetype == MOVETYPE_FLY_WORLDONLY
+           || MUTATOR_CALLHOOK(IsFlying, this)) {
+               this.com_phys_friction = PHYS_FRICTION(this);
+               this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+               this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+               this.com_phys_friction_air = true;
+               sys_phys_simulate(this, dt);
+               this.com_phys_friction_air = false;
+       } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
+               this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+               this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+               this.com_phys_water = true;
+               sys_phys_simulate(this, dt);
+               this.com_phys_water = false;
+       } else if (time < this.ladder_time) {
+               this.com_phys_friction = PHYS_FRICTION(this);
+               this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+               this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+               this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt;
+               if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); }
+               this.com_phys_ladder = true;
+               this.com_phys_friction_air = true;
+               sys_phys_simulate(this, dt);
+               this.com_phys_friction_air = false;
+               this.com_phys_ladder = false;
+               this.com_phys_gravity = '0 0 0';
+       } else if (ITEMS_STAT(this) & IT_USING_JETPACK) {
+               PM_jetpack(this, maxspeed_mod, dt);
+       } else if (IS_ONGROUND(this)) {
+               if (!WAS_ONGROUND(this)) {
+                       emit(phys_land, this);
+                       if (this.lastground < time - 0.3) {
+                               this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
+                       }
+               }
+               this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+               this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt;
+               if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); }
+               this.com_phys_ground = true;
+               this.com_phys_vel_2d = true;
+               sys_phys_simulate(this, dt);
+               this.com_phys_vel_2d = false;
+               this.com_phys_ground = false;
+               this.com_phys_gravity = '0 0 0';
+       } else {
+               this.com_phys_acc_rate_air = PHYS_AIRACCELERATE(this) * min(maxspeed_mod, 1);
+               this.com_phys_acc_rate_air_stop = PHYS_AIRSTOPACCELERATE(this) * maxspeed_mod;
+               this.com_phys_acc_rate_air_strafe = PHYS_AIRSTRAFEACCELERATE(this) * maxspeed_mod;
+               this.com_phys_vel_max_air_strafe = PHYS_MAXAIRSTRAFESPEED(this) * maxspeed_mod;
+               this.com_phys_vel_max_air = PHYS_MAXAIRSPEED(this) * maxspeed_mod;
+               this.com_phys_vel_max = PHYS_MAXAIRSPEED(this) * min(maxspeed_mod, 1);
+               this.com_phys_air = true;
+               this.com_phys_vel_2d = true;
+               sys_phys_simulate(this, dt);
+               this.com_phys_vel_2d = false;
+               this.com_phys_air = false;
+       }
+
+       LABEL(end)
+       if (IS_ONGROUND(this)) { this.lastground = time; }
+// conveyors: then break velocity again
+       if (this.conveyor.state) { this.velocity += this.conveyor.movedir; }
+       this.lastflags = this.flags;
+
+       this.lastclassname = this.classname;
+}
+
+/** for players */
+void sys_phys_simulate(entity this, float dt)
+{
+       if (!this.com_phys_ground && !this.com_phys_air) {
+               // noclipping
+               // flying
+               // on a spawnfunc_func_ladder
+               // swimming in spawnfunc_func_water
+               // swimming
+               UNSET_ONGROUND(this);
+
+               if (this.com_phys_friction_air) {
+                       const vector g = -this.com_phys_gravity;
+                       this.velocity_z += g.z / 2;
+                       this.velocity = this.velocity * (1 - dt * this.com_phys_friction);
+                       this.velocity_z += g.z / 2;
+               }
+       }
+
+       if (this.com_phys_water) {
+               // water jump only in certain situations
+               // this mimics quakeworld code
+               if (this.com_in_jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc) {
+                       vector yawangles = '0 1 0' * this.v_angle.y;
+                       makevectors(yawangles);
+                       vector forward = v_forward;
+                       vector spot = this.origin + 24 * forward;
+                       spot_z += 8;
+                       traceline(spot, spot, MOVE_NOMONSTERS, this);
+                       if (trace_startsolid) {
+                               spot_z += 24;
+                               traceline(spot, spot, MOVE_NOMONSTERS, this);
+                               if (!trace_startsolid) {
+                                       this.velocity = forward * 50;
+                                       this.velocity_z = 310;
+                                       UNSET_ONGROUND(this);
+                                       SET_JUMP_HELD(this);
+                               }
+                       }
+               }
+       }
+       makevectors(vmul(this.v_angle, (this.com_phys_vel_2d ? '0 1 0' : '1 1 1')));
+       // wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+       vector wishvel = v_forward * this.movement.x
+           + v_right * this.movement.y
+           + '0 0 1' * this.movement.z * (this.com_phys_vel_2d ? 0 : 1);
+       if (this.com_phys_water) {
+               if (PHYS_INPUT_BUTTON_CROUCH(this)) {
+                       wishvel.z = -PHYS_MAXSPEED(this);
+               }
+               if (this.viewloc) {
+                       wishvel.z = -160;    // drift anyway
+               } else if (wishvel == '0 0 0') {
+                       wishvel = '0 0 -60'; // drift towards bottom
+               }
+       }
+       if (this.com_phys_ladder) {
+               if (this.viewloc) {
+                       wishvel.z = this.movement_old.x;
+               }
+               if (this.ladder_entity.classname == "func_water") {
+                       float f = vlen(wishvel);
+                       if (f > this.ladder_entity.speed) {
+                               wishvel *= (this.ladder_entity.speed / f);
+                       }
+
+                       this.watertype = this.ladder_entity.skin;
+                       f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
+                       if ((this.origin_z + this.view_ofs_z) < f) {
+                               this.waterlevel = WATERLEVEL_SUBMERGED;
+                       } else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f) {
+                               this.waterlevel = WATERLEVEL_SWIMMING;
+                       } else if ((this.origin_z + this.mins_z + 1) < f) {
+                               this.waterlevel = WATERLEVEL_WETFEET;
+                       } else {
+                               this.waterlevel = WATERLEVEL_NONE;
+                               this.watertype = CONTENT_EMPTY;
+                       }
+               }
+       }
+       // acceleration
+       const vector wishdir = normalize(wishvel);
+       float wishspeed = min(vlen(wishvel), this.com_phys_vel_max);
+
+       if (this.com_phys_air) {
+               if ((IS_SVQC && time >= PHYS_TELEPORT_TIME(this))
+                   ||  (IS_CSQC && PHYS_WATERJUMP_TIME(this) <= 0)) {
+                       // apply air speed limit
+                       float airaccelqw = PHYS_AIRACCEL_QW(this);
+                       float wishspeed0 = wishspeed;
+                       const float maxairspd = this.com_phys_vel_max;
+                       wishspeed = min(wishspeed, maxairspd);
+                       if (IS_DUCKED(this)) {
+                               wishspeed *= 0.5;
+                       }
+                       float airaccel = this.com_phys_acc_rate_air;
+
+                       float accelerating = (this.velocity * wishdir > 0);
+                       float wishspeed2 = wishspeed;
+
+                       // CPM: air control
+                       if (PHYS_AIRSTOPACCELERATE(this)) {
+                               vector curdir = normalize(vec2(this.velocity));
+                               airaccel += (this.com_phys_acc_rate_air_stop - airaccel) * max(0, -(curdir * wishdir));
+                       }
+                       // note that for straight forward jumping:
+                       // step = accel * dt * wishspeed0;
+                       // accel  = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+                       // -->
+                       // dv/dt = accel * maxspeed (when slow)
+                       // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
+                       // log dv/dt = logaccel + logmaxspeed (when slow)
+                       // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
+                       float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90);  // if one is nonzero, other is always zero
+                       if (PHYS_MAXAIRSTRAFESPEED(this)) {
+                               wishspeed =
+                                   min(wishspeed,
+                                       GeomLerp(this.com_phys_vel_max_air, strafity, this.com_phys_vel_max_air_strafe));
+                       }
+                       if (PHYS_AIRSTRAFEACCELERATE(this)) {
+                               airaccel = GeomLerp(airaccel, strafity, this.com_phys_acc_rate_air_strafe);
+                       }
+                       if (PHYS_AIRSTRAFEACCEL_QW(this)) {
+                               airaccelqw =
+                                   (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
+                                   *
+                                   (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
+                       }
+                       // !CPM
+
+                       if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0) {
+                               PM_AirAccelerate(this, dt, wishdir, wishspeed2);
+                       } else {
+                               float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
+                               PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw,
+                                       PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this));
+                       }
+
+                       if (PHYS_AIRCONTROL(this)) {
+                               CPM_PM_Aircontrol(this, dt, wishdir, wishspeed2);
+                       }
+               }
+       } else {
+               if (this.com_phys_ground && IS_DUCKED(this)) { wishspeed *= 0.5; }
+               if (this.com_phys_water) {
+                       wishspeed *= 0.7;
+
+                       //      if (PHYS_WATERJUMP_TIME(this) <= 0) // TODO: use
+                       {
+                               // water friction
+                               float f = 1 - dt * PHYS_FRICTION(this);
+                               f = min(max(0, f), 1);
+                               this.velocity *= f;
+
+                               f = wishspeed - this.velocity * wishdir;
+                               if (f > 0) {
+                                       float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, f);
+                                       this.velocity += accelspeed * wishdir;
+                               }
+
+                               // holding jump button swims upward slowly
+                               if (this.com_in_jump && !this.viewloc) {
+                                       // was:
+                                       // lava: 50
+                                       // slime: 80
+                                       // water: 100
+                                       // idea: double those
+                                       this.velocity_z = 200;
+                                       if (this.waterlevel >= WATERLEVEL_SUBMERGED) {
+                                               this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
+                                       }
+                               }
+                       }
+                       if (this.viewloc) {
+                               const float addspeed = wishspeed - this.velocity * wishdir;
+                               if (addspeed > 0) {
+                                       const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed);
+                                       this.velocity += accelspeed * wishdir;
+                               }
+                       } else {
+                               // water acceleration
+                               PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+                       }
+                       return;
+               }
+               if (this.com_phys_ground) {
+                       // apply edge friction
+                       const float f2 = vlen2(vec2(this.velocity));
+                       if (f2 > 0) {
+                               trace_dphitq3surfaceflags = 0;
+                               tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+                               // TODO: apply edge friction
+                               // apply ground friction
+                               const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+                                   ? PHYS_FRICTION_SLICK(this)
+                                       : PHYS_FRICTION(this);
+
+                               float f = sqrt(f2);
+                               f = 1 - dt * realfriction
+                                   * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
+                               f = max(0, f);
+                               this.velocity *= f;
+                               /*
+                                  Mathematical analysis time!
+
+                                  Our goal is to invert this mess.
+
+                                  For the two cases we get:
+                                   v = v0 * (1 - dt * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
+                                     = v0 - dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+                                   v0 = v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+                                  and
+                                   v = v0 * (1 - dt * PHYS_FRICTION(this))
+                                   v0 = v / (1 - dt * PHYS_FRICTION(this))
+
+                                  These cases would be chosen ONLY if:
+                                   v0 < PHYS_STOPSPEED(this)
+                                   v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
+                                   v < PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this))
+                                  and, respectively:
+                                   v0 >= PHYS_STOPSPEED(this)
+                                   v / (1 - dt * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
+                                   v >= PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this))
+                                */
+                       }
+                       const float addspeed = wishspeed - this.velocity * wishdir;
+                       if (addspeed > 0) {
+                               const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed);
+                               this.velocity += accelspeed * wishdir;
+                       }
+                       return;
+               }
+
+               if (IS_CSQC ? PHYS_WATERJUMP_TIME(this) <= 0 : time >= PHYS_TELEPORT_TIME(this)) {
+                       PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+               }
+       }
+}
+
+.entity groundentity;
+/** for other entities */
+void sys_phys_simulate_simple(entity this, float dt)
+{
+       vector mn = this.mins;
+       vector mx = this.maxs;
+
+       vector g = '0 0 0';
+       if (this.com_phys_gravity_factor && !g) g = '0 0 -1' * PHYS_GRAVITY(NULL);
+
+       vector acc = this.com_phys_acc;
+       vector vel = this.com_phys_vel;
+       vector pos = this.com_phys_pos;
+
+       // SV_Physics_Toss
+
+       vel += g * dt;
+
+       this.angles += dt * this.avelocity;
+       float movetime = dt;
+       for (int i = 0; i < MAX_CLIP_PLANES && movetime > 0; i++) {
+               vector push = vel * movetime;
+               vector p0 = pos;
+               vector p1 = p0 + push;
+               // SV_PushEntity
+               tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+               if (!trace_startsolid) {
+                       bool hit = trace_fraction < 1;
+                       pos = trace_endpos;
+                       entity ent = trace_ent;
+                       // SV_LinkEdict_TouchAreaGrid
+                       if (this.solid != SOLID_NOT) {
+                               FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
+                                       if (it.solid != SOLID_TRIGGER || it == this) continue;
+                                       if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax)) {
+                                           // SV_LinkEdict_TouchAreaGrid_Call
+                                           trace_allsolid = false;
+                                           trace_startsolid = false;
+                                           trace_fraction = 1;
+                                           trace_inwater = false;
+                                           trace_inopen = true;
+                                           trace_endpos = it.origin;
+                                           trace_plane_normal = '0 0 1';
+                                           trace_plane_dist = 0;
+                                           trace_ent = this;
+                                           trace_dpstartcontents = 0;
+                                           trace_dphitcontents = 0;
+                                           trace_dphitq3surfaceflags = 0;
+                                           trace_dphittexturename = string_null;
+                                           gettouch(it)(this, it);
+                                           vel = this.velocity;
+                                       }
+                               });
+                       }
+                       if (hit && this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || this.groundentity != ent)) {
+                               // SV_Impact (ent, trace);
+                               tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+                               void(entity, entity) touched = gettouch(this);
+                               if (touched && this.solid != SOLID_NOT) {
+                                       touched(ent, this);
+                               }
+                               void(entity, entity) touched2 = gettouch(ent);
+                               if (this && ent && touched2 && ent.solid != SOLID_NOT) {
+                                       trace_endpos = ent.origin;
+                                       trace_plane_normal *= -1;
+                                       trace_plane_dist *= -1;
+                                       trace_ent = this;
+                                       trace_dpstartcontents = 0;
+                                       trace_dphitcontents = 0;
+                                       trace_dphitq3surfaceflags = 0;
+                                       trace_dphittexturename = string_null;
+                                       touched2(this, ent);
+                               }
+                       }
+               }
+               // end SV_PushEntity
+               if (wasfreed(this)) { return; }
+               tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+               if (trace_fraction == 1) { break; }
+               movetime *= 1 - min(1, trace_fraction);
+               ClipVelocity(vel, trace_plane_normal, vel, 1);
+       }
+
+       this.com_phys_acc = acc;
+       this.com_phys_vel = vel;
+       this.com_phys_pos = pos;
+       setorigin(this, this.com_phys_pos);
+}
+
+void sys_phys_update_single(entity this)
+{
+       sys_phys_simulate_simple(this, frametime);
+}
diff --git a/qcsrc/ecs/systems/physics.qh b/qcsrc/ecs/systems/physics.qh
new file mode 100644 (file)
index 0000000..cef9916
--- /dev/null
@@ -0,0 +1,11 @@
+#pragma once
+
+SYSTEM(phys, 30, 10);
+
+void sys_phys_fix(entity this, float dt);
+bool sys_phys_override(entity this, float dt);
+void sys_phys_monitor(entity this, float dt);
+void sys_phys_pregame_hold(entity this);
+void sys_phys_ai(entity this);
+void sys_phys_spectator_control(entity this);
+void sys_phys_fixspeed(entity this, float maxspeed_mod);
diff --git a/qcsrc/ecs/systems/sv_physics.qc b/qcsrc/ecs/systems/sv_physics.qc
new file mode 100644 (file)
index 0000000..4973a18
--- /dev/null
@@ -0,0 +1,117 @@
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+       WarpZone_PlayerPhysics_FixVAngle(this);
+       Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
+}
+
+bool sys_phys_override(entity this, float dt)
+{
+       int buttons = PHYS_INPUT_BUTTON_MASK(this);
+       if (PM_check_specialcommand(this, buttons)) { return true; }
+       if (this.PlayerPhysplug && this.PlayerPhysplug(this, dt)) { return true; }
+       return false;
+}
+
+void sys_phys_monitor(entity this, float dt)
+{
+       int buttons = PHYS_INPUT_BUTTON_MASK(this);
+       anticheat_physics(this);
+       if (sv_maxidle > 0) {
+               if (buttons != this.buttons_old
+                   || this.movement != this.movement_old
+                   || this.v_angle != this.v_angle_old) { this.parm_idlesince = time; }
+       }
+       PM_check_nickspam(this);
+       PM_check_punch(this, dt);
+}
+
+void sys_phys_ai(entity this)
+{
+       if (!IS_BOT_CLIENT(this)) { return; }
+       if (playerdemo_read(this)) { return; }
+       bot_think(this);
+}
+
+void sys_phys_pregame_hold(entity this)
+{
+       if (!IS_PLAYER(this)) { return; }
+       const bool allowed_to_move = (time >= game_starttime && !gameover);
+       if (!allowed_to_move) {
+               this.velocity = '0 0 0';
+               set_movetype(this, MOVETYPE_NONE);
+               this.disableclientprediction = 2;
+       } else if (this.disableclientprediction == 2) {
+               if (this.move_movetype == MOVETYPE_NONE) { set_movetype(this, MOVETYPE_WALK); }
+               this.disableclientprediction = 0;
+       }
+}
+
+void sys_phys_spectator_control(entity this)
+{
+       float maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
+       if (!this.spectatorspeed) { this.spectatorspeed = maxspeed_mod; }
+       if ((this.impulse >= 1 && this.impulse <= 19)
+           || (this.impulse >= 200 && this.impulse <= 209)
+           || (this.impulse >= 220 && this.impulse <= 229)
+          ) {
+               if (this.lastclassname != STR_PLAYER) {
+                       if (this.impulse == 10
+                           || this.impulse == 15
+                           || this.impulse == 18
+                           || (this.impulse >= 200 && this.impulse <= 209)
+                          ) { this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5); } else if (this.impulse == 11) {
+                               this.spectatorspeed = maxspeed_mod;
+                       } else if (this.impulse == 12
+                           || this.impulse == 16
+                           || this.impulse == 19
+                           || (this.impulse >= 220 && this.impulse <= 229)
+                                 ) {
+                               this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
+                       } else if (this.impulse >= 1 && this.impulse <= 9) {
+                               this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
+                       }
+               }  // otherwise just clear
+               this.impulse = 0;
+       }
+}
+
+void sys_phys_fixspeed(entity this, float maxspeed_mod)
+{
+       float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
+       if (this.speed != spd) {
+               this.speed = spd;
+               string temps = ftos(spd);
+               stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
+               stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
+               stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
+               stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
+       }
+
+       if (this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min) {
+               this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
+               stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
+       }
+       if (this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max) {
+               this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
+               stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
+       }
+}
+
+void sys_phys_land(entity this)
+{
+       if (autocvar_speedmeter) {
+               LOG_TRACEF("landing velocity: %v (abs: %f)", this.velocity, vlen(this.velocity));
+       }
+       if (this.jumppadcount > 1) {
+               LOG_TRACEF("%dx jumppad combo", this.jumppadcount);
+       }
+       this.jumppadcount = 0;
+}
+
+STATIC_INIT(sys_phys)
+{
+       entity listener = new_pure(sys_phys);
+       subscribe(listener, phys_land, sys_phys_land);
+}
index 03b943625832c472670d76169a58a8ba32195684..65dc080c2ca7a103779918b04ad3cabb25718e81 100644 (file)
@@ -2,6 +2,18 @@
        #define COMPAT_NO_MOD_IS_XONOTIC
 #endif
 
+#ifdef CSQC
+#define IS_CSQC 1
+#else
+#define IS_CSQC 0
+#endif
+
+#ifdef SVQC
+#define IS_SVQC 1
+#else
+#define IS_SVQC 0
+#endif
+
 #include "compiler.qh"
 
 #ifndef QCC_SUPPORT_INT
 #include "macro.qh"
 
 #if NDEBUG
-    #define TC(T, sym) MACRO_BEGIN MACRO_END
+       #define TC(T, sym) MACRO_BEGIN MACRO_END
 #else
-    #define TC(T, sym) MACRO_BEGIN \
-        if (!is_##T(sym)) { \
-            LOG_WARNINGF("Type check failed: " #sym " :: " #T); \
-            isnt_##T(sym); \
-        } \
-    MACRO_END
+       #define TC(T, sym) MACRO_BEGIN \
+               if (!is_##T(sym)) { \
+                       LOG_WARNF("Type check failed: " #sym " :: " #T); \
+                       isnt_##T(sym); \
+               } \
+       MACRO_END
 #endif
 
-#define   is_float(        this) (true || ftoe(this))
-#define isnt_float(        this)
-#define   is_vector(       this) (true || vtos(this))
-#define isnt_vector(       this)
-#define   is_string(       this) (true || stof(this))
-#define isnt_string(       this)
-#define   is_entity(       this) (true || etof(this))
-#define isnt_entity(       this)
-bool      is_int(    float this) { return this == floor(this); }
-void    isnt_int(    float this) { print(ftos(this)); }
-bool      is_bool(   float this) { return this == true || this == false; }
-void    isnt_bool(   float this) { print(ftos(this)); }
+#define   is_float(this) (true || ftoe(this))
+#define isnt_float(this)
+#define   is_vector(this) (true || vtos(this))
+#define isnt_vector(this)
+#define   is_string(this) (true || stof(this))
+#define isnt_string(this)
+#define   is_entity(this) (true || etof(this))
+#define isnt_entity(this)
+bool      is_int(float this) { return this == floor(this); }
+void    isnt_int(float this) { print(ftos(this)); }
+bool      is_bool(float this) { return this == true || this == false; }
+void    isnt_bool(float this) { print(ftos(this)); }
 
 #include "warpzone/mathlib.qc"
 
@@ -77,6 +89,7 @@ void    isnt_bool(   float this) { print(ftos(this)); }
 #include "file.qh"
 #include "functional.qh"
 #include "i18n.qh"
+#include "intrusivelist.qh"
 #include "iter.qh"
 #include "json.qc"
 #include "lazy.qh"
@@ -110,3 +123,157 @@ void    isnt_bool(   float this) { print(ftos(this)); }
 #include "yenc.qh"
 
 #include "matrix/_mod.inc"
+
+#ifndef SVQC
+#define objerror_safe(e)
+#else
+void make_safe_for_remove(entity this);
+    #define objerror_safe(e) make_safe_for_remove(e)
+#endif
+
+#define objerror(this, msg) MACRO_BEGIN { \
+       LOG_WARN("======OBJECT ERROR======"); \
+       entity _e = (this); \
+       eprint(_e); \
+       objerror_safe(_e); \
+       delete(_e); \
+       LOG_WARNF("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information", PROGNAME, __FUNC__, msg); \
+} MACRO_END
+
+#ifdef MENUQC
+       void _m_init();
+       void m_init() { if (_m_init) _m_init(); }
+       #define m_init _m_init
+
+       void _m_shutdown();
+       void m_shutdown() { if (_m_shutdown) _m_shutdown(); }
+       #define m_shutdown _m_shutdown
+
+       void _m_draw(float width, float height);
+       void m_draw(float width, float height) { if (_m_draw) _m_draw(width, height); }
+       #define m_draw _m_draw
+
+       void _m_keydown(int keynr, int ascii);
+       void m_keydown(int keynr, int ascii) { if (_m_keydown) _m_keydown(keynr, ascii); }
+       #define m_keydown _m_keydown
+
+       void _m_toggle(int mode);
+       void m_toggle(int mode) { if (_m_toggle) _m_toggle(mode); }
+       #define m_toggle _m_toggle
+#endif
+
+#ifdef SVQC
+       void _main();
+       void main() { if (_main) _main(); }
+       #define main _main
+
+       void _SV_Shutdown();
+       void SV_Shutdown() { if (_SV_Shutdown) _SV_Shutdown(); }
+       #define SV_Shutdown _SV_Shutdown
+
+       void _StartFrame();
+       void StartFrame() { if (_StartFrame) _StartFrame(); }
+       #define StartFrame _StartFrame
+
+       void _SetNewParms();
+       void SetNewParms() { if (_SetNewParms) _SetNewParms(); }
+       #define SetNewParms _SetNewParms
+
+       void _SetChangeParms(entity this);
+       void SetChangeParms() { ENGINE_EVENT(); if (_SetChangeParms) _SetChangeParms(this); }
+       #define SetChangeParms _SetChangeParms
+
+#ifdef DP_EXT_PRECONNECT
+       void _ClientPreConnect(entity this);
+       void ClientPreConnect() { ENGINE_EVENT(); if (_ClientPreConnect) _ClientPreConnect(this); }
+       #define ClientPreConnect _ClientPreConnect
+#endif
+
+       void _ClientConnect(entity this);
+       void ClientConnect() { ENGINE_EVENT(); if (_ClientConnect) _ClientConnect(this); }
+       #define ClientConnect _ClientConnect
+
+       void _ClientDisconnect(entity this);
+       void ClientDisconnect() { ENGINE_EVENT(); if (_ClientDisconnect) _ClientDisconnect(this); }
+       #define ClientDisconnect _ClientDisconnect
+
+       void _PutClientInServer(entity this);
+       void PutClientInServer() { ENGINE_EVENT(); if (_PutClientInServer) _PutClientInServer(this); }
+       #define PutClientInServer _PutClientInServer
+
+       void _ClientKill(entity this);
+       void ClientKill() { ENGINE_EVENT(); if (_ClientKill) _ClientKill(this); }
+       #define ClientKill _ClientKill
+
+       void _PlayerPreThink(entity this);
+       void PlayerPreThink() { ENGINE_EVENT(); if (_PlayerPreThink) _PlayerPreThink(this); }
+       #define PlayerPreThink _PlayerPreThink
+
+       void _PlayerPostThink(entity this);
+       void PlayerPostThink() { ENGINE_EVENT(); if (_PlayerPostThink) _PlayerPostThink(this); }
+       #define PlayerPostThink _PlayerPostThink
+
+       void _SV_PlayerPhysics(entity this);
+       void SV_PlayerPhysics() { ENGINE_EVENT(); if (_SV_PlayerPhysics) _SV_PlayerPhysics(this); }
+       #define SV_PlayerPhysics _SV_PlayerPhysics
+
+       void _SV_OnEntityPreSpawnFunction(entity this);
+       void SV_OnEntityPreSpawnFunction()
+       {
+               ENGINE_EVENT();
+               if (_SV_OnEntityPreSpawnFunction) _SV_OnEntityPreSpawnFunction(this);
+       }
+       #define SV_OnEntityPreSpawnFunction _SV_OnEntityPreSpawnFunction
+
+       void _SV_ChangeTeam(entity this, int _color);
+       void SV_ChangeTeam(int _color) { ENGINE_EVENT(); if (_SV_ChangeTeam) _SV_ChangeTeam(this, _color); }
+       #define SV_ChangeTeam _SV_ChangeTeam
+
+       void _SV_ParseClientCommand(entity this, string command);
+       void SV_ParseClientCommand(string command)
+       {
+               ENGINE_EVENT();
+               if (_SV_ParseClientCommand) _SV_ParseClientCommand(this, command);
+       }
+       #define SV_ParseClientCommand _SV_ParseClientCommand
+#endif
+
+#ifdef CSQC
+       void _CSQC_Init();
+       void CSQC_Init() { if (_CSQC_Init) _CSQC_Init(); }
+       #define CSQC_Init _CSQC_Init
+
+       void _CSQC_Shutdown();
+       void CSQC_Shutdown() { if (_CSQC_Shutdown) _CSQC_Shutdown(); }
+       #define CSQC_Shutdown _CSQC_Shutdown
+
+       void _CSQC_UpdateView(entity this, float w, float h);
+       void CSQC_UpdateView(float w, float h) { ENGINE_EVENT(); if (_CSQC_UpdateView) _CSQC_UpdateView(this, w, h); }
+       #define CSQC_UpdateView _CSQC_UpdateView
+
+       bool _CSQC_InputEvent(int inputType, float nPrimary, float nSecondary);
+       bool CSQC_InputEvent(int inputType, float nPrimary, float nSecondary)
+       {
+               return _CSQC_InputEvent ? _CSQC_InputEvent(inputType, nPrimary, nSecondary) : false;
+       }
+       #define CSQC_InputEvent _CSQC_InputEvent
+
+       bool _CSQC_ConsoleCommand(string s);
+       bool CSQC_ConsoleCommand(string s) { return _CSQC_ConsoleCommand ? _CSQC_ConsoleCommand(s) : false; }
+       #define CSQC_ConsoleCommand _CSQC_ConsoleCommand
+
+       void _CSQC_Ent_Update(entity this, bool isNew);
+       void CSQC_Ent_Update(bool isNew) { ENGINE_EVENT(); if (_CSQC_Ent_Update) _CSQC_Ent_Update(this, isNew); }
+       #define CSQC_Ent_Update _CSQC_Ent_Update
+
+       void _CSQC_Ent_Remove(entity this);
+       void CSQC_Ent_Remove() { ENGINE_EVENT(); if (_CSQC_Ent_Remove) _CSQC_Ent_Remove(this); }
+       #define CSQC_Ent_Remove _CSQC_Ent_Remove
+#endif
+#undef ENGINE_EVENT
+
+#if XONOTIC
+#ifdef GAMEQC
+       #include <ecs/_mod.qh>
+#endif
+#endif
diff --git a/qcsrc/lib/angle.qh b/qcsrc/lib/angle.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index c0809ecdf88d242e03fbd329dd648e636ee881a1..18305177bf8a266261e1be9ad143dfde695fcace 100644 (file)
@@ -20,7 +20,7 @@ USING(ArrayList, entity);
        MACRO_BEGIN \
        { \
                buf_del(this.al_buf); \
-               remove(this); \
+               delete(this); \
                this = NULL; \
        } MACRO_END
 
index d12ae42501494941e2dd9d679cbe8a13b8d12f64..e430a648d168a41d38a976745880113c6f3258f1 100644 (file)
@@ -9,57 +9,57 @@
 #define count_years_decs(time, decs) sprintf(CTX(_("CI_DEC^%s years")), ftos_decimals(time, decs))
 #define count_years(time) \
        count_fill(time, \
-       CTX(_("CI_ZER^%d years")), /* zeroth */ \
-       CTX(_("CI_FIR^%d year")),  /* first */ \
-       CTX(_("CI_SEC^%d years")), /* year */ \
-       CTX(_("CI_THI^%d years")), /* third */ \
-       CTX(_("CI_MUL^%d years"))) /* multi */
+       _("CI_ZER^%d years"), /* zeroth */ \
+       _("CI_FIR^%d year"),  /* first */ \
+       _("CI_SEC^%d years"), /* year */ \
+       _("CI_THI^%d years"), /* third */ \
+       _("CI_MUL^%d years")) /* multi */
 
 #define count_weeks_decs(time, decs) sprintf(CTX(_("CI_DEC^%s weeks")), ftos_decimals(time, decs))
 #define count_weeks(time) \
        count_fill(time, \
-       CTX(_("CI_ZER^%d weeks")), /* zeroth */ \
-       CTX(_("CI_FIR^%d week")),  /* first */ \
-       CTX(_("CI_SEC^%d weeks")), /* week */ \
-       CTX(_("CI_THI^%d weeks")), /* third */ \
-       CTX(_("CI_MUL^%d weeks"))) /* multi */
+       _("CI_ZER^%d weeks"), /* zeroth */ \
+       _("CI_FIR^%d week"),  /* first */ \
+       _("CI_SEC^%d weeks"), /* week */ \
+       _("CI_THI^%d weeks"), /* third */ \
+       _("CI_MUL^%d weeks")) /* multi */
 
 #define count_days_decs(time, decs) sprintf(CTX(_("CI_DEC^%s days")), ftos_decimals(time, decs))
 #define count_days(time) \
        count_fill(time, \
-       CTX(_("CI_ZER^%d days")), /* zeroth */ \
-       CTX(_("CI_FIR^%d day")),  /* first */ \
-       CTX(_("CI_SEC^%d days")), /* day */ \
-       CTX(_("CI_THI^%d days")), /* third */ \
-       CTX(_("CI_MUL^%d days"))) /* multi */
+       _("CI_ZER^%d days"), /* zeroth */ \
+       _("CI_FIR^%d day"),  /* first */ \
+       _("CI_SEC^%d days"), /* day */ \
+       _("CI_THI^%d days"), /* third */ \
+       _("CI_MUL^%d days")) /* multi */
 
 #define count_hours_decs(time, decs) sprintf(CTX(_("CI_DEC^%s hours")), ftos_decimals(time, decs))
 #define count_hours(time) \
        count_fill(time, \
-       CTX(_("CI_ZER^%d hours")), /* zeroth */ \
-       CTX(_("CI_FIR^%d hour")),  /* first */ \
-       CTX(_("CI_SEC^%d hours")), /* hour */ \
-       CTX(_("CI_THI^%d hours")), /* third */ \
-       CTX(_("CI_MUL^%d hours"))) /* multi */
+       _("CI_ZER^%d hours"), /* zeroth */ \
+       _("CI_FIR^%d hour"),  /* first */ \
+       _("CI_SEC^%d hours"), /* hour */ \
+       _("CI_THI^%d hours"), /* third */ \
+       _("CI_MUL^%d hours")) /* multi */
 
 
 #define count_minutes_decs(time, decs) sprintf(CTX(_("CI_DEC^%s minutes")), ftos_decimals(time, decs))
 #define count_minutes(time) \
        count_fill(time, \
-       CTX(_("CI_ZER^%d minutes")), /* zeroth */ \
-       CTX(_("CI_FIR^%d minute")),  /* first */ \
-       CTX(_("CI_SEC^%d minutes")), /* minute */ \
-       CTX(_("CI_THI^%d minutes")), /* third */ \
-       CTX(_("CI_MUL^%d minutes"))) /* multi */
+       _("CI_ZER^%d minutes"), /* zeroth */ \
+       _("CI_FIR^%d minute"),  /* first */ \
+       _("CI_SEC^%d minutes"), /* minute */ \
+       _("CI_THI^%d minutes"), /* third */ \
+       _("CI_MUL^%d minutes")) /* multi */
 
 #define count_seconds_decs(time, decs) sprintf(CTX(_("CI_DEC^%s seconds")), ftos_decimals(time, decs))
 #define count_seconds(time) \
        count_fill(time, \
-       CTX(_("CI_ZER^%d seconds")), /* zeroth */ \
-       CTX(_("CI_FIR^%d second")),  /* first */ \
-       CTX(_("CI_SEC^%d seconds")), /* second */ \
-       CTX(_("CI_THI^%d seconds")), /* third */ \
-       CTX(_("CI_MUL^%d seconds"))) /* multi */
+       _("CI_ZER^%d seconds"), /* zeroth */ \
+       _("CI_FIR^%d second"),  /* first */ \
+       _("CI_SEC^%d seconds"), /* second */ \
+       _("CI_THI^%d seconds"), /* third */ \
+       _("CI_MUL^%d seconds")) /* multi */
 
 string count_ordinal(int interval)
 {
@@ -102,16 +102,16 @@ string count_fill(float interval, string zeroth, string first, string second, st
 
        switch (floor(interval))
        {
-               case 0: return sprintf(zeroth, interval);
+               case 0: return sprintf(CTX(zeroth), interval);
                case 1:
                {
                        if (interval == 1)  // EXACTLY value of 1
-                               return sprintf(first, interval);
-                       else return sprintf(multi, interval);
+                               return sprintf(CTX(first), interval);
+                       else return sprintf(CTX(multi), interval);
                }
-               case 2: return sprintf(second, interval);
-               case 3: return sprintf(third, interval);
-               default: return sprintf(multi, interval);
+               case 2: return sprintf(CTX(second), interval);
+               case 3: return sprintf(CTX(third), interval);
+               default: return sprintf(CTX(multi), interval);
        }
        return "";
 }
index a2012c8fa2eea8f14c2b223b2b3fb95f834457d9..cd9569e501008be7c8f1947edf944365fd8f8ddb 100644 (file)
@@ -1,5 +1,13 @@
 // generated file; do not modify
-#include <lib/csqcmodel/cl_model.qc>
-#include <lib/csqcmodel/cl_player.qc>
 #include <lib/csqcmodel/interpolate.qc>
-#include <lib/csqcmodel/sv_model.qc>
+#include <lib/csqcmodel/model.qc>
+#ifdef CSQC
+    #include <lib/csqcmodel/cl_model.qc>
+#endif
+#ifdef SVQC
+    #include <lib/csqcmodel/sv_model.qc>
+#endif
+#include <lib/csqcmodel/player.qc>
+#ifdef CSQC
+    #include <lib/csqcmodel/cl_player.qc>
+#endif
index de3102fa855386521886d9fdb7dad640260fabd4..1b05351928a8cc8c8b06337093a286fa4157ed21 100644 (file)
@@ -1,5 +1,13 @@
 // generated file; do not modify
-#include <lib/csqcmodel/cl_model.qh>
-#include <lib/csqcmodel/cl_player.qh>
 #include <lib/csqcmodel/interpolate.qh>
-#include <lib/csqcmodel/sv_model.qh>
+#include <lib/csqcmodel/model.qh>
+#ifdef CSQC
+    #include <lib/csqcmodel/cl_model.qh>
+#endif
+#ifdef SVQC
+    #include <lib/csqcmodel/sv_model.qh>
+#endif
+#include <lib/csqcmodel/player.qh>
+#ifdef CSQC
+    #include <lib/csqcmodel/cl_player.qh>
+#endif
index 789f115d14ac960b3c361bac06ce8229e7960e0b..e4e1d536aa85eb84175ec0e3350d3f14678f86ba 100644 (file)
@@ -194,6 +194,14 @@ void CSQCModel_Draw(entity this)
 
        CSQCModel_Hook_PreDraw(this, isplayer);
 
+       if(isplayer)
+       {
+               if(this.entnum == player_localentnum)
+                       this.renderflags |= RF_EXTERNALMODEL;
+               else
+                       this.renderflags &= ~RF_EXTERNALMODEL;
+       }
+
        // inherit draw flags easily
        entity root = this;
        while(root.tag_entity)
@@ -267,6 +275,9 @@ NET_HANDLE(ENT_CLIENT_MODEL, bool isnew)
                this.csqcmodel_teleported = 1;
        }
 
+       if(sf & BIT(14))
+               viewloc_SetTags(this);
+
        CSQCModel_InterpolateAnimation_Note(this, sf);
        InterpolateOrigin_Note(this);
        CSQCPlayer_PostUpdate(this);
@@ -300,6 +311,6 @@ entity CSQCModel_server2csqc(int i)
 {
        if (i < maxclients) return CSQCModel_players[i];
        ++i;
-       LOG_DEBUGF("player out of bounds: %d\n", i);
+       LOG_DEBUGF("player out of bounds: %d", i);
        return findfloat(NULL, entnum, i);
 }
index 3b20972e25db6e1706aa4a59b944d0a65e71dfaa..b5d9f8bd361fff927dc54f70a5632300a64d4360 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_CL_MODEL_H
-#define LIB_CSQCMODEL_CL_MODEL_H
+#pragma once
 
 #include "common.qh"
 
@@ -50,4 +49,3 @@ void CSQCModel_InterpolateAnimation_1To2_Note(entity this, int sf, float set_tim
 void CSQCModel_InterpolateAnimation_2To4_Do(entity this);
 void CSQCModel_InterpolateAnimation_1To2_Do(entity this);
 // will overwrite lerpfrac, lerpfrac3, lerpfrac4, and possibly clear frame*time if they are undisplayed according to lerpfracs
-#endif
index 0b0de75649aab42d97851711fca47dd1342c8bf8..9e26e70fb7424ff07d3fbf79747bffe218c1dd4f 100644 (file)
@@ -28,6 +28,7 @@
 #include <client/defs.qh>
 #include <client/main.qh>
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/physics/player.qh>
 #include <common/stats.qh>
 #include <common/triggers/trigger/viewloc.qh>
@@ -35,6 +36,7 @@
 #include <common/viewloc.qh>
 
 float autocvar_cl_movement_errorcompensation = 0;
+bool autocvar_cl_movement_intermissionrunning = false;
 
 // engine stuff
 float pmove_onground; // weird engine flag we shouldn't really use but have to for now
@@ -96,7 +98,7 @@ void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff)
 void CSQCPlayer_Unpredict(entity this)
 {
        if (csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED) return;
-       if (csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED) LOG_FATALF("Cannot unpredict in current status (%d)\n", csqcplayer_status);
+       if (csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED) LOG_FATALF("Cannot unpredict in current status (%d)", csqcplayer_status);
        this.origin = csqcplayer_origin;
        this.velocity = csqcplayer_velocity;
        csqcplayer_moveframe = csqcplayer_sequence + 1; // + 1 because the recieved frame has the move already done (server side)
@@ -105,17 +107,17 @@ void CSQCPlayer_Unpredict(entity this)
 
 void CSQCPlayer_SetMinsMaxs(entity this)
 {
-       if ((this.flags & FL_DUCKED) || !this.isplayermodel)
+       if (IS_DUCKED(this) || !this.isplayermodel)
        {
-               this.mins = STAT(PL_CROUCH_MIN, NULL);
-               this.maxs = STAT(PL_CROUCH_MAX, NULL);
-               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, NULL);
+               this.mins = STAT(PL_CROUCH_MIN, this);
+               this.maxs = STAT(PL_CROUCH_MAX, this);
+               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
        }
        else
        {
-               this.mins = STAT(PL_MIN, NULL);
-               this.maxs = STAT(PL_MAX, NULL);
-               this.view_ofs = STAT(PL_VIEW_OFS, NULL);
+               this.mins = STAT(PL_MIN, this);
+               this.maxs = STAT(PL_MAX, this);
+               this.view_ofs = STAT(PL_VIEW_OFS, this);
        }
 }
 
@@ -129,72 +131,31 @@ void CSQCPlayer_SavePrediction(entity this)
 }
 
 void CSQC_ClientMovement_PlayerMove_Frame(entity this);
-void _Movetype_Physics_ClientFrame(entity this, float movedt);
-
-void Movetype_Physics_Spam(entity this)  // optimized
-{
-       _Movetype_Physics_ClientFrame(this, PHYS_INPUT_TIMELENGTH);
-       if(wasfreed(this))
-               return;
-
-       this.avelocity = this.move_avelocity;
-       this.velocity = this.move_velocity;
-       this.angles = this.move_angles;
-       this.flags = BITSET(this.flags, FL_ONGROUND, boolean(this.move_flags & FL_ONGROUND));
-       this.flags = BITSET(this.flags, FL_WATERJUMP, boolean(this.move_flags & FL_WATERJUMP));
-       this.waterlevel = this.move_waterlevel;
-       this.watertype = this.move_watertype;
-       setorigin(this, this.move_origin);
-}
-
-void CSQCPlayer_CheckWater(entity this)
-{
-       this.move_origin = this.origin;
-       this.move_waterlevel = this.waterlevel;
-       this.move_watertype = this.watertype;
-       _Movetype_CheckWater(this);
-       this.waterlevel = this.move_waterlevel;
-       this.watertype = this.move_watertype;
-}
 
 void CSQCPlayer_Physics(entity this)
 {
-       if(autocvar_cl_movement)
-       {
-               if(autocvar_cl_movement == 1)
-                       CSQCPlayer_CheckWater(this); // we apparently need to check water *before* physics so it can use this for water jump
+       if(!autocvar_cl_movement) { return; }
 
-               vector oldv_angle = this.v_angle;
-               vector oldangles = this.angles; // we need to save these, as they're abused by other code
-               this.v_angle = PHYS_INPUT_ANGLES(this);
-               this.angles = PHYS_WORLD_ANGLES(this);
+       _Movetype_CheckWater(this); // we apparently need to check water *before* physics so it can use this for water jump
 
-               CSQC_ClientMovement_PlayerMove_Frame(this);
+       vector oldv_angle = this.v_angle;
+       vector oldangles = this.angles; // we need to save these, as they're abused by other code
+       this.v_angle = PHYS_INPUT_ANGLES(this);
+       this.angles = PHYS_WORLD_ANGLES(this);
 
-               if(autocvar_cl_movement == 1)
-               {
-                       this.move_origin = this.origin;
-                       this.move_angles = this.angles;
-                       //this.move_movetype = MOVETYPE_WALK; // temp
-                       this.move_velocity = this.velocity;
-                       this.move_avelocity = this.avelocity;
-                       this.move_flags = BITSET(this.move_flags, FL_ONGROUND, IS_ONGROUND(this));
-                       this.move_flags = BITSET(this.move_flags, FL_WATERJUMP, boolean(this.flags & FL_WATERJUMP));
-                       this.move_waterlevel = this.waterlevel;
-                       this.move_watertype = this.watertype;
-                       Movetype_Physics_Spam(this);
-               }
+       CSQC_ClientMovement_PlayerMove_Frame(this);
 
-               view_angles = this.v_angle;
-               input_angles = this.angles;
-               this.v_angle = oldv_angle;
-               this.angles = oldangles;
+       Movetype_Physics_NoMatchTicrate(this, PHYS_INPUT_TIMELENGTH, true);
 
-               this.pmove_flags =
-                               ((this.flags & FL_DUCKED) ? PMF_DUCKED : 0) |
-                               (!(this.flags & FL_JUMPRELEASED) ? PMF_JUMP_HELD : 0) |
-                               ((IS_ONGROUND(this)) ? PMF_ONGROUND : 0);
-       }
+       view_angles = this.v_angle;
+       input_angles = this.angles;
+       this.v_angle = oldv_angle;
+       this.angles = oldangles;
+
+       this.pmove_flags =
+                       ((IS_DUCKED(this)) ? PMF_DUCKED : 0) |
+                       ((IS_JUMP_HELD(this)) ? PMF_JUMP_HELD : 0) |
+                       ((IS_ONGROUND(this)) ? PMF_ONGROUND : 0);
 }
 
 void CSQCPlayer_PredictTo(entity this, float endframe, bool apply_error)
@@ -230,6 +191,15 @@ void CSQCPlayer_PredictTo(entity this, float endframe, bool apply_error)
                do
                {
                        if (!getinputstate(csqcplayer_moveframe)) break;
+                       /*if (input_timelength > 0.0005)
+                       {
+                               if (input_timelength > 0.05)
+                               {
+                                       input_timelength /= 2;
+                                       CSQCPlayer_Physics(this);
+                               }
+                               CSQCPlayer_Physics(this);
+                       }*/
                        CSQCPlayer_Physics(this);
                        CSQCPlayer_SetMinsMaxs(this);
                        ++csqcplayer_moveframe;
@@ -254,10 +224,10 @@ void CSQCPlayer_SetViewLocation()
 /** Called once per CSQC_UpdateView() */
 void CSQCPlayer_SetCamera()
 {
-       const vector v0 = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
+       const vector v0 = ((intermission && !autocvar_cl_movement_intermissionrunning) ? '0 0 0' : pmove_vel); // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
        const float vh = STAT(VIEWHEIGHT);
-       const vector pl_viewofs = STAT(PL_VIEW_OFS, NULL);
-       const vector pl_viewofs_crouch = STAT(PL_CROUCH_VIEW_OFS, NULL);
+       const vector pl_viewofs = STAT(PL_VIEW_OFS);
+       const vector pl_viewofs_crouch = STAT(PL_CROUCH_VIEW_OFS);
        const entity e = csqcplayer;
        if (e)
        {
index 1c97199c8e29684225649a78f116a9abca857778..297e2e69d648c1826289fa21c95e389f5a61b5f2 100644 (file)
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_CL_PLAYER_H
-#define LIB_CSQCMODEL_CL_PLAYER_H
+#pragma once
 
-int autocvar_cl_movement = 1;
+bool autocvar_cl_movement = true;
 
 entity csqcplayer;
 float csqcplayer_status;
@@ -42,4 +41,3 @@ void CSQCPlayer_SetCamera();
 float CSQCPlayer_PreUpdate(entity this);
 float CSQCPlayer_PostUpdate(entity this);
 float CSQCPlayer_IsLocalPlayer(entity this);
-#endif
index a700f3af2b9bda88665508f3456a0bd066c6f613..37b88997ef84bf7d456e34575fc0859d79f01442 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_COMMON_H
-#define LIB_CSQCMODEL_COMMON_H
+#pragma once
 
 #include <common/csqcmodel_settings.qh>
 
@@ -94,4 +93,3 @@ const int CSQCMODEL_PROPERTY_SIZE = BIT(15);
 #else
 #define ALLPROPERTIES ALLPROPERTIES_COMMON
 #endif
-#endif
index 2f4828053b0b98e73ec890398374aa3cd86618b6..3ea385cbcd8b5e4918c1f88ed8b3c6eaed9508d4 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_INTERPOLATE_H
-#define LIB_CSQCMODEL_INTERPOLATE_H
+#pragma once
 
 .int iflags;
 const int IFLAG_VELOCITY = BIT(0);
@@ -49,4 +48,3 @@ void InterpolateOrigin_Do(entity this);
 
 // in case we interpolate that:
 .vector v_angle;
-#endif
diff --git a/qcsrc/lib/csqcmodel/model.qc b/qcsrc/lib/csqcmodel/model.qc
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/qcsrc/lib/csqcmodel/model.qh b/qcsrc/lib/csqcmodel/model.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/lib/csqcmodel/player.qc b/qcsrc/lib/csqcmodel/player.qc
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/qcsrc/lib/csqcmodel/player.qh b/qcsrc/lib/csqcmodel/player.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 85b8a4fcbb80580059728230af0915e0096569b5..359cf1738f01666e5f9902a7cad17980bf14d194 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_CSQCMODEL_SETTINGS_H
-#define LIB_CSQCMODEL_SETTINGS_H
+#pragma once
 // define this if svqc code wants to use .frame2 and .lerpfrac
 //#define CSQCMODEL_HAVE_TWO_FRAMES
 
@@ -24,4 +23,3 @@
 //vector PL_CROUCH_MIN  = ...;
 //vector PL_CROUCH_MAX  = ...;
 //vector PL_CROUCH_VIEW_OFS  = ...;
-#endif
index d495ae0154e58bd297d3de76de2c4eb7c8aed761..dd8d5d33aaee2a8f479cd6edcc34f9047adf6730 100644 (file)
@@ -24,6 +24,7 @@
 #include "common.qh"
 #include <common/animdecide.qh>
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/util.qh>
 #include <server/constants.qh>
 #include <server/defs.qh>
@@ -64,13 +65,9 @@ bool CSQCModel_Send(entity this, entity to, int sf)
 void CSQCModel_CheckUpdate(entity e)
 {
        // some nice flags for CSQCMODEL_IF
-       float isplayer = IS_CLIENT(e);
-       float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
-       float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
-
-       unused_float = isplayer;
-       unused_float = islocalplayer;
-       unused_float = isnolocalplayer;
+       noref float isplayer = IS_CLIENT(e);
+       noref float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
+       noref float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
 
 #if CSQCPLAYER_FORCE_UPDATES
        if(isplayer && time > e.csqcmodel_nextforcedupdate)
index 3e043d3532130748d44e5900ec6c253b435fd013..e963f12a1bf6ac79d9d3e45bf941e10e92927fef 100644 (file)
@@ -19,8 +19,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef LIB_CSQCMODEL_SV_MODEL_H
-#define LIB_CSQCMODEL_SV_MODEL_H
+#pragma once
 
 #include "common.qh"
 
@@ -41,4 +40,3 @@ void CSQCModel_UnlinkEntity(entity e);
 #undef CSQCMODEL_PROPERTY
 #undef CSQCMODEL_ENDIF
 #undef CSQCMODEL_IF
-#endif
index 3e7e020c67a5fea2ea7368fb3444a30f9fbebea2..4f34bb4853ade7f0149274ebdb3c325d4468147a 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 
-#ifndef MENUQC
+#ifdef GAMEQC
 
        #include "oo.qh"
        #include "self.qh"
@@ -12,7 +12,7 @@
        /** Remove entity */
        void SUB_Remove(entity this)
        {
-               remove(this);
+               delete(this);
        }
 
        void defer_think(entity this)
index 698c9262370974781c1f0585caaba18103d19ff7..fbb4a09b5510705f71d43a0eb461ded50ece0d0d 100644 (file)
@@ -5,7 +5,7 @@
        #include "i18n.qh"
        #include "vector.qh"
 
-       #include <client/defs.qh>
+       float vid_conwidth;
 
        void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg)
        {
index c66d7b9a88bd8a163d20d5a13cef85132102892c..8ebedb76f65f66110d3fd26ffaa46277e1b24021 100644 (file)
@@ -49,7 +49,7 @@ string CTX(string s)
        int p = strstrofs(s, "^", 0);
        string ret = (p < 0) ? s : substring(s, p + 1, -1);
 #if CTX_CACHE
-        LOG_DEBUGF("CTX(\"%s\")\n", s);
+        LOG_DEBUGF("CTX(\"%s\")", s);
                HM_sets(CTX_cache, s, ret);
 #endif
        return ret;
diff --git a/qcsrc/lib/intrusivelist.qh b/qcsrc/lib/intrusivelist.qh
new file mode 100644 (file)
index 0000000..9d3ec54
--- /dev/null
@@ -0,0 +1,245 @@
+#pragma once
+
+#include "iter.qh"
+
+const int IL_MAX = 128;
+
+void IL_INIT(entity this);
+void IL_DTOR(entity this);
+void IL_ENDFRAME();
+
+/**
+ * limitations:
+ *   NULL cannot be present
+ *   elements can only be present once
+ *   a maximum of `IL_MAX` lists can exist at one time
+ *   freed entities must be removed from the list
+ */
+CLASS(IntrusiveList, Object)
+       ATTRIB(IntrusiveList, il_head, entity);
+       ATTRIB(IntrusiveList, il_tail, entity);
+       ATTRIB(IntrusiveList, il_nextfld, .entity, nil);
+       ATTRIB(IntrusiveList, il_prevfld, .entity, nil);
+       INIT(IntrusiveList) { IL_INIT(this); }
+       DESTRUCTOR(IntrusiveList) { IL_DTOR(this); }
+ENDCLASS(IntrusiveList)
+
+// bitflags
+.vector il_lists;
+// bitflags
+.vector il_listmask;
+
+#define IL_NEW() NEW(IntrusiveList)
+
+#define IL_EMPTY(this) (this.il_head == NULL)
+
+#define IL_FIRST(this) (this.il_head)
+#define IL_LAST(this) (this.il_tail)
+#define IL_PEEK(this) (this.il_tail)
+
+bool IL_CONTAINS(IntrusiveList this, entity it)
+{
+       assert(this, return false);
+       return it.(this.il_nextfld) || this.il_head == it || this.il_tail == it;
+}
+
+/**
+ * Push to tail
+ */
+entity IL_PUSH(IntrusiveList this, entity it)
+{
+       assert(this, return NULL);
+       assert(it, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+       assert(!IL_CONTAINS(this, it), return NULL);
+
+       entity tail = it.(il_prev) = this.il_tail;
+       tail ? (tail.(il_next) = it) : this.il_head = it;
+       this.il_tail = it;
+       it.il_lists |= this.il_listmask;
+       return it;
+}
+
+/**
+ * Push to head
+ */
+entity IL_UNSHIFT(IntrusiveList this, entity it)
+{
+       assert(this, return NULL);
+       assert(it, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+       assert(!IL_CONTAINS(this, it), return NULL);
+
+       entity head = it.(il_next) = this.il_head;
+       head ? (head.(il_prev) = it) : this.il_tail = it;
+       this.il_head = it;
+       it.il_lists |= this.il_listmask;
+       return it;
+}
+
+/**
+ * Pop from tail
+ */
+entity IL_POP(IntrusiveList this)
+{
+       assert(this, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+
+       if (!this.il_tail) return NULL;
+       entity it = this.il_tail;
+       entity prev = it.(il_prev);
+       if (prev) (this.il_tail = prev).(il_next) = NULL;
+       else this.il_head = this.il_tail = NULL;
+       return it;
+}
+
+/**
+ * Pop from head
+ */
+entity IL_SHIFT(IntrusiveList this)
+{
+       assert(this, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+
+       if (!this.il_head) return NULL;
+       entity it = this.il_head;
+       entity next = it.(il_next);
+       if (next) (this.il_head = next).(il_prev) = NULL;
+       else this.il_head = this.il_tail = NULL;
+       return it;
+}
+
+/**
+ * Remove any element, anywhere in the list
+ */
+void IL_REMOVE(IntrusiveList this, entity it)
+{
+       assert(this, return);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+       entity next = it.(il_next);
+       entity prev = it.(il_prev);
+       entity ohead = this.il_head;
+       entity otail = this.il_tail;
+       next ? next.(il_prev) = prev : this.il_tail = prev;
+       prev ? prev.(il_next) = next : this.il_head = next;
+       LOG_DEBUGF("remove %i (%i :: %i), head: %i -> %i, tail: %i -> %i", it, it.(il_prev), it.(il_next), ohead, this.il_head, otail, this.il_tail);
+       it.(il_next) = it.(il_prev) = NULL;
+}
+
+/**
+ * Remove all elements
+ */
+#define IL_CLEAR(this) \
+       MACRO_BEGIN \
+       { \
+               IntrusiveList __il = this; \
+               assert(__il); \
+               .entity il_prev = __il.il_prevfld; \
+               IL_EACH(__il, true, it.(il_next) = it.(il_prev) = NULL); \
+               __il.il_head = __il.il_tail = NULL; \
+       } MACRO_END
+
+/**
+ * Delete the list
+ */
+#define IL_DELETE(this) \
+       MACRO_BEGIN \
+       { \
+               delete(this); \
+               this = NULL; \
+       } MACRO_END
+
+#define IL_EACH(this, cond, body) \
+       MACRO_BEGIN \
+       { \
+               IntrusiveList _il = this; \
+               assert(_il); \
+               .entity il_next = _il.il_nextfld; \
+               noref int i = 0; \
+               for (entity _next, _it = _il.il_head; _it; (_it = _next, ++i)) \
+               { \
+                       const noref entity it = _it; \
+                       _next = it.(il_next); \
+                       if (cond) { LAMBDA(body) } \
+               } \
+       } MACRO_END
+
+.int il_id;
+IntrusiveList il_links[IL_MAX];
+.entity il_links_flds[IL_MAX * 2];
+int il_links_ptr;
+
+#define IL_FLOOR(n) ((n) | 0)
+#define IL_CEIL(n)  IL_FLOOR((n) + 0.5)
+
+#define IL_LISTS_PER_BIT IL_CEIL(IL_MAX / (3 * 24))
+
+void IL_INIT(IntrusiveList this)
+{
+       .entity nextfld, prevfld;
+       for (int i = il_links_ptr; i < il_links_ptr + IL_MAX; ++i) {
+               int idx = i;
+               if (idx >= IL_MAX) idx -= IL_MAX;
+               int id = idx;
+               idx *= 2;
+               if (!il_links[idx]) {
+                       il_links[idx] = this;
+                       nextfld = il_links_flds[idx + 0];
+                       prevfld = il_links_flds[idx + 1];
+                       this.il_id = id;
+                       int bit = IL_FLOOR(id / IL_LISTS_PER_BIT);
+                       if (bit < (1 * 24)) this.il_listmask = '1 0 0' * (1 << (bit - (0 * 24)));
+                       else if (bit < (2 * 24)) this.il_listmask = '0 1 0' * (1 << (bit - (1 * 24)));
+                       else if (bit < (3 * 24)) this.il_listmask = '0 0 1' * (1 << (bit - (2 * 24)));
+                       else assert(false);
+                       il_links_ptr = id + 1;
+                       if (il_links_ptr >= IL_MAX) il_links_ptr -= IL_MAX;
+                       this.il_nextfld = nextfld;
+                       this.il_prevfld = prevfld;
+                       return;
+               }
+       }
+       LOG_WARNF("IntrusiveList overflow");
+}
+
+void IL_DTOR(IntrusiveList this)
+{
+       IL_CLEAR(this);
+       il_links[this.il_id] = NULL;
+}
+
+void IL_ENDFRAME()
+{
+#if 0
+       // incompatible with CSQC, remove() clears entities
+       for (int i = 0; i < IL_MAX; ++i) {
+               IntrusiveList list = il_links[i];
+               if (list) {
+                       .entity nextfld = list.il_nextfld;
+                       for (entity next, it = list.il_head; it; it = next) {
+                               next = it.(nextfld);
+                               if (wasfreed(it)) {
+                                       IL_REMOVE(list, it);
+                               }
+                       }
+               }
+       }
+#endif
+}
+
+void ONREMOVE(entity this)
+{
+       if (this.il_lists) {
+               for (int i = 0; i < IL_MAX; ++i) {
+                       IntrusiveList list = il_links[i];
+                       if (this.il_lists & list.il_listmask && IL_CONTAINS(list, this)) {
+                               IL_REMOVE(list, this);
+                       }
+               }
+       }
+}
index c21d02121347d34b79042a31809ccd421693ab3f..e3cf7410fba38b7177f6d6ee6c928c5f9e7ce122 100644 (file)
        MACRO_BEGIN \
        { \
                int _i = 0; \
-               for (entity _it = list##_first; _it; (_it = _it.next, ++_i)) \
+               for (entity _it = list##_first, _next = NULL; _it; (_it = _next, ++_i)) \
                { \
                        const noref int i = _i; \
                        ITER_CONST noref entity it = _it; \
+                       _next = _it.next; \
                        if (cond) { LAMBDA(body) } \
                } \
        } MACRO_END
             if (cond) LAMBDA(body) \
         } \
     } MACRO_END
+#define MUTEX_LOCK(this) MACRO_BEGIN \
+       if (this) LOG_SEVEREF("Loop mutex held by %s", this); \
+       this = __FUNC__; \
+MACRO_END
+#define MUTEX_UNLOCK(this) MACRO_BEGIN \
+       this = string_null; \
+MACRO_END
 #define _FOREACH_ENTITY_FIND_UNORDERED(id, T, fld, match, cond, body) \
        MACRO_BEGIN { \
-               if (_FOREACH_ENTITY_FIND_##T##_##id##mutex) LOG_SEVEREF("Loop mutex held by %s", _FOREACH_ENTITY_FIND_##T##_##id##mutex); \
-               _FOREACH_ENTITY_FIND_##T##_##id##mutex = __FUNC__; \
+               MUTEX_LOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \
                entity _foundchain_first = _findchain##T##_tofield(fld, match, _FOREACH_ENTITY_FIND_##T##_next##id); \
                FOREACH_LIST(_foundchain, _FOREACH_ENTITY_FIND_##T##_next##id, cond, body); \
-               _FOREACH_ENTITY_FIND_##T##_##id##mutex = string_null; \
+               MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \
        } MACRO_END
 
 #define FOREACH_ENTITY(cond, body) ORDERED(FOREACH_ENTITY)(cond, body)
 .entity _FOREACH_ENTITY_FIND_flags_next; noref string _FOREACH_ENTITY_FIND_flags_mutex;
 #define FOREACH_ENTITY_FLAGS_UNORDERED(fld, match, body) _FOREACH_ENTITY_FIND_UNORDERED(, flags, fld, match, true, body)
 
-#ifndef MENUQC
+#ifdef GAMEQC
 entity(vector org, float rad, .entity tofield) _findchainradius_tofield = #22;
-#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body)
+#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) ORDERED(FOREACH_ENTITY_RADIUS)(org, dist, cond, body)
 .entity _FOREACH_ENTITY_FIND_radius_next; noref string _FOREACH_ENTITY_FIND_radius_mutex;
 #define FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body) _FOREACH_ENTITY_FIND_UNORDERED(, radius, org, dist, cond, body)
+.entity _FOREACH_ENTITY_FIND_radius_nexttmp; noref string _FOREACH_ENTITY_FIND_radius_tmpmutex;
+#define FOREACH_ENTITY_RADIUS_ORDERED(org, dist, cond, body) \
+MACRO_BEGIN \
+       entity _rev_first = NULL; \
+       _FOREACH_ENTITY_FIND_UNORDERED(tmp, radius, org, dist, cond, (it._FOREACH_ENTITY_FIND_radius_nexttmp = _rev_first, _rev_first = it)); \
+       MUTEX_LOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \
+       FOREACH_LIST(_rev, _FOREACH_ENTITY_FIND_radius_nexttmp, true, body); \
+       MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \
+MACRO_END
 #endif
 
 #define FOREACH_ENTITY_FLOAT(fld, match, body) ORDERED(FOREACH_ENTITY_FLOAT)(fld, match, body)
diff --git a/qcsrc/lib/json.qh b/qcsrc/lib/json.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index ad30115a2e74c3fb00e25564cfe0e998816d42a9..62d04b32e909bb741b55a74f77c37f9bccfa084c 100644 (file)
@@ -3,7 +3,7 @@
 #include "oo.qh"
 
 CLASS(Lazy, Object)
-       ATTRIB(Lazy, m_get, entity(), func_null);
+       ATTRIB(Lazy, m_get, entity());
        CONSTRUCTOR(Lazy, entity() _compute)
        {
                this.m_get = _compute;
index 6afc862ed0a6306b97e2ad5ca02702b3a79a4817..0dd430722f755218f5f796883a5dcf4317caf885 100644 (file)
@@ -1,14 +1,14 @@
 #pragma once
 
 CLASS(LinkedListNode, Object)
-       ATTRIB(LinkedListNode, ll_data, entity, NULL)
-       ATTRIB(LinkedListNode, ll_prev, LinkedListNode, NULL)
-       ATTRIB(LinkedListNode, ll_next, LinkedListNode, NULL)
+       ATTRIB(LinkedListNode, ll_data, entity);
+       ATTRIB(LinkedListNode, ll_prev, LinkedListNode);
+       ATTRIB(LinkedListNode, ll_next, LinkedListNode);
 ENDCLASS(LinkedListNode)
 
 CLASS(LinkedList, Object)
-       ATTRIB(LinkedList, ll_head, LinkedListNode, NULL);
-       ATTRIB(LinkedList, ll_tail, LinkedListNode, NULL);
+       ATTRIB(LinkedList, ll_head, LinkedListNode);
+       ATTRIB(LinkedList, ll_tail, LinkedListNode);
 ENDCLASS(LinkedList)
 
 #define LL_NEW() NEW(LinkedList)
@@ -40,7 +40,7 @@ entity LL_POP(LinkedList this)
        LinkedListNode prev = n.ll_prev;
        if (prev) (this.ll_tail = prev).ll_next = NULL;
        else this.ll_head = this.ll_tail = NULL;
-       remove(n);
+       delete(n);
        return e;
 }
 
@@ -57,7 +57,7 @@ entity LL_POP(LinkedList this)
                        entity it = LL_POP(_ll); \
                        if (!it) continue; \
                        dtor \
-                       remove(it); \
+                       delete(it); \
                } \
        } MACRO_END
 
@@ -68,7 +68,7 @@ entity LL_POP(LinkedList this)
        MACRO_BEGIN \
        { \
                LL_CLEAR_2(this, dtor); \
-               remove(this); \
+               delete(this); \
                this = NULL; \
        } MACRO_END
 
index 370d96be5629f2fdc3e6a26b767799a7c1c2c7b4..7f5bc720412348f2dcc8df053027f4de84f3e1b1 100644 (file)
 
 
 #if defined(MENUQC)
-string(string...) strcat0n = #53;
+string(string, string...) strcat1n = #53;
 #else
-string(string...) strcat0n = #115;
+string(string, string...) strcat1n = #115;
 #endif
 
-#define __SOURCELOC__ (sprintf("^7%s^9" "(" "^9"__FILE__"^7"  ":"  "^9"STR(__LINE__)"^7" ")", __FUNC__))
+// would be nice if __FUNC__ could be concatenated at compile time
+#if 0
+       // less work, bigger binary
+       #define __SOURCELOC__ (sprintf("^7%s^9" "(" "^9"__FILE__"^7"  ":"  "^9"STR(__LINE__)"^7" ")", __FUNC__))
+#else
+       #define __SOURCELOC__ (sprintf("^7%s^9" "(" "^9%s^7"  ":"  "^9%s^7" ")", __FUNC__, __FILE__, STR(__LINE__)))
+#endif
 
+#define _LOG_HEADER(level) "^9[::" "^7"PROGNAME"^9" "::" level"^9" "] ", __SOURCELOC__
 #define _LOG(f, level, s) \
        MACRO_BEGIN { \
-               f(sprintf("^9[::" "^7"PROGNAME"^9" "::" level"^9" "] %s\n^7%s\n", __SOURCELOC__, s)); \
+               f(strcat1n(_LOG_HEADER(level), "\n^7", s, "\n")); \
        } MACRO_END
 
-#define  LOG_FATAL(...) _LOG_FATAL(strcat0n(__VA_ARGS__))
+#define  LOG_FATAL(...) _LOG_FATAL(strcat1n(__VA_ARGS__))
 #define  LOG_FATALF(...) _LOG_FATAL(sprintf(__VA_ARGS__))
 #define _LOG_FATAL(s) _LOG(error, "^1FATAL", s)
 
-#define  LOG_SEVERE(...) _LOG_SEVERE(strcat0n(__VA_ARGS__))
+#define  LOG_SEVERE(...) _LOG_SEVERE(strcat1n(__VA_ARGS__))
 #define  LOG_SEVEREF(...) _LOG_SEVERE(sprintf(__VA_ARGS__))
 #define _LOG_SEVERE(s) _LOG(backtrace, "^1SEVERE", s)
 
-#define  LOG_WARNING(...) _LOG_WARNING(strcat0n(__VA_ARGS__))
-#define  LOG_WARNINGF(...) _LOG_WARNING(sprintf(__VA_ARGS__))
-#define _LOG_WARNING(s) _LOG(print, "^3WARNING", s)
+#define  LOG_WARN(...) _LOG_WARN(strcat1n(__VA_ARGS__))
+#define  LOG_WARNF(...) _LOG_WARN(sprintf(__VA_ARGS__))
+#define _LOG_WARN(s) _LOG(print, "^3WARNING", s)
 
-#define  LOG_INFO(...) _LOG_INFO(strcat0n(__VA_ARGS__))
+#define  LOG_INFO(...) _LOG_INFO(strcat1n(__VA_ARGS__))
 #define  LOG_INFOF(...) _LOG_INFO(sprintf(__VA_ARGS__))
 #define _LOG_INFO(s) \
        MACRO_BEGIN { \
-               string ___s = s; \
-               if (autocvar_developer) \
-                       _LOG(print, "^5INFO", ___s); \
-               else \
-                       print(___s); \
+               dprint(_LOG_HEADER("^5INFO")); \
+               print("\n^7", s); \
        } MACRO_END
 
-#define  LOG_TRACE(...) _LOG_TRACE(strcat0n(__VA_ARGS__))
+#define  LOG_TRACE(...) _LOG_TRACE(strcat1n(__VA_ARGS__))
 #define  LOG_TRACEF(...) _LOG_TRACE(sprintf(__VA_ARGS__))
 #define _LOG_TRACE(s) _LOG(dprint, "^6TRACE", s)
 
-#define  LOG_DEBUG(...) _LOG_DEBUG(strcat0n(__VA_ARGS__))
+#define  LOG_DEBUG(...) _LOG_DEBUG(strcat1n(__VA_ARGS__))
 #define  LOG_DEBUGF(...) _LOG_DEBUG(sprintf(__VA_ARGS__))
 #define _LOG_DEBUG(s) _LOG(dprint2, "^2DEBUG", s)
 
@@ -82,13 +86,7 @@ string(string...) strcat0n = #115;
        } MACRO_END
 
 // TODO: this sucks, lets find a better way to do backtraces?
-#ifdef SVQC
-       void builtin_remove(entity);
-       #define _backtrace() builtin_remove(NULL)
-#else
-       void remove(entity);
-       #define _backtrace() remove(NULL)
-#endif
+#define _backtrace() builtin_remove(NULL)
 
 noref int autocvar_developer;
 noref bool autocvar_prvm_backtraceforwarnings;
index dff3710a5f3d04a7c114ba1b5c9f612ece4737d8..1541b9997cff5efb80c96fa1a3844c00b6dcc1ba 100644 (file)
@@ -14,3 +14,6 @@
 
 #define _STR(it) #it
 #define STR(it) _STR(it)
+
+#define EMPTY()
+#define DEFER(id) id EMPTY()
index 57b8f8c67939a113fee43e48d3f00b65673eac67..55da4d63f0caab22ec1c305a02ad0ca85822e116 100644 (file)
@@ -9,7 +9,7 @@ void db_save(int db, string filename)
        int fh = fopen(filename, FILE_WRITE);
        if (fh < 0)
        {
-               LOG_WARNINGF("^1Can't write DB to %s\n", filename);
+               LOG_WARNF("^1Can't write DB to %s", filename);
                return;
        }
        fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
@@ -64,7 +64,7 @@ int db_load(string filename)
 void db_dump(int db, string filename)
 {
        int fh = fopen(filename, FILE_WRITE);
-       if (fh < 0) LOG_FATALF("Can't dump DB to %s\n");
+       if (fh < 0) LOG_FATALF("Can't dump DB to %s");
        fputs(fh, "0\n");
        for (int i = 0, n = buf_getsize(db); i < n; ++i)
        {
index feb7a8247455bd84d7e426014bc301f787ac2c6e..92d45bf142573663919fcfe5b6f93f09dc2a8f16 100644 (file)
@@ -82,12 +82,12 @@ vector bezier_quadratic_getderivative(vector a, vector b, vector c, float t)
               + (b - a) * 2;
 }
 
-float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x)
+float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float spd)
 {
        return (((startspeedfactor + endspeedfactor - 2
-                ) * x - 2 * startspeedfactor - endspeedfactor + 3
-               ) * x + startspeedfactor
-              ) * x;
+                ) * spd - 2 * startspeedfactor - endspeedfactor + 3
+               ) * spd + startspeedfactor
+              ) * spd;
 }
 
 bool cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor)
@@ -195,54 +195,54 @@ float power2of(float e)
        return pow(2, e);
 }
 
-float log2of(float x)
+float log2of(float e)
 {
        // NOTE: generated code
-       if (x > 2048)
-               if (x > 131072)
-                       if (x > 1048576)
-                               if (x > 4194304) return 23;
+       if (e > 2048)
+               if (e > 131072)
+                       if (e > 1048576)
+                               if (e > 4194304) return 23;
                                else
-                                       if (x > 2097152) return 22;
+                                       if (e > 2097152) return 22;
                                        else return 21;
                        else
-                               if (x > 524288) return 20;
+                               if (e > 524288) return 20;
                                else
-                                       if (x > 262144) return 19;
+                                       if (e > 262144) return 19;
                                        else return 18;
                else
-                       if (x > 16384)
-                               if (x > 65536) return 17;
+                       if (e > 16384)
+                               if (e > 65536) return 17;
                                else
-                                       if (x > 32768) return 16;
+                                       if (e > 32768) return 16;
                                        else return 15;
                        else
-                               if (x > 8192) return 14;
+                               if (e > 8192) return 14;
                                else
-                                       if (x > 4096) return 13;
+                                       if (e > 4096) return 13;
                                        else return 12;
        else
-               if (x > 32)
-                       if (x > 256)
-                               if (x > 1024) return 11;
+               if (e > 32)
+                       if (e > 256)
+                               if (e > 1024) return 11;
                                else
-                                       if (x > 512) return 10;
+                                       if (e > 512) return 10;
                                        else return 9;
                        else
-                               if (x > 128) return 8;
+                               if (e > 128) return 8;
                                else
-                                       if (x > 64) return 7;
+                                       if (e > 64) return 7;
                                        else return 6;
                else
-                       if (x > 4)
-                               if (x > 16) return 5;
+                       if (e > 4)
+                               if (e > 16) return 5;
                                else
-                                       if (x > 8) return 4;
+                                       if (e > 8) return 4;
                                        else return 3;
                        else
-                               if (x > 2) return 2;
+                               if (e > 2) return 2;
                                else
-                                       if (x > 1) return 1;
+                                       if (e > 1) return 1;
                                        else return 0;
 }
 
index 4854f092a0aba4d21b65675082913140dfb38d39..d68dc7d968ee14850297dc46b5ef744c76761a73 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
-#include <lib/matrix/command.qc>
+#if XONOTIC
+       #include <lib/matrix/command.qc>
+#endif
 #include <lib/matrix/matrix.qc>
index 965812297204137ccb73c865636c0784ca5002f3..c351afe8f7c91f41123a4d68effd731f7f79659a 100644 (file)
@@ -1,3 +1,5 @@
 // generated file; do not modify
-#include <lib/matrix/command.qh>
+#if XONOTIC
+       #include <lib/matrix/command.qh>
+#endif
 #include <lib/matrix/matrix.qh>
index 27c7ec959c50e79fcafceccaf24732fee6119253..449aa373b31885d5aa3a5958d7855c94c4fd3d6c 100644 (file)
@@ -1,6 +1,6 @@
 #include "command.qh"
 
-#include <common/command/all.qh>
+#include <common/command/_mod.qh>
 
 GENERIC_COMMAND(mx, "Send a matrix command") {
     switch (argv(1)) {
index 23ec02e823242d3d58d74959d21695ece58ac6fc..c399c2aa6cf86e2d9a9ba910667c8cd1c926ba43 100644 (file)
@@ -22,7 +22,7 @@ void MX_Nick_(entity fh, entity pass, int status)
             fh.url_verb = "PUT";
             fh.url_content_type = "application/json";
             url_fputs(fh, sprintf("{\"displayname\": \"%s\"}", pass.message));
-            remove(pass);
+            delete(pass);
             url_fclose(fh);
             break;
         }
@@ -46,7 +46,7 @@ void MX_Messages_(entity fh, entity pass, int status)
 {
     switch (status) {
         default: {
-            LOG_WARNINGF("status: %d", status);
+            LOG_WARNF("status: %d", status);
             break;
         }
         case URL_READY_CLOSED: break;
@@ -90,7 +90,7 @@ void MX_Sync_(entity fh, entity pass, int status)
 {
     switch (status) {
         default: {
-            LOG_WARNINGF("status: %d", status);
+            LOG_WARNF("status: %d", status);
             break;
         }
         case URL_READY_CLOSED: break;
@@ -175,7 +175,7 @@ void MX_Typing_(entity fh, entity pass, int status)
             fh.url_verb = "PUT";
             fh.url_content_type = "application/json";
             url_fputs(fh, sprintf("{\"typing\": %s, \"timeout\": 30000}", pass.message));
-            remove(pass);
+            delete(pass);
             url_fclose(fh);
             break;
         }
@@ -204,7 +204,7 @@ void MX_Say_(entity fh, entity pass, int status)
             fh.url_verb = "PUT";
             fh.url_content_type = "application/json";
             url_fputs(fh, sprintf("{\"msgtype\": \"m.text\", \"body\": \"%s\"}", pass.message));
-            strunzone(pass.message); remove(pass);
+            strunzone(pass.message); delete(pass);
             url_fclose(fh);
             break;
         }
index 950000f04756ac028e77b1b2859c0fa63dcad215..21e0c5239f59aa7399533f5c35aca8c0e0c2487b 100644 (file)
@@ -1,9 +1,6 @@
 #pragma once
 
 #ifdef __STDC__
-       #define EMPTY()
-       #define DEFER(id) id EMPTY()
-
        #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
        #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
        #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
index 4e09d4b01043363a619153154f90dd08baf5ba6d..56c80e02d1b13ca5dcf66105aa597bb7c28d67f3 100644 (file)
@@ -98,6 +98,9 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
        .int Version;  // deprecated, use SendFlags
        .int SendFlags;
 
+       IntrusiveList g_uncustomizables;
+       STATIC_INIT(g_uncustomizables) { g_uncustomizables = IL_NEW(); }
+
        void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
        {
                if (e.classname == "") e.classname = "net_linked";
@@ -130,16 +133,18 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
        .void(entity this) uncustomizeentityforclient;
        .float uncustomizeentityforclient_set;
 
-       void SetCustomizer(entity e, bool(entity this) customizer, void(entity this) uncustomizer)
+       void SetCustomizer(entity e, bool(entity this, entity client) customizer, void(entity this) uncustomizer)
        {
                setcefc(e, customizer);
                e.uncustomizeentityforclient = uncustomizer;
                e.uncustomizeentityforclient_set = !!uncustomizer;
+               if(uncustomizer)
+                       IL_PUSH(g_uncustomizables, e);
        }
 
        void UncustomizeEntitiesRun()
        {
-               FOREACH_ENTITY_FLOAT(uncustomizeentityforclient_set, true, it.uncustomizeentityforclient(it));
+               IL_EACH(g_uncustomizables, it.uncustomizeentityforclient_set, it.uncustomizeentityforclient(it));
        }
 
        STRING_ITERATOR(g_buf, string_null, 0);
@@ -156,13 +161,13 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
                {
                        entity reader = C2S_Protocol_from(C2S);
                        if (reader && reader.m_read && reader.m_read(NULL, sender, true)) continue;
-                       LOG_SEVEREF("Net_ClientCommand() with malformed C2S=%d\n", C2S);
+                       LOG_SEVEREF("Net_ClientCommand() with malformed C2S=%d", C2S);
                        return;
                }
                g_buf_i--;
                int expected = strlen(buf);
-               if (g_buf_i > expected) LOG_WARNINGF("Underflow: %d", g_buf_i - expected);
-               if (g_buf_i < expected) LOG_WARNINGF("Overrflow: %d", expected - g_buf_i);
+               if (g_buf_i > expected) LOG_WARNF("Underflow: %d", g_buf_i - expected);
+               if (g_buf_i < expected) LOG_WARNF("Overrflow: %d", expected - g_buf_i);
        }
 
 #endif
@@ -176,7 +181,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
                } MACRO_END
        #define Net_Reject() \
                MACRO_BEGIN { \
-                       if (this)     remove(this); \
+                       if (this)     delete(this); \
                } MACRO_END
 
        string g_buf;
@@ -203,6 +208,54 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
                } MACRO_END
 #endif
 
+// serialization: new style
+
+USING(Stream, int);
+#if defined(SVQC)
+       #define stream_reading(stream) false
+       #define stream_writing(stream) true
+#elif defined(CSQC)
+       #define stream_reading(stream) true
+       #define stream_writing(stream) false
+#endif
+
+#define serialize(T, stream, ...) serialize_##T(stream, __VA_ARGS__)
+
+#if defined(SVQC)
+       #define serialize_byte(stream, this) \
+               MACRO_BEGIN \
+               WriteByte(stream, this); \
+               MACRO_END
+#elif defined(CSQC)
+       #define serialize_byte(stream, this) \
+               MACRO_BEGIN \
+               this = ReadByte(); \
+               MACRO_END
+#endif
+
+#if defined(SVQC)
+       #define serialize_float(stream, this) \
+               MACRO_BEGIN \
+               WriteCoord(stream, this); \
+               MACRO_END
+#elif defined(CSQC)
+       #define serialize_float(stream, this) \
+               MACRO_BEGIN \
+               this = ReadCoord(); \
+               MACRO_END
+#endif
+
+#define serialize_vector(stream, this) \
+    MACRO_BEGIN \
+    vector _v = this; \
+    serialize_float(stream, _v.x); \
+    serialize_float(stream, _v.y); \
+    serialize_float(stream, _v.z); \
+    this = _v; \
+    MACRO_END
+
+// serialization: old
+
 #define ReadRegistered(r) r##_from(Read_byte())
 #define WriteRegistered(r, to, it) Write_byte(to, it.m_id)
 
@@ -239,12 +292,13 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
 #define Read_string() ReadString()
 #define Write_string(to, f) WriteString(to, f)
 
-#ifndef MENUQC
+#ifdef GAMEQC
        const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05;
        #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
        #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
 
        #ifdef CSQC
+               float servertime;
                entity ReadCSQCEntity()
                {
                        int f = ReadShort();
@@ -260,7 +314,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
                #define ReadInt48_t() vec3(ReadInt24_t(), ReadInt24_t(), 0)
                #define ReadInt72_t() vec3(ReadInt24_t(), ReadInt24_t(), ReadInt24_t())
 
-               int _ReadSByte;
+               noref int _ReadSByte;
                #define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7)))
                #define ReadFloat() ReadCoord()
                #define ReadVector() vec3(ReadFloat(), ReadFloat(), ReadFloat())
index 3708552d5968c5d5bcfb1c976bd56cae9779fa63..7a7a72641a0601a1d857a0654631223d452275dd 100644 (file)
@@ -71,10 +71,17 @@ entity __spawn(string _classname, string _sourceLoc, bool pure)
 #define new_pure(class) _new(class, true)
 #define spawn() __spawn("entity", __FILE__ ":" STR(__LINE__), false)
 
+[[accumulate]] void ONREMOVE(entity this) {}
+
+#ifndef SVQC
+       #define delete_fn builtin_remove
+#endif
+
 #define delete(this) MACRO_BEGIN { \
     entity _this = (this); \
     void(entity) _dtor = _this.dtor; \
-    if (_dtor) _dtor(_this); else remove(_this); \
+    ONREMOVE(this); \
+    if (_dtor) _dtor(_this); else delete_fn(_this); \
     /* this = NULL; */  \
 } MACRO_END
 
@@ -220,13 +227,17 @@ STATIC_INIT(RegisterClasses)
     } \
        STATIC_METHOD(cname, dtorimpl, void(cname this))
 
-#define ATTRIB(cname, name, type, val)      \
-       class(cname).type name;                \
+#define ATTRIB(...) EVAL_ATTRIB(OVERLOAD_(ATTRIB, __VA_ARGS__))
+#define EVAL_ATTRIB(...) __VA_ARGS__
+#define ATTRIB_3(cname, name, type) INIT(cname) {} class(cname) .type name
+#define ATTRIB_4(cname, name, type, val) \
+       ATTRIB_3(cname, name, type); \
        INIT(cname) \
        { \
                noref bool strzone; /* Error on strzone() calls. */ \
                this.name = val; \
-       }
+       } \
+       ATTRIB_3(cname, name, type)
 
 #define STATIC_ATTRIB(cname, name, type, val) \
        type cname##_##name; \
@@ -257,7 +268,7 @@ STATIC_INIT(RegisterClasses)
        }
 
 #define ATTRIBARRAY(cname, name, type, cnt) \
-       class(cname).type name[cnt];
+       class(cname) .type name[cnt]
 
 #define ENDCLASS(cname) \
        INIT(cname) \
@@ -271,7 +282,7 @@ STATIC_INIT(RegisterClasses)
 #define spawn_1(this)
 #define _vtbl NULL
 CLASS(Object, );
-    DESTRUCTOR(Object) { remove(this); }
+    DESTRUCTOR(Object) { builtin_remove(this); }
     #define remove(this) delete(this)
        METHOD(Object, describe, string(Object this))
        {
index d3228d47c864b9fdcce2d64c54570805f4d71a0a..927410cde115de908e5357781ba789c33378716b 100644 (file)
@@ -12,8 +12,7 @@
 /* without even the implied warranty of merchantability or fitness for a      */
 /* particular purpose.                                                        */
 /*                                                                            */
-#ifndef P99_H_
-#define P99_H_
+#pragma once
 
 #define P99_MAX_NUMBER 16
 #define P00_ARG(                            \
@@ -83,5 +82,3 @@
     P99_PASTE2(P99_PASTE3(_1, _2, _3), _4)
 #define P99_PASTE5(_1, _2, _3, _4, _5) \
     P99_PASTE2(P99_PASTE4(_1, _2, _3, _4), _5)
-
-#endif /* !P99_H_ */
index aff961c55da56b9dcde016c90335dbee7026d5e9..627fec11a92e1293bec76db098230b0a5fafc7f0 100644 (file)
@@ -9,7 +9,7 @@ void RandomSelection_Init()
        RandomSelection_best_priority = -1;
 }
 
-void RandomSelection_Add(entity e, float f, string s, float weight, float priority)
+void RandomSelection_Add(entity e, float f, string s, vector v, float weight, float priority)
 {
        if (priority > RandomSelection_best_priority)
        {
@@ -17,6 +17,7 @@ void RandomSelection_Add(entity e, float f, string s, float weight, float priori
                RandomSelection_chosen_ent = e;
                RandomSelection_chosen_float = f;
                RandomSelection_chosen_string = s;
+               RandomSelection_chosen_vec = v;
                RandomSelection_totalweight = weight;
        }
        else if (priority == RandomSelection_best_priority)
@@ -27,6 +28,7 @@ void RandomSelection_Add(entity e, float f, string s, float weight, float priori
                        RandomSelection_chosen_ent = e;
                        RandomSelection_chosen_float = f;
                        RandomSelection_chosen_string = s;
+                       RandomSelection_chosen_vec = v;
                }
        }
 }
@@ -38,8 +40,7 @@ void DistributeEvenly_Init(float amount, float totalweight)
 {
        if (DistributeEvenly_amount)
        {
-               LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
-               LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n");
+               LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ", ftos(DistributeEvenly_totalweight), " left!)");
        }
        if (totalweight == 0) DistributeEvenly_amount = 0;
        else DistributeEvenly_amount = amount;
@@ -97,7 +98,7 @@ float gsl_ran_gaussian(float sigma)
                prandom_seed = c;
 
        #ifdef USE_PRANDOM_DEBUG
-                       LOG_TRACE("RANDOM -> ", ftos(c), "\n");
+                       LOG_TRACE("RANDOM -> ", ftos(c));
        #endif
 
                return c / 65536;  // in [0..1[
@@ -122,14 +123,14 @@ float gsl_ran_gaussian(float sigma)
        {
                prandom_seed = seed;
        #ifdef USE_PRANDOM_DEBUG
-                       LOG_TRACE("SRANDOM ", ftos(seed), "\n");
+                       LOG_TRACE("SRANDOM ", ftos(seed));
        #endif
        }
 
        #ifdef USE_PRANDOM_DEBUG
                void prandom_debug()
                {
-                       LOG_TRACE("Current random seed = ", ftos(prandom_seed), "\n");
+                       LOG_TRACE("Current random seed = ", ftos(prandom_seed));
                }
        #endif
 #endif
index 1834a97a331164086464ce6ecc87d0527971fb9a..b2fc53f0cac4edaaa3ef964c25b7b6093ac2ae2c 100644 (file)
@@ -5,9 +5,14 @@ float RandomSelection_best_priority;
 entity RandomSelection_chosen_ent;
 float RandomSelection_chosen_float;
 string RandomSelection_chosen_string;
+vector RandomSelection_chosen_vec;
 
 void RandomSelection_Init();
-void RandomSelection_Add(entity e, float f, string s, float weight, float priority);
+void RandomSelection_Add(entity e, float f, string s, vector v, float weight, float priority);
+#define RandomSelection_AddEnt(e, weight, priority) RandomSelection_Add(e, 0, string_null, '0 0 0', weight, priority)
+#define RandomSelection_AddFloat(f, weight, priority) RandomSelection_Add(NULL, f, string_null, '0 0 0', weight, priority)
+#define RandomSelection_AddString(s, weight, priority) RandomSelection_Add(NULL, 0, s, '0 0 0', weight, priority)
+#define RandomSelection_AddVec(v, weight, priority) RandomSelection_Add(NULL, 0, string_null, v, weight, priority)
 
 // prandom - PREDICTABLE random number generator
 
index 7ce923c537d8242ff77cd73f336ab438d173f57b..9d171e031ab4391090341737d675574a0154aca2 100644 (file)
@@ -71,10 +71,13 @@ REGISTRY(Registries, BITS(8))
        REGISTER_INIT(id) {} \
        void Register_##id() \
        { \
-               if (registry##_COUNT >= registry##_MAX) LOG_FATALF("Registry capacity exceeded (%d)", registry##_MAX); \
-               entity this = id = inst; \
-               this.registered_id = #id; \
-               REGISTRY_PUSH(registry, fld, this); \
+               entity this = id; \
+               if (this == NULL) { \
+                       if (registry##_COUNT >= registry##_MAX) LOG_FATALF("Registry capacity exceeded (%d)", registry##_MAX); \
+                       this = id = inst; \
+                       this.registered_id = #id; \
+                       REGISTRY_PUSH(registry, fld, this); \
+               } \
                Register_##id##_init(this); \
        } \
        ACCUMULATE_FUNCTION(_Register##registry, Register_##id) \
@@ -157,7 +160,7 @@ void Registry_send(string id, string hash);
                FOREACH(id, true, s = strcat(s, join, it.registered_id)); \
                s = substring(s, strlen(join), -1); \
                string h = REGISTRY_HASH(id) = strzone(digest_hex(algo, s)); \
-               LOG_DEBUGF(#id ": %s\n[%s]\n", h, s); \
+               LOG_DEBUGF(#id ": %s\n[%s]", h, s); \
        } \
        void Registry_check(string r, string sv) \
        { \
@@ -166,7 +169,7 @@ void Registry_send(string id, string hash);
                        string cl = REGISTRY_HASH(id); \
                        if (cl != sv) \
                        { \
-                               LOG_FATALF("client/server mismatch (%s).\nCL: %s\nSV: %s\n", r, cl, sv); \
+                               LOG_FATALF("client/server mismatch (%s).\nCL: %s\nSV: %s", r, cl, sv); \
                        } \
                } \
        } \
@@ -178,7 +181,11 @@ void Registry_send(string id, string hash);
 #define REGISTER_REGISTRY_2(id, str) \
        ACCUMULATE_FUNCTION(__static_init, Register##id) \
        CLASS(id##Registry, Object) \
-               ATTRIB(id##Registry, m_name, string, str) \
-               ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first) \
+               ATTRIB(id##Registry, m_name, string, str); \
+               ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first); \
+               METHOD(id##Registry, m_reload, void()); \
        ENDCLASS(id##Registry) \
-       REGISTER(Registries, REGISTRY, id, m_id, NEW(id##Registry));
+       REGISTER(Registries, REGISTRY, id, m_id, NEW(id##Registry)); \
+       METHOD(id##Registry, m_reload, void()) { \
+               Register##id(); \
+       }
index f69b6072bea77316d2b5154a20cf760e92cd3c54..a36466d2e0ffb8daca62e22bd35706bbeb5e761a 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 
-#ifndef MENUQC
+#ifdef GAMEQC
 
     /**
      * Replicate a client cvar into a server field
index ec43a34022592ae86b5c0c26ec23c5005ffcc938..f4c246f33058997803e1291f243286cfce99362d 100644 (file)
@@ -48,7 +48,7 @@
 
 #define SELFWRAP(T, R, oldargs, args, forward) \
     .R oldargs T; \
-    .R oldargs __##T = T; \
+    noref .R oldargs __##T = T; \
     .R args self##T; \
     R T##_self oldargs { ENGINE_EVENT(); return this.self##T forward; }
 
@@ -66,20 +66,26 @@ SELFWRAP(think, void, (), (entity this), (this))
 #define setthink(e, f) SELFWRAP_SET(think, e, f)
 #define getthink(e) SELFWRAP_GET(think, e)
 
-SELFWRAP(touch, void, (), (entity this), (this))
+#ifdef GAMEQC
+SELFWRAP(touch, void, (), (entity this, entity toucher), (this, other))
 #define settouch(e, f) SELFWRAP_SET(touch, e, f)
 #define gettouch(e) SELFWRAP_GET(touch, e)
+#endif
 
-SELFWRAP(blocked, void, (), (entity this), (this))
+#ifdef GAMEQC
+SELFWRAP(blocked, void, (), (entity this, entity blocker), (this, other))
 #define setblocked(e, f) SELFWRAP_SET(blocked, e, f)
 #define blocked stopusingthis
+#endif
 
 SELFWRAP(predraw, void, (), (entity this), (this))
 #define setpredraw(e, f) SELFWRAP_SET(predraw, e, f)
 
-SELFWRAP(customizeentityforclient, bool, (), (entity this), (this))
+#ifdef GAMEQC
+SELFWRAP(customizeentityforclient, bool, (), (entity this, entity client), (this, other))
 #define setcefc(e, f) SELFWRAP_SET(customizeentityforclient, e, f)
 #define getcefc(e) SELFWRAP_GET(customizeentityforclient, e)
+#endif
 
 SELFWRAP(camera_transform, vector, (vector org, vector ang), (entity this, vector org, vector ang), (this, org, ang))
 #define setcamera_transform(e, f) SELFWRAP_SET(camera_transform, e, f)
@@ -96,14 +102,8 @@ SELFWRAP(SendEntity, bool, (entity to, int sendflags), (entity this, entity to,
 #endif
 #define error(...) (__self = (NULL), builtin_error(__VA_ARGS__))
 #define movetogoal(e, ...) (__self = (e), builtin_movetogoal(__VA_ARGS__))
-#ifndef SVQC
-    #define objerror(e, ...) (__self = (e), builtin_objerror(__VA_ARGS__))
-#else
-    void make_safe_for_remove(entity this);
-    #define objerror(e, ...) (__self = (e), make_safe_for_remove(__self), builtin_objerror(__VA_ARGS__))
-#endif
 #define walkmove(e, ...) (__self = (e), builtin_walkmove(__VA_ARGS__))
 
-#ifndef MENUQC
+#ifdef GAMEQC
 void adaptor_think2use(entity this) { if (this.use) this.use(this, NULL, NULL); }
 #endif
index 0463979c1a3fb855c3245360ffcb3be7f4387a6a..e8aeaecae61f968bedd1f5ac640d7dce2e677b05 100644 (file)
@@ -20,7 +20,7 @@ noref bool require_spawnfunc_prefix;
 
        #define _spawnfunc_checktypes(fld) \
                if (fieldname == #fld) \
-                       if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted\n", fieldname);
+                       if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", fieldname);
 #else
        #define _spawnfunc_checktypes(fld)
 #endif
@@ -29,7 +29,7 @@ noref bool require_spawnfunc_prefix;
 
        noref bool __spawnfunc_expecting;
        noref entity __spawnfunc_expect;
-       bool __spawnfunc_unreachable_workaround = true;
+       noref bool __spawnfunc_unreachable_workaround = true;
 
        #define spawnfunc_1(id) spawnfunc_2(id, FIELDS_UNION)
        #define spawnfunc_2(id, whitelist) \
@@ -62,7 +62,7 @@ noref bool require_spawnfunc_prefix;
                                        if (fieldname == "") continue; \
                                        FIELDS_COMMON(_spawnfunc_check) \
                                        whitelist(_spawnfunc_check) \
-                                       LOG_WARNINGF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue.\n"), #id, fieldname, value); \
+                                       LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), #id, fieldname, value); \
                                } \
                                this.spawnfunc_checked = true; \
                        } \
@@ -144,6 +144,7 @@ noref bool require_spawnfunc_prefix;
                FIELD_SCALAR(fld, monster_moveflags) \
                FIELD_SCALAR(fld, monster_name) \
                FIELD_SCALAR(fld, movetype) \
+               FIELD_SCALAR(fld, move_movetype) \
                FIELD_SCALAR(fld, netname) \
                FIELD_SCALAR(fld, nextthink) \
                FIELD_SCALAR(fld, noalign) \
@@ -164,6 +165,7 @@ noref bool require_spawnfunc_prefix;
                FIELD_SCALAR(fld, sound1) \
                FIELD_SCALAR(fld, sounds) \
                FIELD_SCALAR(fld, spawnflags) \
+               FIELD_SCALAR(fld, spawnmob) \
                FIELD_SCALAR(fld, speed) \
                FIELD_SCALAR(fld, strength) \
                FIELD_SCALAR(fld, target2) \
index e1cfeb8721ce54f6b29a9f440e410d2c29bf5626..6b5febe6fbfcab9909de383b4d7ccd86940474d6 100644 (file)
@@ -9,8 +9,23 @@ void __static_init_precache() {}
 void __shutdown() {}
 #define shutdownhooks() CALL_ACCUMULATED_FUNCTION(__shutdown)
 
+#define GETTIME_REALTIME 1
+#ifdef MENUQC
+float(int tmr) _gettime = #67;
+#else
+float(int tmr) _gettime = #519;
+#endif
+
+void profile(string s)
+{
+       static float g_starttime;
+       float rt = _gettime(GETTIME_REALTIME);
+       if (!g_starttime) g_starttime = rt;
+       LOG_TRACEF("[%f] %s", rt - g_starttime, s);
+}
+
 #define _STATIC_INIT(where, func) \
-       void _static_##func(); \
+       [[accumulate]] void _static_##func() { profile(#func); } \
        ACCUMULATE_FUNCTION(where, _static_##func) \
        void _static_##func()
 
index bd8b792967a5cfbba7365455c12aa6044b19b2a4..4642f76403c3124b6cdc30fb9b3f923263394548 100644 (file)
@@ -8,6 +8,24 @@
 .int m_id;
 USING(vectori, vector);
 
+const int STATS_ENGINE_RESERVE = 32;
+// must be listed in ascending order
+#define MAGIC_STATS(_, x) \
+       _(x, MOVEVARS_TIMESCALE, 241) \
+       /**/
+
+int g_magic_stats_hole = 0;
+
+#define MAGIC_STATS_FIX_MANUAL(it, var, id) \
+       if (it.registered_id == "STAT_" #var) { --g_magic_stats_hole; it.m_id = id; } else
+
+#define MAGIC_STATS_FIX_AUTO(it, var, id) \
+       if (it.m_id == id) { ++g_magic_stats_hole; ++it.m_id; }
+
+#define MAGIC_STATS_FIX(it) \
+       it.m_id += g_magic_stats_hole; \
+       MAGIC_STATS(MAGIC_STATS_FIX_MANUAL, it) { MAGIC_STATS(MAGIC_STATS_FIX_AUTO, it) }
+
 #define REGISTER_STAT(...) EVAL_REGISTER_STAT(OVERLOAD(REGISTER_STAT, __VA_ARGS__))
 #define EVAL_REGISTER_STAT(...) __VA_ARGS__
 #if defined(CSQC)
@@ -27,7 +45,7 @@ USING(vectori, vector);
        #define _STAT(id) g_stat_##id
        #define REGISTER_STAT_2(id, T) \
                T _STAT(id); \
-               T CAT(_STAT(id), _prev); \
+               /* T CAT(_STAT(id), _prev); */ \
                REGISTER(Stats, STAT_##id, m_id, new_pure(stat)) \
                { \
                        if (#T == "vector" || #T == "vectori") { \
@@ -38,8 +56,8 @@ USING(vectori, vector);
                [[accumulate]] void stats_get() \
                { \
                        T it = getstat_##T(STAT_##id.m_id); \
-                       if (it != CAT(_STAT(id), _prev)) \
-                               CAT(_STAT(id), _prev) = _STAT(id) = it; \
+                       /* if (it != CAT(_STAT(id), _prev)) \
+                               CAT(_STAT(id), _prev) = */ _STAT(id) = it; \
                }
        #define REGISTER_STAT_3(x, T, expr) REGISTER_STAT_2(x, T)
 #elif defined(SVQC)
@@ -68,8 +86,11 @@ USING(vectori, vector);
        /** Prevent engine stats being sent */
        STATIC_INIT(stats_clear)
        {
-               int r = 32;
+               int r = STATS_ENGINE_RESERVE;
                for (int i = 0, n = 256 - r; i < n; ++i) {
+                       #define X(_, name, id) if (i == id) continue;
+                       MAGIC_STATS(X, );
+                       #undef X
                        addstat(r + i, AS_INT, __stat_null);
                }
        }
@@ -99,15 +120,16 @@ USING(vectori, vector);
     #define REGISTER_STAT_3(id, T, expr)
 #endif
 
-const int STATS_ENGINE_RESERVE = 32;
-
 REGISTRY(Stats, 256 - STATS_ENGINE_RESERVE)
 REGISTER_REGISTRY(Stats)
 REGISTRY_SORT(Stats)
 REGISTRY_CHECK(Stats)
 STATIC_INIT(RegisterStats_renumber)
 {
-       FOREACH(Stats, true, it.m_id = STATS_ENGINE_RESERVE + i);
+       FOREACH(Stats, true, {
+               it.m_id = STATS_ENGINE_RESERVE + i;
+               MAGIC_STATS_FIX(it);
+       });
 }
 #ifdef SVQC
 STATIC_INIT(stats_add) { stats_add(); }
index 2ade29691280beb373c05170f00b9e8d47667190..617891d8b6230f75bafe674b423896d90ac068f1 100644 (file)
@@ -339,7 +339,12 @@ int vercmp(string v1, string v2)
        return vercmp_recursive(v1, v2);
 }
 
+const string HEXDIGITS_MINSET = "0123456789ABCDEFabcdef";
 const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef";
 #define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0))
 #define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10)
-#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1))
+#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS_MINSET, (d), 1))
+#define IS_HEXDIGIT(d) (strstrofs(HEXDIGITS_MINSET, (d), 0) >= 0)
+
+const string DIGITS = "0123456789";
+#define IS_DIGIT(d) (strstrofs(DIGITS, (d), 0) >= 0)
index d05ae28cd903b76edc4770204b142d5f8689c589..ff6f2d23d5b52f1e00d75ba699c6552031ba8f5b 100644 (file)
@@ -15,7 +15,7 @@
 #define SUCCEED() (TEST_ok = true)
 
 /** Add a failure, but continue */
-#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARNINGF(msg); } MACRO_END
+#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARN(msg); } MACRO_END
 
 /** Add a failure and return */
 #define FAIL(msg) _TEST_ASSERT(ADD_FAILURE(msg))
@@ -87,7 +87,7 @@ int TEST_failed;
 
 #define EXPECT_NO_FATAL_FAILURE_(statement, then) \
        EXPECT_NO_FATAL_FAILURE__(statement, { \
-               LOG_WARNINGF( \
+               LOG_WARNF( \
                        "  Actual: %d fatal failures\n" \
                        "Expected: no fatal failures\n", \
                        TEST_fatal - TEST_prevfatal \
index 97e1c7958493f1c5ba6caa3403c7b9ea9e84020d..60ad3d88da517e11174910a1a1d5da29085b5b15 100644 (file)
@@ -1,10 +1,13 @@
 #pragma once
 
 #define reinterpret_cast(T, it) _unsafe_cast_##T(0, it)
-#define X(T) T _unsafe_cast_##T(int dummy, ...) { return ...(0, T); }
+#define X(T) \
+    T _unsafe_cast_##T(int dummy, ...) { return ...(0, T); } \
+    USING(T##_fld, .T); T##_fld _unsafe_cast_##T##_fld(int dummy, ...) { return ...(0, T##_fld); }
 X(bool)
 X(int)
 X(float)
+X(vector)
 X(entity)
 X(string)
 USING(rawfunc, float(...));
@@ -24,3 +27,5 @@ STATIC_INIT(INTEGER_ONE)
 {
     INTEGER_ONE = reinterpret_cast(int, _unsafe_fld2) - reinterpret_cast(int, _unsafe_fld1);
 }
+
+#define ARRAY_INDEX(T, arr, idx) (reinterpret_cast(T##_fld, reinterpret_cast(int, arr[0]) + FTOI(idx)))
index c4c700a72d6b8341e9e561af642d638853c78e9d..d1d5c65ed2a3d4bcf34e1a35789a709e44521c07 100644 (file)
@@ -55,7 +55,7 @@ float url_URI_Get_Callback(int id, float status, string data)
                        LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n");
                        e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
                        strunzone(e.url_url);
-                       remove(e);
+                       delete(e);
                        return 1;
                }
                e.url_rbufpos = 0;
@@ -64,7 +64,7 @@ float url_URI_Get_Callback(int id, float status, string data)
                        LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n");
                        e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
                        strunzone(e.url_url);
-                       remove(e);
+                       delete(e);
                        return 1;
                }
                for (i = 0; i < n; ++i)
@@ -77,7 +77,7 @@ float url_URI_Get_Callback(int id, float status, string data)
                // an ERROR
                e.url_ready(e, e.url_ready_pass, -fabs(status));
                strunzone(e.url_url);
-               remove(e);
+               delete(e);
                return 1;
        }
 }
@@ -107,7 +107,7 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
                                        LOG_INFO("url_single_fopen: out of memory in buf_create\n");
                                        rdy(e, pass, URL_READY_ERROR);
                                        strunzone(e.url_url);
-                                       remove(e);
+                                       delete(e);
                                        return;
                                }
                                e.url_wbufpos = 0;
@@ -229,7 +229,7 @@ void url_fclose(entity e)
                                        e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
                                        buf_del(e.url_wbuf);
                                        strunzone(e.url_url);
-                                       remove(e);
+                                       delete(e);
                                        return;
                                }
                        }
@@ -241,7 +241,7 @@ void url_fclose(entity e)
                                e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
                                buf_del(e.url_wbuf);
                                strunzone(e.url_url);
-                               remove(e);
+                               delete(e);
                                return;
                        }
 
@@ -262,20 +262,20 @@ void url_fclose(entity e)
                        e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
                        buf_del(e.url_rbuf);
                        strunzone(e.url_url);
-                       remove(e);
+                       delete(e);
                }
        }
        else if (e.url_fh == URL_FH_STDOUT)
        {
                e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);  // closing creates no reading handle
-               remove(e);
+               delete(e);
        }
        else
        {
                // file
                fclose(e.url_fh);
                e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);  // closing creates no reading handle
-               remove(e);
+               delete(e);
        }
 }
 
@@ -316,7 +316,7 @@ void url_fputs(entity e, string s)
        else if (e.url_fh == URL_FH_STDOUT)
        {
                // stdout
-               LOG_INFO(s);
+               print(s);
        }
        else
        {
@@ -336,7 +336,7 @@ void url_multi_ready(entity fh, entity me, float status)
                        LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n");
                        me.url_ready(fh, me.url_ready_pass, status);
                        strunzone(me.url_url);
-                       remove(me);
+                       delete(me);
                        return;
                }
                me.url_attempt += 1;
@@ -345,7 +345,7 @@ void url_multi_ready(entity fh, entity me, float status)
                {
                        me.url_ready(fh, me.url_ready_pass, status);
                        strunzone(me.url_url);
-                       remove(me);
+                       delete(me);
                        return;
                }
                url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
index def1dae2d489f512a0c677087943814c00f8b8a7..0a887cfebb03c4544df88a8d0316bd6f8aa4ab8d 100644 (file)
@@ -34,6 +34,13 @@ vector cross(vector a, vector b)
 }
 #endif
 
+noref vector _vmul_a, _vmul_b;
+#define vmul(a, b) \
+    (_vmul_a = (a), _vmul_b = (b), \
+         '1 0 0' * (_vmul_a.x * _vmul_b.x) \
+       + '0 1 0' * (_vmul_a.y * _vmul_b.y) \
+       + '0 0 1' * (_vmul_a.z * _vmul_b.z))
+
 const vector eX = '1 0 0';
 const vector eY = '0 1 0';
 const vector eZ = '0 0 1';
@@ -94,7 +101,7 @@ noref vector _vec2;
 noref vector _vec3;
 #define vec3(_x, _y, _z) (_vec3.x = (_x), _vec3.y = (_y), _vec3.z = (_z), _vec3)
 
-vector rotate(vector v, float a)
+vector Rotate(vector v, float a)
 {
        float a_sin = sin(a), a_cos = cos(a);
        vector r = '0 0 0';
@@ -116,7 +123,26 @@ vector reflect(vector dir, vector norm)
        return dir - 2 * (dir * norm) * norm;
 }
 
-#ifndef MENUQC
+/**
+ * clip vel along the plane defined by norm (assuming 0 distance away), bounciness determined by bounce 0..1
+ */
+vector vec_reflect(vector vel, vector norm, float bounce)
+{
+       return vel - (1 + bounce) * (vel * norm) * norm;
+}
+
+vector vec_epsilon(vector this, float eps)
+{
+       if (this.x > -eps && this.x < eps) this.x = 0;
+       if (this.y > -eps && this.y < eps) this.y = 0;
+       if (this.z > -eps && this.z < eps) this.z = 0;
+       return this;
+}
+
+#define ClipVelocity(in, normal, out, overbounce) \
+       (out = vec_epsilon(vec_reflect(in, normal, (overbounce) - 1), 0.1))
+
+#ifdef GAMEQC
        vector get_corner_position(entity box, int corner)
        {
                switch (corner)
index 0a10843c0087872de9ed0a0046747331d438543f..b287651a10a4fb868acdfeca18d2c0dfaf77b4d0 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_ANGLETRANSFORM_H
-#define LIB_WARPZONE_ANGLETRANSFORM_H
+#pragma once
 
 #ifndef POSITIVE_PITCH_IS_DOWN
 #define POSITIVE_PITCH_IS_DOWN 1
@@ -42,4 +41,3 @@ vector AnglesTransform_ToVAngles(vector v);
 // transformed = original * transform + postshift
 vector AnglesTransform_Multiply_GetPostShift(vector sf0, vector st0, vector t1, vector st1);
 vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st);
-#endif
index 5a1e320e93462b32195814e54159d7ee26eab576..15a3ca4c3ccc6e90870c72ab7bc74251777dd397 100644 (file)
@@ -25,7 +25,7 @@ void WarpZone_Fade_PreDraw(entity this)
                this.drawmask = MASK_NORMAL;
 }
 
-void WarpZone_Touch (entity this);
+void WarpZone_Touch(entity this, entity toucher);
 NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew)
 {
        warpzone_warpzones_exist = 1;
@@ -35,6 +35,9 @@ NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew)
        }
        this.classname = "trigger_warpzone";
 
+       if(isnew)
+               IL_PUSH(g_warpzones, this);
+
        int f = ReadByte();
        this.warpzone_isboxy = (f & 1);
        if(f & 4)
@@ -89,7 +92,7 @@ NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew)
        // engine currently wants this
        setpredraw(this, WarpZone_Fade_PreDraw);
 
-       //this.move_touch = WarpZone_Touch;
+       //settouch(this, WarpZone_Touch);
        return true;
 }
 
index 8f3e643b5c0eabae6e8d30817a40ccebc071a7df..47ff24136839d23144caa37af2c9f2265e367a57 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_CLIENT_H
-#define LIB_WARPZONE_CLIENT_H
+#pragma once
 
 void WarpZone_FixPMove();
 void WarpZone_FixView();
@@ -8,4 +7,3 @@ void WarpZone_Shutdown();
 
 vector warpzone_save_view_origin;
 vector warpzone_save_view_angles;
-#endif
index 5acacc7734d47953601c3fd385fa35b2f86497e4..5a3929e1e92d58ecd6520f0bbb05d68499e61f73 100644 (file)
@@ -4,7 +4,7 @@
     #include <common/t_items.qh>
 #elif defined(MENUQC)
 #elif defined(SVQC)
-    #include <common/weapons/all.qh>
+    #include <common/weapons/_all.qh>
 #endif
 
 void WarpZone_Accumulator_Clear(entity acc)
@@ -112,7 +112,7 @@ float WarpZoneLib_BoxTouchesBrush_Recurse()
 #ifdef CSQC
        if (trace_networkentity)
        {
-               LOG_TRACE("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush\n");
+               LOG_TRACE("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush");
                // we cannot continue, as a player blocks us...
                // so, abort
                return 0;
@@ -154,31 +154,33 @@ float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
 entity WarpZone_Find(vector mi, vector ma)
 {
        // if we are near any warpzone planes - MOVE AWAY (work around nearclip)
-       entity e;
        if(!warpzone_warpzones_exist)
                return NULL;
-       for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
-               if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, NULL))
-                       return e;
+       IL_EACH(g_warpzones, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL),
+       {
+               return it;
+       });
        return NULL;
 }
 
 void WarpZone_MakeAllSolid()
 {
-       entity e;
        if(!warpzone_warpzones_exist)
                return;
-       for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
-               e.solid = SOLID_BSP;
+       IL_EACH(g_warpzones, true,
+       {
+               it.solid = SOLID_BSP;
+       });
 }
 
 void WarpZone_MakeAllOther()
 {
-       entity e;
        if(!warpzone_warpzones_exist)
                return;
-       for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
-               e.solid = SOLID_TRIGGER;
+       IL_EACH(g_warpzones, true,
+       {
+               it.solid = SOLID_TRIGGER;
+       });
 }
 
 void WarpZone_Trace_InitTransform()
@@ -271,7 +273,7 @@ void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end,
        {
                if(--i < 1)
                {
-                       LOG_TRACE("Too many warpzones in sequence, aborting trace.\n");
+                       LOG_TRACE("Too many warpzones in sequence, aborting trace.");
                        trace_ent = NULL;
                        break;
                }
@@ -299,7 +301,7 @@ void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end,
                if(trace_ent == wz)
                {
                        // FIXME can this check be removed? Do we really need it?
-                       LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n");
+                       LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace");
                        trace_ent = NULL;
                        break;
                }
@@ -395,7 +397,7 @@ void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZo
        {
                if(--i < 1)
                {
-                       LOG_TRACE("Too many warpzones in sequence, aborting trace.\n");
+                       LOG_TRACE("Too many warpzones in sequence, aborting trace.");
                        trace_ent = NULL;
                        break;
                }
@@ -413,7 +415,7 @@ void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZo
                if(trace_ent == wz)
                {
                        // FIXME can this check be removed? Do we really need it?
-                       LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n");
+                       LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace");
                        trace_ent = NULL;
                        break;
                }
@@ -671,7 +673,7 @@ void WarpZone_RefSys_GC(entity this)
        // garbage collect unused reference systems
        this.nextthink = time + 1;
        if(this.owner.WarpZone_refsys != this)
-               remove(this);
+               delete(this);
 }
 void WarpZone_RefSys_CheckCreate(entity me)
 {
@@ -688,7 +690,7 @@ void WarpZone_RefSys_Clear(entity me)
 {
        if(me.WarpZone_refsys)
        {
-               remove(me.WarpZone_refsys);
+               delete(me.WarpZone_refsys);
                me.WarpZone_refsys = NULL;
        }
 }
index 2cfa7ef9bc906e7beca25011b57c64527544186e..26c0e80fe4623526ee12cecc05842fb92c4f5dd0 100644 (file)
@@ -1,9 +1,11 @@
-#ifndef LIB_WARPZONE_COMMON_H
-#define LIB_WARPZONE_COMMON_H
+#pragma once
 
 // uncomment this if your mod uses the roll angle in fixangle
 // #define KEEP_ROLL
 
+IntrusiveList g_warpzones;
+STATIC_INIT(g_warpzones) { g_warpzones = IL_NEW(); }
+
 float warpzone_warpzones_exist;
 float warpzone_cameras_exist;
 
@@ -111,6 +113,5 @@ float WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher);
 void WarpZoneLib_ExactTrigger_Init(entity this);
 
 // WARNING: this kills the trace globals
-#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch(this, other)) return
+#define EXACTTRIGGER_TOUCH(e,t) if(WarpZoneLib_ExactTrigger_Touch((e), (t))) return
 #define EXACTTRIGGER_INIT  WarpZoneLib_ExactTrigger_Init(this)
-#endif
index ac3b65ff1a276a3dd35c36f1c12d206055fd9fb0..e190518275b9c407570efae466fbdcb7ef758898 100644 (file)
 #elif defined(SVQC)
 #endif
 
-int fpclassify(float x)
+int fpclassify(float e)
 {
-       if(isnan(x))
+       if(isnan(e))
                return FP_NAN;
-       if(isinf(x))
+       if(isinf(e))
                return FP_INFINITE;
-       if(x == 0)
+       if(e == 0)
                return FP_ZERO;
        return FP_NORMAL;
 }
-bool isfinite(float x)
+bool isfinite(float e)
 {
-       return !(isnan(x) || isinf(x));
+       return !(isnan(e) || isinf(e));
 }
-bool isinf(float x)
+bool isinf(float e)
 {
-       return (x != 0) && (x + x == x);
+       return (e != 0) && (e + e == e);
 }
-bool isnan(float x)
+bool isnan(float e)
 {
-       float y;
-       y = x;
-       return (x != y);
+       float f = e;
+       return (e != f);
 }
-bool isnormal(float x)
+bool isnormal(float e)
 {
-       return isfinite(x);
+       return isfinite(e);
 }
-bool signbit(float x)
+bool signbit(float e)
 {
-       return (x < 0);
+       return (e < 0);
 }
 
-float acosh(float x)
+float acosh(float e)
 {
-       return log(x + sqrt(x*x - 1));
+       return log(e + sqrt(e*e - 1));
 }
-float asinh(float x)
+float asinh(float e)
 {
-       return log(x + sqrt(x*x + 1));
+       return log(e + sqrt(e*e + 1));
 }
-float atanh(float x)
+float atanh(float e)
 {
-       return 0.5 * log((1+x) / (1-x));
+       return 0.5 * log((1+e) / (1-e));
 }
-float cosh(float x)
+float cosh(float e)
 {
-       return 0.5 * (exp(x) + exp(-x));
+       return 0.5 * (exp(e) + exp(-e));
 }
-float sinh(float x)
+float sinh(float e)
 {
-       return 0.5 * (exp(x) - exp(-x));
+       return 0.5 * (exp(e) - exp(-e));
 }
-float tanh(float x)
+float tanh(float e)
 {
-       return sinh(x) / cosh(x);
+       return sinh(e) / cosh(e);
 }
 
-float exp(float x)
+float exp(float e)
 {
-       return pow(M_E, x);
+       return pow(M_E, e);
 }
-float exp2(float x)
+float exp2(float e)
 {
-       return pow(2, x);
+       return pow(2, e);
 }
-float expm1(float x)
+float expm1(float e)
 {
-       return exp(x) - 1;
+       return exp(e) - 1;
 }
 
-vector frexp(float x)
+vector frexp(float e)
 {
        vector v;
        v.z = 0;
-       v.y = ilogb(x) + 1;
-       v.x = x / exp2(v.y);
+       v.y = ilogb(e) + 1;
+       v.x = e / exp2(v.y);
        return v;
 }
-int ilogb(float x)
+int ilogb(float e)
 {
-       return floor(log2(fabs(x)));
+       return floor(log2(fabs(e)));
 }
-float ldexp(float x, int e)
+float ldexp(float e, int e)
 {
-       return x * pow(2, e);
+       return e * pow(2, e);
 }
-float logn(float x, float base)
+float logn(float e, float base)
 {
-       return log(x) / log(base);
+       return log(e) / log(base);
 }
-float log10(float x)
+float log10(float e)
 {
-       return log(x) * M_LOG10E;
+       return log(e) * M_LOG10E;
 }
-float log1p(float x)
+float log1p(float e)
 {
-       return log(x + 1);
+       return log(e + 1);
 }
-float log2(float x)
+float log2(float e)
 {
-       return log(x) * M_LOG2E;
+       return log(e) * M_LOG2E;
 }
-float logb(float x)
+float logb(float e)
 {
-       return floor(log2(fabs(x)));
+       return floor(log2(fabs(e)));
 }
 vector modf(float f)
 {
        return '1 0 0' * (f - trunc(f)) + '0 1 0' * trunc(f);
 }
 
-float scalbn(float x, int n)
+float scalbn(float e, int n)
 {
-       return x * pow(2, n);
+       return e * pow(2, n);
 }
 
-float cbrt(float x)
+float cbrt(float e)
 {
-       return copysign(pow(fabs(x), 1.0/3.0), x);
+       return copysign(pow(fabs(e), 1.0/3.0), e);
 }
-float hypot(float x, float y)
+float hypot(float e, float f)
 {
-       return sqrt(x*x + y*y);
+       return sqrt(e*e + f*f);
 }
 
-float erf(float x)
+float erf(float e)
 {
        // approximation taken from wikipedia
-       float y;
-       y = x*x;
-       return copysign(sqrt(1 - exp(-y * (1.273239544735163 + 0.14001228868667 * y) / (1 + 0.14001228868667 * y))), x);
+       float f;
+       f = e*e;
+       return copysign(sqrt(1 - exp(-f * (1.273239544735163 + 0.14001228868667 * f) / (1 + 0.14001228868667 * f))), e);
 }
-float erfc(float x)
+float erfc(float e)
 {
-       return 1.0 - erf(x);
+       return 1.0 - erf(e);
 }
-vector lgamma(float x)
+vector lgamma(float e)
 {
        // TODO improve accuracy
-       if(!isfinite(x))
-               return fabs(x) * '1 0 0' + copysign(1, x) * '0 1 0';
-       if(x < 1 && x == floor(x))
+       if(!isfinite(e))
+               return fabs(e) * '1 0 0' + copysign(1, e) * '0 1 0';
+       if(e < 1 && e == floor(e))
                return nan("gamma") * '1 1 1';
-       if(x < 0.1)
+       if(e < 0.1)
        {
                vector v;
-               v = lgamma(1.0 - x);
+               v = lgamma(1.0 - e);
                // reflection formula:
                // gamma(1-z) * gamma(z) = pi / sin(pi*z)
                // lgamma(1-z) + lgamma(z) = log(pi) - log(sin(pi*z))
                // sign of gamma(1-z) = sign of gamma(z) * sign of sin(pi*z)
-               v.z = sin(M_PI * x);
+               v.z = sin(M_PI * e);
                v.x = log(M_PI) - log(fabs(v.z)) - v.x;
                if(v.z < 0)
                        v.y = -v.y;
                v.z = 0;
                return v;
        }
-       if(x < 1.1)
-               return lgamma(x + 1) - log(x) * '1 0 0';
-       x -= 1;
-       return (0.5 * log(2 * M_PI * x) + x * (log(x) - 1)) * '1 0 0' + '0 1 0';
+       if(e < 1.1)
+               return lgamma(e + 1) - log(e) * '1 0 0';
+       e -= 1;
+       return (0.5 * log(2 * M_PI * e) + e * (log(e) - 1)) * '1 0 0' + '0 1 0';
 }
-float tgamma(float x)
+float tgamma(float e)
 {
-       vector v;
-       v = lgamma(x);
+       vector v = lgamma(e);
        return exp(v.x) * v.y;
 }
 
@@ -184,109 +182,109 @@ float tgamma(float x)
  *  1 % -2 == -1
  * -1 % -2 == -1
  */
-float pymod(float x, float y)
+float pymod(float e, float f)
 {
-       return x - y * floor(x / y);
+       return e - f * floor(e / f);
 }
 
-float nearbyint(float x)
+float nearbyint(float e)
 {
-       return rint(x);
+       return rint(e);
 }
-float trunc(float x)
+float trunc(float e)
 {
-       return (x>=0) ? floor(x) : ceil(x);
+       return (e>=0) ? floor(e) : ceil(e);
 }
 
-float fmod(float x, float y)
+float fmod(float e, float f)
 {
-       return x - y * trunc(x / y);
+       return e - f * trunc(e / f);
 }
-float remainder(float x, float y)
+float remainder(float e, float f)
 {
-       return x - y * rint(x / y);
+       return e - f * rint(e / f);
 }
-vector remquo(float x, float y)
+vector remquo(float e, float f)
 {
        vector v;
        v.z = 0;
-       v.y = rint(x / y);
-       v.x = x - y * v.y;
+       v.y = rint(e / f);
+       v.x = e - f * v.y;
        return v;
 }
 
-float copysign(float x, float y)
+float copysign(float e, float f)
 {
-       return fabs(x) * ((y>0) ? 1 : -1);
+       return fabs(e) * ((f>0) ? 1 : -1);
 }
 float nan(string tag)
 {
        return sqrt(-1);
 }
-float nextafter(float x, float y)
+float nextafter(float e, float f)
 {
        // TODO very crude
-       if(x == y)
+       if(e == f)
                return nan("nextafter");
-       if(x > y)
-               return -nextafter(-x, -y);
-       // now we know that x < y
-       // so we need the next number > x
+       if(e > f)
+               return -nextafter(-e, -f);
+       // now we know that e < f
+       // so we need the next number > e
        float d, a, b;
-       d = max(fabs(x), 0.00000000000000000000001);
-       a = x + d;
+       d = max(fabs(e), 0.00000000000000000000001);
+       a = e + d;
        do
        {
                d *= 0.5;
                b = a;
-               a = x + d;
+               a = e + d;
        }
-       while(a != x);
+       while(a != e);
        return b;
 }
-float nexttoward(float x, float y)
+float nexttoward(float e, float f)
 {
-       return nextafter(x, y);
+       return nextafter(e, f);
 }
 
-float fdim(float x, float y)
+float fdim(float e, float f)
 {
-       return max(x-y, 0);
+       return max(e-f, 0);
 }
-float fmax(float x, float y)
+float fmax(float e, float f)
 {
-       return max(x, y);
+       return max(e, f);
 }
-float fmin(float x, float y)
+float fmin(float e, float f)
 {
-       return min(x, y);
+       return min(e, f);
 }
-float fma(float x, float y, float z)
+float fma(float e, float f, float g)
 {
-       return x * y + z;
+       return e * f + g;
 }
 
-int isgreater(float x, float y)
+int isgreater(float e, float f)
 {
-       return x > y;
+       return e > f;
 }
-int isgreaterequal(float x, float y)
+int isgreaterequal(float e, float f)
 {
-       return x >= y;
+       return e >= f;
 }
-int isless(float x, float y)
+int isless(float e, float f)
 {
-       return x < y;
+       return e < f;
 }
-int islessequal(float x, float y)
+int islessequal(float e, float f)
 {
-       return x <= y;
+       return e <= f;
 }
-int islessgreater(float x, float y)
+int islessgreater(float e, float f)
 {
-       return x < y || x > y;
+       return e < f || e > f;
 }
-int isunordered(float x, float y)
+int isunordered(float e, float f)
 {
-       return !(x < y || x == y || x > y);
+       return !(e < f || e == f || e > f);
 }
index c3de3838dde28cb70ed0d9e915751554436ccd49..f5d8f63e74a1eba84c3a1008675e7fa0e4be3f91 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef LIB_WARPZONE_MATHLIB_H
-#define LIB_WARPZONE_MATHLIB_H
+#pragma once
 
 // <math.h>
 
@@ -11,55 +10,55 @@ const int FP_INFINITE = 1;
 const int FP_ZERO = 2;
 const int FP_SUBNORMAL = 3;
 const int FP_NORMAL = 4;
-int fpclassify(float x);
-bool isfinite(float x);
-bool isinf(float x);
-bool isnan(float x);
-bool isnormal(float x);
-bool signbit(float x);
-
-//float acos(float x);
-//float asin(float x);
-//float atan(float x);
-//float atan2(float y, float x);
-//float cos(float x);
-//float sin(float x);
-//float tan(float x);
-
-float acosh(float x);
-float asinh(float x);
-float atanh(float x);
-float cosh(float x);
-float sinh(float x);
-float tanh(float x);
-
-float exp(float x);
-float exp2(float x);
-float expm1(float x);
-
-vector frexp(float x); // returns mantissa as _x, exponent as _y
-int ilogb(float x);
-float ldexp(float x, int e);
-//float log(float x);
-float logn(float x, float base);
-float log10(float x);
-float log1p(float x);
-float log2(float x);
-float logb(float x);
+int fpclassify(float e);
+bool isfinite(float e);
+bool isinf(float e);
+bool isnan(float e);
+bool isnormal(float e);
+bool signbit(float e);
+
+//float acos(float e);
+//float asin(float e);
+//float atan(float e);
+//float atan2(float f, float e);
+//float cos(float e);
+//float sin(float e);
+//float tan(float e);
+
+float acosh(float e);
+float asinh(float e);
+float atanh(float e);
+float cosh(float e);
+float sinh(float e);
+float tanh(float e);
+
+float exp(float e);
+float exp2(float e);
+float expm1(float e);
+
+vector frexp(float e); // returns mantissa as _x, exponent as _y
+int ilogb(float e);
+float ldexp(float e, int e);
+//float log(float e);
+float logn(float e, float base);
+float log10(float e);
+float log1p(float e);
+float log2(float e);
+float logb(float e);
 vector modf(float f); // fraction as _x, integer as _y
 
-float scalbn(float x, int n);
+float scalbn(float e, int n);
 
-float cbrt(float x);
-//float fabs(float x);
-float hypot(float x, float y);
-//float pow(float x, float y);
-//float sqrt(float x, float y);
+float cbrt(float e);
+//float fabs(float e);
+float hypot(float e, float f);
+//float pow(float e, float f);
+//float sqrt(float e, float f);
 
-float erf(float x);
-float erfc(float x);
-vector lgamma(float x); // value in _x, sign in _y
-float tgamma(float x);
+float erf(float e);
+float erfc(float e);
+vector lgamma(float e); // value in _x, sign in _y
+float tgamma(float e);
 
 /**
  * Pythonic mod:
@@ -70,35 +69,35 @@ float tgamma(float x);
  *  1 % -2 == -1
  * -1 % -2 == -1
  */
-float pymod(float x, float y);
+float pymod(float e, float f);
 
-//float ceil(float x);
-//float floor(float x);
-float nearbyint(float x);
-//float rint(float x);
-//float round(float x);
-float trunc(float x);
+//float ceil(float e);
+//float floor(float e);
+float nearbyint(float e);
+//float rint(float e);
+//float round(float e);
+float trunc(float e);
 
-float fmod(float x, float y);
-float remainder(float x, float y);
-vector remquo(float x, float y);
+float fmod(float e, float f);
+float remainder(float e, float f);
+vector remquo(float e, float f);
 
-float copysign(float x, float y);
+float copysign(float e, float f);
 float nan(string tag);
-float nextafter(float x, float y);
-float nexttoward(float x, float y);
+float nextafter(float e, float f);
+float nexttoward(float e, float f);
 
-float fdim(float x, float y);
-float fmax(float x, float y);
-float fmin(float x, float y);
-float fma(float x, float y, float z);
+float fdim(float e, float f);
+float fmax(float e, float f);
+float fmin(float e, float f);
+float fma(float e, float f, float g);
 
-int isgreater(float x, float y);
-int isgreaterequal(float x, float y);
-int isless(float x, float y);
-int islessequal(float x, float y);
-int islessgreater(float x, float y);
-int isunordered(float x, float y);
+int isgreater(float e, float f);
+int isgreaterequal(float e, float f);
+int isless(float e, float f);
+int islessequal(float e, float f);
+int islessgreater(float e, float f);
+int isunordered(float e, float f);
 
 const float M_E        = 2.7182818284590452354;   /* e */
 const float M_LOG2E    = 1.4426950408889634074;   /* log_2 e */
@@ -114,5 +113,3 @@ const float M_2_PI     = 0.63661977236758134308;  /* 2/pi */
 const float M_2_SQRTPI = 1.12837916709551257390;  /* 2/sqrt(pi) */
 const float M_SQRT2    = 1.41421356237309504880;  /* sqrt(2) */
 const float M_SQRT1_2  = 0.70710678118654752440;  /* 1/sqrt(2) */
-
-#endif
index 7ddb96cbbf32b6506028705ca23fb08caed0ec6d..116ad00023ae98b1a4321c609698298a2dd46f0b 100644 (file)
@@ -5,9 +5,9 @@
 #elif defined(MENUQC)
 #elif defined(SVQC)
        #include <common/constants.qh>
+       #include <common/net_linked.qh>
        #include <common/triggers/subs.qh>
        #include <common/util.qh>
-       #include <server/command/common.qh>
        #include <server/constants.qh>
        #include <server/defs.qh>
 #endif
 .float warpzone_teleport_finishtime;
 .entity warpzone_teleport_zone;
 
-#ifdef SVQC
-       #define WarpZone_StoreProjectileData(e_) MACRO_BEGIN { \
-               entity e = e_; \
-               e.warpzone_oldorigin = e.origin; \
-               e.warpzone_oldvelocity = e.velocity; \
-               e.warpzone_oldangles = e.angles; \
-               } MACRO_END
-#elif defined(CSQC)
-       #define WarpZone_StoreProjectileData(e_) MACRO_BEGIN { \
-               entity e = e_; \
-               e.warpzone_oldorigin = e.move_origin; \
-               e.warpzone_oldvelocity = e.move_velocity; \
-               e.warpzone_oldangles = e.move_angles; \
-               } MACRO_END
-#endif
+#define WarpZone_StoreProjectileData(e_) MACRO_BEGIN { \
+       entity e = e_; \
+       e.warpzone_oldorigin = e.origin; \
+       e.warpzone_oldvelocity = e.velocity; \
+       e.warpzone_oldangles = e.angles; \
+       } MACRO_END
 
 void WarpZone_TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity)
 {
-#ifdef SVQC
        setorigin(player, to); // NOTE: this also aborts the move, when this is called by touch
+#ifdef SVQC
        player.oldorigin = to; // for DP's unsticking
-       player.angles = to_angles;
        player.fixangle = true;
-       player.velocity = to_velocity;
-#elif defined(CSQC)
-       player.move_origin = to;
-       player.move_angles = to_angles;
-       player.move_velocity = to_velocity;
 #endif
+       player.angles = to_angles;
+       player.velocity = to_velocity;
 
        BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT);
 
        if(IS_PLAYER(player))
-#ifdef SVQC
                BITCLR_ASSIGN(player.flags, FL_ONGROUND);
-#elif defined(CSQC)
-               BITCLR_ASSIGN(player.move_flags, FL_ONGROUND);
-#endif
 
        WarpZone_PostTeleportPlayer_Callback(player);
 }
@@ -85,15 +68,9 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1)
 {
        vector o0, a0, v0, o1, a1, v1, o10;
 
-#ifdef SVQC
        o0 = player.origin + player.view_ofs;
        v0 = player.velocity;
        a0 = player.angles;
-#elif defined(CSQC)
-       o0 = player.move_origin + player.view_ofs;
-       v0 = player.move_velocity;
-       a0 = player.move_angles;
-#endif
 
        o10 = o1 = WarpZone_TransformOrigin(wz, o0);
        v1 = WarpZone_TransformVelocity(wz, v0);
@@ -189,30 +166,26 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1)
        return 1;
 }
 
-void WarpZone_Touch (entity this)
+void WarpZone_Touch(entity this, entity toucher)
 {
-       if(other.classname == "trigger_warpzone")
+       if(toucher.classname == "trigger_warpzone")
                return;
 
-       if(time <= other.warpzone_teleport_finishtime) // already teleported this frame
+       if(time <= toucher.warpzone_teleport_finishtime) // already teleported this frame
                return;
 
        // FIXME needs a better check to know what is safe to teleport and what not
-#ifdef SVQC
-       if(other.movetype == MOVETYPE_NONE || other.movetype == MOVETYPE_FOLLOW || other.tag_entity)
-#elif defined(CSQC)
-       if(other.move_movetype == MOVETYPE_NONE || other.move_movetype == MOVETYPE_FOLLOW || other.tag_networkentity)
+       if((toucher.move_movetype == MOVETYPE_NONE && toucher.move_movetype == MOVETYPE_NONE) || toucher.move_movetype == MOVETYPE_FOLLOW || toucher.move_movetype == MOVETYPE_FOLLOW || toucher.tag_entity
+#ifdef CSQC
+       || tag_networkentity
 #endif
+       )
                return;
 
-       if(WarpZoneLib_ExactTrigger_Touch(this, other))
+       if(WarpZoneLib_ExactTrigger_Touch(this, toucher))
                return;
 
-#ifdef SVQC
-       if(WarpZone_PlaneDist(this, other.origin + other.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet)
-#elif defined(CSQC)
-       if(WarpZone_PlaneDist(this, other.move_origin + other.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet)
-#endif
+       if(WarpZone_PlaneDist(this, toucher.origin + toucher.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet)
                return;
 
        float f;
@@ -227,36 +200,32 @@ void WarpZone_Touch (entity this)
        //       24/(0.25/frametime)
        //       96*frametime
        float d;
-       d = 24 + max(vlen(other.mins), vlen(other.maxs));
-       if(IS_NOT_A_CLIENT(other))
-       #ifdef SVQC
-               f = -d / bound(frametime * d * 1, frametime * vlen(other.velocity), d);
-       #elif defined(CSQC)
-               f = -d / bound(frametime * d * 1, frametime * vlen(other.move_velocity), d);
-       #endif
+       d = 24 + max(vlen(toucher.mins), vlen(toucher.maxs));
+       if(IS_NOT_A_CLIENT(toucher))
+               f = -d / bound(frametime * d * 1, frametime * vlen(toucher.velocity), d);
        else
                f = -1;
-       if(WarpZone_Teleport(this, other, f, 0))
+       if(WarpZone_Teleport(this, toucher, f, 0))
        {
 #ifdef SVQC
                string save1, save2;
 
                save1 = this.target; this.target = string_null;
                save2 = this.target3; this.target3 = string_null;
-               SUB_UseTargets(this, other, other); // use other too?
+               SUB_UseTargets(this, toucher, toucher); // use toucher too?
                if (!this.target) this.target = save1;
                if (!this.target3) this.target3 = save2;
 
                save1 = this.target; this.target = string_null;
                save2 = this.target2; this.target2 = string_null;
-               SUB_UseTargets(this.enemy, other, other); // use other too?
+               SUB_UseTargets(this.enemy, toucher, toucher); // use toucher too?
                if (!this.target) this.target = save1;
                if (!this.target2) this.target2 = save2;
 #endif
        }
        else
        {
-               LOG_TRACE("WARPZONE FAIL AHAHAHAHAH))\n");
+               LOG_TRACE("WARPZONE FAIL AHAHAHAHAH))");
        }
 }
 
@@ -365,17 +334,8 @@ float WarpZone_CheckProjectileImpact(entity player)
 {
        vector o0, v0;
 
-       .vector orgvec, velvec;
-#ifdef SVQC
-       orgvec = origin;
-       velvec = velocity;
-#elif defined(CSQC)
-       orgvec = move_origin;
-       velvec = move_velocity;
-#endif
-
-       o0 = player.orgvec + player.view_ofs;
-       v0 = player.velvec;
+       o0 = player.origin + player.view_ofs;
+       v0 = player.velocity;
 
        // if we teleported shortly before, abort
        if(time <= player.warpzone_teleport_finishtime + 0.1)
@@ -393,19 +353,15 @@ float WarpZone_CheckProjectileImpact(entity player)
        LOG_INFO("impactfilter found something - and it even gets handled correctly - please tell divVerent that this code apparently gets triggered again\n");
 #endif
        LOG_INFO("Entity type: ", player.classname, "\n");
-       LOG_INFO("Origin: ", vtos(player.orgvec), "\n");
-       LOG_INFO("Velocity: ", vtos(player.velvec), "\n");
+       LOG_INFO("Origin: ", vtos(player.origin), "\n");
+       LOG_INFO("Velocity: ", vtos(player.velocity), "\n");
 
 #ifdef WARPZONELIB_REMOVEHACK
        return 0;
 #else
        // retry previous move
-#ifdef SVQC
        setorigin(player, player.warpzone_oldorigin);
-#elif defined(CSQC)
-       player.move_origin = player.warpzone_oldorigin;
-#endif
-       player.velvec = player.warpzone_oldvelocity;
+       player.velocity = player.warpzone_oldvelocity;
        if(WarpZone_Teleport(wz, player, 0, 1))
        {
                string save1, save2;
@@ -425,7 +381,7 @@ float WarpZone_CheckProjectileImpact(entity player)
        else
        {
                setorigin(player, o0 - player.view_ofs);
-               player.velvec = v0;
+               player.velocity = v0;
        }
 
        return +1;
@@ -434,9 +390,9 @@ float WarpZone_CheckProjectileImpact(entity player)
 #endif
 #endif
 
-float WarpZone_Projectile_Touch(entity this)
+float WarpZone_Projectile_Touch(entity this, entity toucher)
 {
-       if(other.classname == "trigger_warpzone")
+       if(toucher.classname == "trigger_warpzone")
                return true;
 
        // no further impacts if we teleported this frame!
@@ -496,7 +452,7 @@ float WarpZone_Projectile_Touch(entity this)
        }
 #endif
 
-       if(WarpZone_Projectile_Touch_ImpactFilter_Callback(this, other))
+       if(WarpZone_Projectile_Touch_ImpactFilter_Callback(this, toucher))
                return true;
 #endif
 
@@ -675,7 +631,6 @@ void WarpZone_InitStep_ClearTarget(entity this)
        this.enemy = NULL;
 }
 
-entity warpzone_first; .entity warpzone_next;
 void WarpZone_InitStep_FindTarget(entity this)
 {
        float i;
@@ -775,6 +730,8 @@ spawnfunc(trigger_warpzone)
        BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
        this.warpzone_next = warpzone_first;
        warpzone_first = this;
+
+       IL_PUSH(g_warpzones, this);
 }
 spawnfunc(func_camera)
 {
@@ -847,46 +804,51 @@ void WarpZone_StartFrame()
                WarpZone_PostInitialize_Callback();
        }
 
-       entity oldother = other;
+       if(warpzone_warpzones_exist)
+       {
+               IL_EACH(g_projectiles, true,
+               {
+                       WarpZone_StoreProjectileData(it);
+               });
+       }
+               
 
-       FOREACH_ENTITY(!is_pure(it),
+       FOREACH_CLIENT(true,
        {
                if(warpzone_warpzones_exist)
-                       WarpZone_StoreProjectileData(it);
+                       WarpZone_StoreProjectileData(it); // TODO: not actually needed
 
                if(IS_OBSERVER(it) || it.solid == SOLID_NOT)
                if(IS_CLIENT(it)) // we don't care about it being a bot
                {
-                       other = it; // player
-
                        // warpzones
                        if (warpzone_warpzones_exist) {
                                entity e = WarpZone_Find(it.origin + it.mins, it.origin + it.maxs);
                                if (e)
-                               if (!WarpZoneLib_ExactTrigger_Touch(e, other))
+                               if (!WarpZoneLib_ExactTrigger_Touch(e, it))
                                if (WarpZone_PlaneDist(e, it.origin + it.view_ofs) <= 0)
                                        WarpZone_Teleport(e, it, -1, 0); // NOT triggering targets by this!
                        }
 
                        // teleporters
-                       if(other.teleportable)
+                       if(it.teleportable)
                        {
                                entity ent = Teleport_Find(it.origin + it.mins, it.origin + it.maxs);
                                if (ent)
-                               if (!WarpZoneLib_ExactTrigger_Touch(ent, other))
-                                       Simple_TeleportPlayer(ent, other); // NOT triggering targets by this!
+                               if (!WarpZoneLib_ExactTrigger_Touch(ent, it))
+                                       Simple_TeleportPlayer(ent, it); // NOT triggering targets by this!
                        }
                }
        });
-       other = oldother;
 }
 
 .float warpzone_reconnecting;
 bool visible_to_some_client(entity ent)
 {
-       FOREACH_ENTITY(!IS_NOT_A_CLIENT(it), LAMBDA(
-               if (IS_PLAYER(it) && IS_REAL_CLIENT(it) && checkpvs(it.origin + it.view_ofs, ent)) return true;
-       ));
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && checkpvs(it.origin + it.view_ofs, ent),
+       {
+               return true;
+       });
        return false;
 }
 void trigger_warpzone_reconnect_use(entity this, entity actor, entity trigger)
index 9abea2a689ac90b710e5652ea546d1bf4c80aac1..4287ef60de798195e0e963d93736a7348a88d175 100644 (file)
@@ -1,9 +1,10 @@
-#ifndef LIB_WARPZONE_SERVER_H
-#define LIB_WARPZONE_SERVER_H
+#pragma once
 
 #ifdef SVQC
+entity warpzone_first; .entity warpzone_next;
+
 void WarpZone_StartFrame();
-float WarpZone_Projectile_Touch(entity this);
+float WarpZone_Projectile_Touch(entity this, entity toucher);
 
 // THESE must be defined by calling QC code:
 void WarpZone_PostTeleportPlayer_Callback(entity pl);
@@ -17,5 +18,3 @@ void WarpZone_PlayerPhysics_FixVAngle(entity this);
 
 void WarpZone_PostInitialize_Callback();
 #endif
-
-#endif
index 73f09c0e07e871c65c738e887547ec95e8e46d31..d5533647f51e7727cb19b886e9fd3eea6887bb8c 100644 (file)
@@ -43,7 +43,7 @@ void WarpZoneLib_ExactTrigger_Init(entity this)
                setsize(this, this.mins * this.scale, this.maxs * this.scale);
        else
                setsize(this, this.mins, this.maxs);
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.solid = SOLID_TRIGGER;
        this.model = "";
 }
index 39d1d24699caab93e8d209c4489844f7cbd6fb5c..b73289927b474878d93081425a75876f7b2ae62d 100644 (file)
@@ -1,9 +1,7 @@
-#ifndef LIB_WARPZONE_UTIL_SERVER_H
-#define LIB_WARPZONE_UTIL_SERVER_H
+#pragma once
 
 float WarpZoneLib_MoveOutOfSolid(entity e);
 float WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher);
 #ifdef SVQC
 void WarpZoneLib_ExactTrigger_Init(entity this);
 #endif
-#endif
diff --git a/qcsrc/menu/_all.inc b/qcsrc/menu/_all.inc
new file mode 100644 (file)
index 0000000..e5198f5
--- /dev/null
@@ -0,0 +1,10 @@
+#include <menu/_all.qh>
+#include "_mod.inc"
+
+#include "anim/_mod.inc"
+#include "command/_mod.inc"
+#include "item/_mod.inc"
+#include "mutators/_mod.inc"
+#include "xonotic/_mod.inc"
+
+#include <common/_all.inc>
index 0427f322ae3884c451f0954ba3cafcc3b962660b..009feb577c00fbaa1454ed373845f7cd6998fedb 100644 (file)
@@ -15,14 +15,14 @@ CLASS(Animation, Object)
        METHOD(Animation, resumeAnim, void(Animation this));
        METHOD(Animation, isFinished, float(Animation this));
        METHOD(Animation, finishAnim, void(Animation this));
-       ATTRIB(Animation, object, entity, NULL)
+       ATTRIB(Animation, object, entity);
        void setterDummy(Animation this, float) {}
-       ATTRIB(Animation, setter, void(Animation this, float), setterDummy)
-       ATTRIB(Animation, value, float, 0)
-       ATTRIB(Animation, startTime, float, 0)
-       ATTRIB(Animation, duration, float, 0)
-       ATTRIB(Animation, startValue, float, 0)
-       ATTRIB(Animation, delta, float, 0)
-       ATTRIB(Animation, stopped, float, false)
-       ATTRIB(Animation, finished, float, false)
+       ATTRIB(Animation, setter, void(Animation this, float), setterDummy);
+       ATTRIB(Animation, value, float, 0);
+       ATTRIB(Animation, startTime, float, 0);
+       ATTRIB(Animation, duration, float, 0);
+       ATTRIB(Animation, startValue, float, 0);
+       ATTRIB(Animation, delta, float, 0);
+       ATTRIB(Animation, stopped, float, false);
+       ATTRIB(Animation, finished, float, false);
 ENDCLASS(Animation)
index 5e39191dd658c71d38dd363393483854ba7cb3e4..3009ab4eaf8b00f47fd4efdf362b3c0ba42b67b0 100644 (file)
@@ -37,7 +37,7 @@
 
                if (n) n.prevSibling = p;
                else this.lastChild = p;
-               remove(other);
+               delete(other);
        }
 
        METHOD(AnimHost, removeAllAnim, void(entity this))
index 1292b7d8b4e25380f57edb8cd89b4ce1c287460d..85b03aecf35cf7ae93ee5050dde2efe3108cfd48 100644 (file)
@@ -14,6 +14,6 @@ CLASS(AnimHost, Object)
        METHOD(AnimHost, finishAllAnim, void(entity));
        METHOD(AnimHost, finishObjAnim, void(entity, entity));
        METHOD(AnimHost, tickAll, void(entity));
-       ATTRIB(AnimHost, firstChild, entity, NULL)
-       ATTRIB(AnimHost, lastChild, entity, NULL)
+       ATTRIB(AnimHost, firstChild, entity);
+       ATTRIB(AnimHost, lastChild, entity);
 ENDCLASS(AnimHost)
index 832a87c73bc070d069ab376e3e8e0832e3bcfcb9..bb2c0dbc0a6c39076ca516994aac715625763853 100644 (file)
@@ -10,5 +10,5 @@ float easingQuadInOut(float, float, float, float);
 CLASS(Easing, Animation)
        METHOD(Easing, calcValue, float(entity, float, float, float, float));
        METHOD(Easing, setMath, void(entity, float(float, float, float, float)));
-       ATTRIB(Easing, math, float(float, float, float, float), easingLinear)
+       ATTRIB(Easing, math, float(float, float, float, float), easingLinear);
 ENDCLASS(Easing)
index d21db6920bb198be95815379f4a7adc61c98de7b..abf18de96fcc7598d9d8b100a8893c4a8f52a56b 100644 (file)
@@ -5,9 +5,9 @@ CLASS(Keyframe, Animation)
        METHOD(Keyframe, addEasing, entity(entity, float, float, float(float, float, float, float)));
        METHOD(Keyframe, addAnim, void(entity, entity));
        METHOD(Keyframe, calcValue, float(entity, float, float, float, float));
-       ATTRIB(Keyframe, currentChild, entity, NULL)
-       ATTRIB(Keyframe, firstChild, entity, NULL)
-       ATTRIB(Keyframe, lastChild, entity, NULL)
+       ATTRIB(Keyframe, currentChild, entity);
+       ATTRIB(Keyframe, firstChild, entity);
+       ATTRIB(Keyframe, lastChild, entity);
 ENDCLASS(Keyframe)
 entity makeHostedKeyframe(entity, void(entity, float), float, float, float);
 entity makeKeyframe(entity, void(entity, float), float, float, float);
diff --git a/qcsrc/menu/auto-super.pl b/qcsrc/menu/auto-super.pl
deleted file mode 100644 (file)
index 00926d0..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-my %classoffile = ();
-my %classes = ();
-my %baseclass = ();
-my %methods = ();
-my %attrs = ();
-my %methodnames = ();
-my %old2new = ();
-
-print STDERR "Scanning...\n";
-for my $f(@ARGV)
-{
-       open my $fh, '<', $f;
-       while(<$fh>)
-       {
-               if(/^CLASS\(([^)]*)\)(?:\s*EXTENDS\(([^)]*)\))?/)
-               {
-                       $classes{$1} = defined($2) ? $2 : "Object";
-                       $classoffile{$f} = $1;
-               }
-               if(/^\s*METHOD\(([^),]*),\s*([^),]*)/)
-               {
-                       $methods{$1}{$2} = $1;
-                       $methodnames{"$1"."_"."$2"} = $f;
-                       $old2new{"$2$1"} = "$1"."_"."$2";
-               }
-               if(/^\s*ATTRIB(?:ARRAY)?\(([^),]*),\s*([^),]*)/)
-               {
-                       $attrs{$1}{$2} = $1;
-               }
-       }
-       close $fh;
-}
-
-# propagate down methods etc.
-print STDERR "Propagating...\n";
-for my $class(keys %classes)
-{
-       print STDERR "$class";
-       my $base = $class;
-       for(;;)
-       {
-               $base = $classes{$base};
-               last if not defined $base;
-               print STDERR " -> $base";
-               while(my ($method, $definingclass) = each %{$methods{$base}})
-               {
-                       $methods{$class}{$method} = $definingclass
-                               if not defined $methods{$class}{$method};
-               }
-               while(my ($attr, $definingclass) = each %{$attrs{$base}})
-               {
-                       $attrs{$class}{$attr} = $definingclass
-                               if not defined $attrs{$class}{$attr};
-               }
-       }
-       print STDERR "\n";
-}
-
-# change all calls to base method to super, complain about skipping
-print STDERR "Fixing...\n";
-for my $f(@ARGV)
-{
-       open my $fh, '<', $f;
-       my $s = do { undef local $/; <$fh>; };
-       my $s0 = $s;
-       close $fh;
-
-       my $class = $classoffile{$f};
-       my $base = $classes{$class};
-       next if not defined $base;
-
-       for(keys %old2new)
-       {
-               $s =~ s/\b$_\b/$old2new{$_}/g;
-       }
-
-       my @methods_super = map { [ $methods{$base}{$_} . "_" . $_, "SUPER($class).$_" ]; } keys %{$methods{$base}};
-       for(@methods_super)
-       {
-               my ($search, $replace) = @$_;
-               my $n = ($s =~ s/\b$search\b/$replace/g);
-               print STDERR "[$f] $search -> $replace... $n replacements\n"
-                       if $n;
-       }
-
-       for(grep { $methodnames{$_} ne $f } keys %methodnames)
-       {
-               if($s =~ /\b$_\b/)
-               {
-                       print STDERR "[$f] calls non-super external method directly: $_\n";
-               }
-       }
-
-       if($s ne $s0)
-       {
-               print STDERR "Rewriting $f...\n";
-               open my $fh, '>', $f;
-               print $fh $s;
-               close $fh;
-       }
-}
index e721f357a3953f975ff1b1de84bf0d2f627506d0..0bcef50de71c6d6f4b59d1a8574916f6c2866652 100644 (file)
@@ -1,3 +1,2 @@
 // generated file; do not modify
-#include <menu/command/all.qc>
 #include <menu/command/menu_cmd.qc>
index 5e66557301f031473d87d5506f1ce672af33b80f..91c0a8f35e80a23ccdc487acb7a1ba1853d107e6 100644 (file)
@@ -1,3 +1,2 @@
 // generated file; do not modify
-#include <menu/command/all.qh>
 #include <menu/command/menu_cmd.qh>
diff --git a/qcsrc/menu/command/all.qc b/qcsrc/menu/command/all.qc
deleted file mode 100644 (file)
index 283cca0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <common/command/all.qc>
index 0f87cbb0959cb38df039f1cac70f796cabd0185b..72aadf6247bac401778744fe630ea81443979fba 100644 (file)
@@ -5,7 +5,7 @@
 
 #include "../mutators/events.qh"
 
-#include <common/command/generic.qh>
+#include <common/command/_mod.qh>
 
 .entity firstChild, nextSibling;
 
index b2ce50382a5e16b7a647085f93cd59ad16aede0f..01184a4bf5d026e2c5c0580c8ef625e07610772a 100644 (file)
@@ -271,7 +271,7 @@ void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector the
 void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
 {
        if(theSize.x <= 0 || theSize.y <= 0) {
-               LOG_TRACE("Drawing zero size text?\n");
+               LOG_TRACE("Drawing zero size text?");
                return;
        }
 
index 118a45426788a044479e882f2961a7ae0ede0605..00dae2ed37db4ae2eb84c01977b2a78917831ebb 100644 (file)
@@ -19,12 +19,12 @@ CLASS(Item, Object)
        METHOD(Item, hideNotify, void(Item));
        METHOD(Item, toString, string(Item));
        METHOD(Item, destroy, void(Item));
-       ATTRIB(Item, focused, float, 0)
-       ATTRIB(Item, focusable, float, 0)
-       ATTRIB(Item, allowFocusSound, float, 0)
-       ATTRIB(Item, parent, entity, NULL)
-       ATTRIB(Item, preferredFocusPriority, float, 0)
-       ATTRIB(Item, origin, vector, '0 0 0')
-       ATTRIB(Item, size, vector, '0 0 0')
-       ATTRIB(Item, tooltip, string, string_null)
+       ATTRIB(Item, focused, float, 0);
+       ATTRIB(Item, focusable, float, 0);
+       ATTRIB(Item, allowFocusSound, float, 0);
+       ATTRIB(Item, parent, entity);
+       ATTRIB(Item, preferredFocusPriority, float, 0);
+       ATTRIB(Item, origin, vector, '0 0 0');
+       ATTRIB(Item, size, vector, '0 0 0');
+       ATTRIB(Item, tooltip, string);
 ENDCLASS(Item)
index c956d362d22827af30204d1c7f2e45d561512ea7..9db9360f8f08a2fd2236a5ef17e7dbbf0f3c4ec0 100644 (file)
@@ -5,21 +5,21 @@ CLASS(BorderImage, Label)
        METHOD(BorderImage, configureBorderImage, void(entity, string, float, vector, string, float));
        METHOD(BorderImage, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(BorderImage, recalcPositionWithText, void(entity, string));
-       ATTRIB(BorderImage, isBold, float, 1)
+       ATTRIB(BorderImage, isBold, float, 1);
        METHOD(BorderImage, draw, void(entity));
-       ATTRIB(BorderImage, src, string, string_null)
-       ATTRIB(BorderImage, borderHeight, float, 0)
-       ATTRIB(BorderImage, borderVec, vector, '0 0 0')
-       ATTRIB(BorderImage, color, vector, '1 1 1')
-       ATTRIB(BorderImage, closeButton, entity, NULL)
-       ATTRIB(BorderImage, realFontSize_Nexposeed, vector, '0 0 0')
-       ATTRIB(BorderImage, realOrigin_Nexposeed, vector, '0 0 0')
-       ATTRIB(BorderImage, isNexposeeTitleBar, float, 0)
-       ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0)
-       ATTRIB(BorderImage, zoomedOutTitleBar, float, 0)
-       ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0')
-       ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0')
-       ATTRIB(BorderImage, saveRelSize, vector, '0 0 0')
+       ATTRIB(BorderImage, src, string);
+       ATTRIB(BorderImage, borderHeight, float, 0);
+       ATTRIB(BorderImage, borderVec, vector, '0 0 0');
+       ATTRIB(BorderImage, color, vector, '1 1 1');
+       ATTRIB(BorderImage, closeButton, entity);
+       ATTRIB(BorderImage, realFontSize_Nexposeed, vector, '0 0 0');
+       ATTRIB(BorderImage, realOrigin_Nexposeed, vector, '0 0 0');
+       ATTRIB(BorderImage, isNexposeeTitleBar, float, 0);
+       ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0);
+       ATTRIB(BorderImage, zoomedOutTitleBar, float, 0);
+       ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0');
+       ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0');
+       ATTRIB(BorderImage, saveRelSize, vector, '0 0 0');
 ENDCLASS(BorderImage)
 
 .vector colorC, colorF;
index 6fd2bc8882b032c00b924765c8cee27015bdbccb..1a164245c8f269368067b3b43c4ae6f92fd9066f 100644 (file)
@@ -13,30 +13,30 @@ CLASS(Button, Label)
        METHOD(Button, mouseDrag, float(entity, vector));
        METHOD(Button, mouseRelease, float(entity, vector));
        METHOD(Button, playClickSound, void(entity));
-       ATTRIB(Button, onClick, void(entity, entity), func_null)
-       ATTRIB(Button, onClickEntity, entity, NULL)
-       ATTRIB(Button, src, string, string_null)
-       ATTRIB(Button, srcSuffix, string, string_null)
-       ATTRIB(Button, src2, string, string_null) // is centered, same aspect, and stretched to label size
-       ATTRIB(Button, src2scale, float, 1)
-       ATTRIB(Button, srcMulti, float, 1)        // 0: button square left, text right; 1: button stretched, text over it
-       ATTRIB(Button, buttonLeftOfText, float, 0)
-       ATTRIB(Button, focusable, float, 1)
-       ATTRIB(Button, allowFocusSound, float, 1)
-       ATTRIB(Button, pressed, float, 0)
-       ATTRIB(Button, clickTime, float, 0)
-       ATTRIB(Button, applyButton, entity, NULL)
-       ATTRIB(Button, disableOnClick, bool, false)
-       ATTRIB(Button, disabled, float, 0)
-       ATTRIB(Button, disabledAlpha, float, 0.3)
-       ATTRIB(Button, forcePressed, float, 0)
-       ATTRIB(Button, color, vector, '1 1 1')
-       ATTRIB(Button, colorC, vector, '1 1 1')
-       ATTRIB(Button, colorF, vector, '1 1 1')
-       ATTRIB(Button, colorD, vector, '1 1 1')
-       ATTRIB(Button, color2, vector, '1 1 1')
-       ATTRIB(Button, alpha2, float, 1)
+       ATTRIB(Button, onClick, void(entity, entity));
+       ATTRIB(Button, onClickEntity, entity);
+       ATTRIB(Button, src, string);
+       ATTRIB(Button, srcSuffix, string);
+       ATTRIB(Button, src2, string); // is centered, same aspect, and stretched to label size
+       ATTRIB(Button, src2scale, float, 1);
+       ATTRIB(Button, srcMulti, float, 1);        // 0: button square left, text right; 1: button stretched, text over it
+       ATTRIB(Button, buttonLeftOfText, float, 0);
+       ATTRIB(Button, focusable, float, 1);
+       ATTRIB(Button, allowFocusSound, float, 1);
+       ATTRIB(Button, pressed, float, 0);
+       ATTRIB(Button, clickTime, float, 0);
+       ATTRIB(Button, applyButton, entity);
+       ATTRIB(Button, disableOnClick, bool, false);
+       ATTRIB(Button, disabled, float, 0);
+       ATTRIB(Button, disabledAlpha, float, 0.3);
+       ATTRIB(Button, forcePressed, float, 0);
+       ATTRIB(Button, color, vector, '1 1 1');
+       ATTRIB(Button, colorC, vector, '1 1 1');
+       ATTRIB(Button, colorF, vector, '1 1 1');
+       ATTRIB(Button, colorD, vector, '1 1 1');
+       ATTRIB(Button, color2, vector, '1 1 1');
+       ATTRIB(Button, alpha2, float, 1);
 
-       ATTRIB(Button, origin, vector, '0 0 0')
-       ATTRIB(Button, size, vector, '0 0 0')
+       ATTRIB(Button, origin, vector, '0 0 0');
+       ATTRIB(Button, size, vector, '0 0 0');
 ENDCLASS(Button)
index 32f6b0f5023a22eb89698851804093ce07956789..931e4f79c8f1c09f32270b9ae8aa1c5b2b23f12d 100644 (file)
@@ -7,10 +7,10 @@ CLASS(CheckBox, Button)
        METHOD(CheckBox, playClickSound, void(entity));
        METHOD(CheckBox, toString, string(entity));
        METHOD(CheckBox, setChecked, void(entity, float));
-       ATTRIB(CheckBox, useDownAsChecked, float, 0)
-       ATTRIB(CheckBox, checked, float, 0)
+       ATTRIB(CheckBox, useDownAsChecked, float, 0);
+       ATTRIB(CheckBox, checked, float, 0);
     void CheckBox_Click(entity me, entity other);
-       ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click)
-       ATTRIB(CheckBox, srcMulti, float, 0)
-       ATTRIB(CheckBox, disabled, float, 0)
+       ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click);
+       ATTRIB(CheckBox, srcMulti, float, 0);
+       ATTRIB(CheckBox, disabled, float, 0);
 ENDCLASS(CheckBox)
index dbb4cf2211c27f2b641c9d017e42fe353cfa512e..8273f5ddde38a43ec3e153d3a5f02247f452a775 100644 (file)
@@ -25,12 +25,12 @@ CLASS(Container, Item)
        METHOD(Container, showNotify, void(entity));
        METHOD(Container, hideNotify, void(entity));
        METHOD(Container, preferredFocusedGrandChild, entity(entity));
-       ATTRIB(Container, focusable, float, 0)
-       ATTRIB(Container, firstChild, entity, NULL)
-       ATTRIB(Container, lastChild, entity, NULL)
-       ATTRIB(Container, focusedChild, entity, NULL)
-       ATTRIB(Container, savedFocus, entity, NULL)
-       ATTRIB(Container, shown, float, 0)
+       ATTRIB(Container, focusable, float, 0);
+       ATTRIB(Container, firstChild, entity);
+       ATTRIB(Container, lastChild, entity);
+       ATTRIB(Container, focusedChild, entity);
+       ATTRIB(Container, savedFocus, entity);
+       ATTRIB(Container, shown, float, 0);
 
        METHOD(Container, enterSubitem, void(entity, entity));
        METHOD(Container, enterLieSubitem, void(entity, vector, vector, vector, float));
index c4a78fa568802c305e763aedca9e96eb59bfb595..9e0ea95ebad6dc9555357c310f01a0c9ac9ad793 100644 (file)
 
        void Dialog_close(entity me)
        {
-               if (me.parent.instanceOfNexposee) ExposeeCloseButton_Click(me, me.parent);
-               else if (me.parent.instanceOfModalController) DialogCloseButton_Click(me, me);
+               if (me.parent.instanceOfNexposee)
+               {
+                       ExposeeCloseButton_Click(me, me.parent);
+                       if(me.hideMenuOnClose)
+                       {
+                               me.hideMenuOnClose = false;
+                               m_hide();
+                       }
+               }
+               else if (me.parent.instanceOfModalController)
+                       DialogCloseButton_Click(me, me);
        }
 
        float Dialog_keyDown(entity me, float key, float ascii, float shift)
                                return 1;
                        }
                }
-               return SUPER(Dialog).keyDown(me, key, ascii, shift);
+               float r = SUPER(Dialog).keyDown(me, key, ascii, shift);
+               if (!me.closable && key == K_ESCAPE)
+                       return 1;
+               return r;
        }
index 17343e7cd2f3697d7914f8d29742ddf125465ea2..6fa9b26ab38038fe9c271b495c8ff4cbd9a4052e 100644 (file)
@@ -30,43 +30,45 @@ CLASS(Dialog, InputContainer)
        METHOD(Dialog, TR, void(entity));
        METHOD(Dialog, gotoRC, void(entity, float, float));
 
-       ATTRIB(Dialog, isTabRoot, float, 1)
-       ATTRIB(Dialog, closeButton, entity, NULL)
-       ATTRIB(Dialog, intendedHeight, float, 0)
-       ATTRIB(Dialog, itemOrigin, vector, '0 0 0')
-       ATTRIB(Dialog, itemSize, vector, '0 0 0')
-       ATTRIB(Dialog, itemSpacing, vector, '0 0 0')
-       ATTRIB(Dialog, currentRow, float, 0)
-       ATTRIB(Dialog, currentColumn, float, 0)
-       ATTRIB(Dialog, firstColumn, float, 0)
+       ATTRIB(Dialog, isTabRoot, float, 1);
+       ATTRIB(Dialog, closeButton, entity);
+       ATTRIB(Dialog, intendedHeight, float, 0);
+       ATTRIB(Dialog, itemOrigin, vector, '0 0 0');
+       ATTRIB(Dialog, itemSize, vector, '0 0 0');
+       ATTRIB(Dialog, itemSpacing, vector, '0 0 0');
+       ATTRIB(Dialog, currentRow, float, 0);
+       ATTRIB(Dialog, currentColumn, float, 0);
+       ATTRIB(Dialog, firstColumn, float, 0);
 
        // to be customized
-       ATTRIB(Dialog, closable, float, 1)
-       ATTRIB(Dialog, title, string, "Form1")  // ;)
-       ATTRIB(Dialog, color, vector, '1 0.5 1')
-       ATTRIB(Dialog, intendedWidth, float, 0)
-       ATTRIB(Dialog, rows, float, 3)
-       ATTRIB(Dialog, columns, float, 2)
+       ATTRIB(Dialog, closable, float, 1);
+       ATTRIB(Dialog, title, string, "Form1");
+       ATTRIB(Dialog, color, vector, '1 0.5 1');
+       ATTRIB(Dialog, intendedWidth, float, 0);
+       ATTRIB(Dialog, rows, float, 3);
+       ATTRIB(Dialog, columns, float, 2);
 
-       ATTRIB(Dialog, marginTop, float, 0)     // pixels
-       ATTRIB(Dialog, marginBottom, float, 0)  // pixels
-       ATTRIB(Dialog, marginLeft, float, 0)    // pixels
-       ATTRIB(Dialog, marginRight, float, 0)   // pixels
-       ATTRIB(Dialog, columnSpacing, float, 0) // pixels
-       ATTRIB(Dialog, rowSpacing, float, 0)    // pixels
-       ATTRIB(Dialog, rowHeight, float, 0)     // pixels
-       ATTRIB(Dialog, titleHeight, float, 0)   // pixels
-       ATTRIB(Dialog, titleFontSize, float, 0) // pixels; if 0, title causes no margin
-       ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0)
-       ATTRIB(Dialog, zoomedOutTitleBar, float, 0)
+       ATTRIB(Dialog, marginTop, float, 0);     // pixels
+       ATTRIB(Dialog, marginBottom, float, 0);  // pixels
+       ATTRIB(Dialog, marginLeft, float, 0);    // pixels
+       ATTRIB(Dialog, marginRight, float, 0);   // pixels
+       ATTRIB(Dialog, columnSpacing, float, 0); // pixels
+       ATTRIB(Dialog, rowSpacing, float, 0);    // pixels
+       ATTRIB(Dialog, rowHeight, float, 0);     // pixels
+       ATTRIB(Dialog, titleHeight, float, 0);   // pixels
+       ATTRIB(Dialog, titleFontSize, float, 0); // pixels; if 0, title causes no margin
+       ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0);
+       ATTRIB(Dialog, zoomedOutTitleBar, float, 0);
 
-       ATTRIB(Dialog, requiresConnection, float, 0)  // set to true if the dialog requires a connection to be opened
+       ATTRIB(Dialog, requiresConnection, float, 0);  // set to true if the dialog requires a connection to be opened
 
-       ATTRIB(Dialog, backgroundImage, string, string_null)
-       ATTRIB(Dialog, borderLines, float, 1)
-       ATTRIB(Dialog, closeButtonImage, string, string_null)
+       ATTRIB(Dialog, backgroundImage, string);
+       ATTRIB(Dialog, borderLines, float, 1);
+       ATTRIB(Dialog, closeButtonImage, string);
 
-       ATTRIB(Dialog, frame, entity, NULL)
+       ATTRIB(Dialog, hideMenuOnClose, bool, false);
+
+       ATTRIB(Dialog, frame, entity);
 ENDCLASS(Dialog)
 
 void Dialog_Close(entity button, entity me);
index 06ac91f025fcb53c80804f4dcb2f4ac0f56e2838..59405ec169f15448df4e7f028c67b8e43ef56c0b 100644 (file)
                }
                return 1;
        }
-       void Image_setZoom(entity me, float z, float atMousePosition)
+       void Image_setZoom(entity me, float newzoom, float atMousePosition)
        {
                float prev_zoomFactor;
                prev_zoomFactor = me.zoomFactor;
-               if (z < 0)  // multiply by the current zoomFactor (but can also snap to real dimensions or to box)
+               if (newzoom < 0)  // multiply by the current zoomFactor (but can also snap to real dimensions or to box)
                {
-                       me.zoomFactor *= -z;
+                       me.zoomFactor *= -newzoom;
                        float realSize_in_the_middle, boxSize_in_the_middle;
                        realSize_in_the_middle = ((prev_zoomFactor - 1) * (me.zoomFactor - 1) < 0);
                        boxSize_in_the_middle = (me.zoomBox > 0 && (prev_zoomFactor - me.zoomBox) * (me.zoomFactor - me.zoomBox) < 0);
                                me.zoomFactor = me.zoomBox; // snap to box
                        }
                }
-               else if (z == 0)                    // reset (no zoom)
+               else if (newzoom == 0)                    // reset (no zoom)
                {
                        if (me.zoomBox > 0) me.zoomFactor = me.zoomBox;
                        else me.zoomFactor = 1;
                }
                else  // directly set
                {
-                       me.zoomFactor = z;
+                       me.zoomFactor = newzoom;
                }
                me.zoomFactor = bound(1 / 16, me.zoomFactor, 16);
                if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax) me.zoomFactor = me.zoomMax;
index 35bca5985a056c7fc949d027caaa8f655d486cbb..726f328600d79b570c210b3bb234006868ef2982 100644 (file)
@@ -11,18 +11,18 @@ CLASS(Image, Item)
        METHOD(Image, setZoom, void(entity, float, float));
        METHOD(Image, drag_setStartPos, float(entity, vector));
        METHOD(Image, drag, float(entity, vector));
-       ATTRIB(Image, src, string, string_null)
-       ATTRIB(Image, color, vector, '1 1 1')
-       ATTRIB(Image, forcedAspect, float, 0)        // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size
-       ATTRIB(Image, zoomBox, float, 0)             // used by forcedAspect -2 when the image is larger than the containing box
-       ATTRIB(Image, zoomFactor, float, 1)
-       ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0')
-       ATTRIB(Image, zoomSnapToTheBox, float, 1)    // snap the zoomed in image to the box borders when zooming/dragging it
-       ATTRIB(Image, zoomTime, float, 0)
-       ATTRIB(Image, zoomLimitedByTheBox, float, 0) // forbids zoom if image would be larger than the containing box
-       ATTRIB(Image, zoomMax, float, 0)
-       ATTRIB(Image, start_zoomOffset, vector, '0 0 0')
-       ATTRIB(Image, start_coords, vector, '0 0 0')
-       ATTRIB(Image, imgOrigin, vector, '0 0 0')
-       ATTRIB(Image, imgSize, vector, '0 0 0')
+       ATTRIB(Image, src, string);
+       ATTRIB(Image, color, vector, '1 1 1');
+       ATTRIB(Image, forcedAspect, float, 0);        // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size
+       ATTRIB(Image, zoomBox, float, 0);             // used by forcedAspect -2 when the image is larger than the containing box
+       ATTRIB(Image, zoomFactor, float, 1);
+       ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0');
+       ATTRIB(Image, zoomSnapToTheBox, float, 1);    // snap the zoomed in image to the box borders when zooming/dragging it
+       ATTRIB(Image, zoomTime, float, 0);
+       ATTRIB(Image, zoomLimitedByTheBox, float, 0); // forbids zoom if image would be larger than the containing box
+       ATTRIB(Image, zoomMax, float, 0);
+       ATTRIB(Image, start_zoomOffset, vector, '0 0 0');
+       ATTRIB(Image, start_coords, vector, '0 0 0');
+       ATTRIB(Image, imgOrigin, vector, '0 0 0');
+       ATTRIB(Image, imgSize, vector, '0 0 0');
 ENDCLASS(Image)
index cfb576cfaa3c4544fec21fd9b286bb9cc7c90553..e3eede653b9c0c297bf698b36ed1b4108ae5035b 100644 (file)
@@ -14,31 +14,31 @@ CLASS(InputBox, Label)
        METHOD(InputBox, showNotify, void(entity));
        METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector));
 
-       ATTRIB(InputBox, src, string, string_null)
+       ATTRIB(InputBox, src, string);
 
-       ATTRIB(InputBox, cursorPos, float, 0)  // characters
-       ATTRIB(InputBox, scrollPos, float, 0)  // widths
+       ATTRIB(InputBox, cursorPos, float, 0);  // characters
+       ATTRIB(InputBox, scrollPos, float, 0);  // widths
 
-       ATTRIB(InputBox, focusable, float, 1)
-       ATTRIB(InputBox, allowFocusSound, float, 1)
-       ATTRIB(InputBox, disabled, float, 0)
-       ATTRIB(InputBox, lastChangeTime, float, 0)
-       ATTRIB(InputBox, dragScrollTimer, float, 0)
-       ATTRIB(InputBox, dragScrollPos, vector, '0 0 0')
-       ATTRIB(InputBox, pressed, float, 0)
-       ATTRIB(InputBox, editColorCodes, float, 1)
-       ATTRIB(InputBox, forbiddenCharacters, string, "")
-       ATTRIB(InputBox, color, vector, '1 1 1')
-       ATTRIB(InputBox, colorF, vector, '1 1 1')
-       ATTRIB(InputBox, maxLength, float, 255)  // if negative, it counts bytes, not chars
-       ATTRIB(InputBox, applyButton, entity, NULL)
+       ATTRIB(InputBox, focusable, float, 1);
+       ATTRIB(InputBox, allowFocusSound, float, 1);
+       ATTRIB(InputBox, disabled, float, 0);
+       ATTRIB(InputBox, lastChangeTime, float, 0);
+       ATTRIB(InputBox, dragScrollTimer, float, 0);
+       ATTRIB(InputBox, dragScrollPos, vector, '0 0 0');
+       ATTRIB(InputBox, pressed, float, 0);
+       ATTRIB(InputBox, editColorCodes, float, 1);
+       ATTRIB(InputBox, forbiddenCharacters, string, "");
+       ATTRIB(InputBox, color, vector, '1 1 1');
+       ATTRIB(InputBox, colorF, vector, '1 1 1');
+       ATTRIB(InputBox, maxLength, float, 255);  // if negative, it counts bytes, not chars
+       ATTRIB(InputBox, applyButton, entity);
 
-       ATTRIB(InputBox, enableClearButton, float, 1)
-       ATTRIB(InputBox, clearButton, entity, NULL)
-       ATTRIB(InputBox, cb_width, float, 0)
-       ATTRIB(InputBox, cb_pressed, float, 0)
-       ATTRIB(InputBox, cb_focused, float, 0)
-       ATTRIB(InputBox, cb_color, vector, '1 1 1')
-       ATTRIB(InputBox, cb_colorF, vector, '1 1 1')
-       ATTRIB(InputBox, cb_colorC, vector, '1 1 1')
+       ATTRIB(InputBox, enableClearButton, float, 1);
+       ATTRIB(InputBox, clearButton, entity);
+       ATTRIB(InputBox, cb_width, float, 0);
+       ATTRIB(InputBox, cb_pressed, float, 0);
+       ATTRIB(InputBox, cb_focused, float, 0);
+       ATTRIB(InputBox, cb_color, vector, '1 1 1');
+       ATTRIB(InputBox, cb_colorF, vector, '1 1 1');
+       ATTRIB(InputBox, cb_colorC, vector, '1 1 1');
 ENDCLASS(InputBox)
index 3840721e35f9a032ab3f008471bb53e22a806802..37c46240f77fdcd8bf704b1b6d999aaa3de5e254 100644 (file)
@@ -20,7 +20,8 @@
        float InputContainer_keyDown(entity me, float scan, float ascii, float shift)
        {
                entity f, ff;
-               if (SUPER(InputContainer).keyDown(me, scan, ascii, shift)) return 1;
+               if (SUPER(InputContainer).keyDown(me, scan, ascii, shift))
+                       return 1;
                if (scan == K_ESCAPE)
                {
                        f = me.focusedChild;
index 5d84189fafeb34653d352b98699be757bea5b51f..9ae4a537506bcad1a54fb7721b7816dd74685d35 100644 (file)
@@ -11,6 +11,6 @@ CLASS(InputContainer, Container)
        METHOD(InputContainer, resizeNotify, void(entity, vector, vector, vector, vector));
 
        METHOD(InputContainer, _changeFocusXY, bool(entity this, vector pos));
-       ATTRIB(InputContainer, mouseFocusedChild, entity, NULL)
-       ATTRIB(InputContainer, isTabRoot, float, 0)
+       ATTRIB(InputContainer, mouseFocusedChild, entity);
+       ATTRIB(InputContainer, isTabRoot, float, 0);
 ENDCLASS(InputContainer)
index ca9ec00b09dd0f90618c935575e47e9108b239b7..d21b5676bd597d32da93d61b3f3a850d15d5af7b 100644 (file)
@@ -38,7 +38,7 @@
                {
                        if (!me.overrideRealOrigin_x) me.realOrigin_x = me.keepspaceLeft;
                        if (!me.overrideCondenseFactor) me.condenseFactor = spaceAvail / spaceUsed;
-                       LOG_TRACEF("NOTE: label text %s too wide for label, condensed by factor %f\n", t, me.condenseFactor);
+                       LOG_TRACEF("NOTE: label text %s too wide for label, condensed by factor %f", t, me.condenseFactor);
                }
 
                if (!me.overrideRealOrigin_y)
index 428958c20107c0fbf52dfbabe7304ee9b45765bf..f91ae8ad3cb12a13265d085fe937cf32959f2a95 100644 (file)
@@ -8,27 +8,27 @@ CLASS(Label, Item)
        METHOD(Label, setText, void(entity, string));
        METHOD(Label, toString, string(entity));
        METHOD(Label, recalcPositionWithText, void(entity, string));
-       ATTRIB(Label, isBold, float, 0)
-       ATTRIB(Label, text, string, string_null)
-       ATTRIB(Label, currentText, string, string_null)
-       ATTRIB(Label, fontSize, float, 8)
-       ATTRIB(Label, align, float, 0.5)
-       ATTRIB(Label, allowCut, float, 0)
-       ATTRIB(Label, allowColors, float, 0)
-       ATTRIB(Label, keepspaceLeft, float, 0) // for use by subclasses (radiobuttons for example)
-       ATTRIB(Label, keepspaceRight, float, 0)
-       ATTRIB(Label, marginLeft, float, 0)    // alternate way to specify keepspace* (in characters from the font)
-       ATTRIB(Label, marginRight, float, 0)
-       ATTRIB(Label, realFontSize, vector, '0 0 0')
-       ATTRIB(Label, realOrigin, vector, '0 0 0')
-       ATTRIB(Label, alpha, float, 0.7)
-       ATTRIB(Label, colorL, vector, SKINCOLOR_TEXT)
-       ATTRIB(Label, disabled, float, 0)
-       ATTRIB(Label, disabledAlpha, float, 0.3)
-       ATTRIB(Label, textEntity, entity, NULL)
-       ATTRIB(Label, allowWrap, float, 0)
-       ATTRIB(Label, recalcPos, float, 0)
-       ATTRIB(Label, condenseFactor, float, 1)
-       ATTRIB(Label, overrideRealOrigin, vector, '0 0 0')
-       ATTRIB(Label, overrideCondenseFactor, float, 0)
+       ATTRIB(Label, isBold, float, 0);
+       ATTRIB(Label, text, string);
+       ATTRIB(Label, currentText, string);
+       ATTRIB(Label, fontSize, float, 8);
+       ATTRIB(Label, align, float, 0.5);
+       ATTRIB(Label, allowCut, float, 0);
+       ATTRIB(Label, allowColors, float, 0);
+       ATTRIB(Label, keepspaceLeft, float, 0); // for use by subclasses (radiobuttons for example);
+       ATTRIB(Label, keepspaceRight, float, 0);
+       ATTRIB(Label, marginLeft, float, 0);    // alternate way to specify keepspace* (in characters from the font);
+       ATTRIB(Label, marginRight, float, 0);
+       ATTRIB(Label, realFontSize, vector, '0 0 0');
+       ATTRIB(Label, realOrigin, vector, '0 0 0');
+       ATTRIB(Label, alpha, float, 0.7);
+       ATTRIB(Label, colorL, vector, SKINCOLOR_TEXT);
+       ATTRIB(Label, disabled, float, 0);
+       ATTRIB(Label, disabledAlpha, float, 0.3);
+       ATTRIB(Label, textEntity, entity);
+       ATTRIB(Label, allowWrap, float, 0);
+       ATTRIB(Label, recalcPos, float, 0);
+       ATTRIB(Label, condenseFactor, float, 1);
+       ATTRIB(Label, overrideRealOrigin, vector, '0 0 0');
+       ATTRIB(Label, overrideCondenseFactor, float, 0);
 ENDCLASS(Label)
index d9d63ab8c5abe2a6958b8c463fecdd985313ff4d..aa10c20b3b0f6d04bac8d5810e8b53b74bb799f2 100644 (file)
                oldshift = draw_shift;
                oldscale = draw_scale;
 
-               float y;
                i = me.getItemAtPos(me, me.scrollPos);
-               y = me.getItemStart(me, i) - me.scrollPos;
-               for ( ; i < me.nItems && y < 1; ++i)
+               float j = me.getItemStart(me, i) - me.scrollPos;
+               for ( ; i < me.nItems && j < 1; ++i)
                {
-                       draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
+                       draw_shift = boxToGlobal(eY * j, oldshift, oldscale);
                        vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i);
                        absSize = boxToGlobalSize(relSize, me.size);
                        draw_scale = boxToGlobalSize(relSize, oldscale);
                        me.drawListBoxItem(me, i, absSize, (me.selectedItem == i), (me.focusedItem == i));
-                       y += relSize.y;
+                       j += relSize.y;
                }
                draw_ClearClip();
 
index 461125e303fbc34bb2e41c9c2344f5cdf21a450d..56fda91cc15d82e163e362e9a250c38a8c1bb525 100644 (file)
@@ -11,46 +11,46 @@ CLASS(ListBox, Item)
        METHOD(ListBox, mouseDrag, float(entity, vector));
        METHOD(ListBox, mouseRelease, float(entity, vector));
        METHOD(ListBox, focusLeave, void(entity));
-       ATTRIB(ListBox, focusable, float, 1)
-       ATTRIB(ListBox, focusedItem, int, -1)
-       ATTRIB(ListBox, focusedItemAlpha, float, 0.3)
+       ATTRIB(ListBox, focusable, float, 1);
+       ATTRIB(ListBox, focusedItem, int, -1);
+       ATTRIB(ListBox, focusedItemAlpha, float, 0.3);
        METHOD(ListBox, setFocusedItem, void(entity, int));
-       ATTRIB(ListBox, mouseMoveOffset, float, -1)  // let know where the cursor is when the list scrolls without moving the cursor
-       ATTRIB(ListBox, allowFocusSound, float, 1)
-       ATTRIB(ListBox, selectedItem, int, 0)
-       ATTRIB(ListBox, size, vector, '0 0 0')
-       ATTRIB(ListBox, origin, vector, '0 0 0')
-       ATTRIB(ListBox, scrollPos, float, 0)  // measured in window heights, fixed when needed
-       ATTRIB(ListBox, scrollPosTarget, float, 0)
+       ATTRIB(ListBox, mouseMoveOffset, float, -1);  // let know where the cursor is when the list scrolls without moving the cursor
+       ATTRIB(ListBox, allowFocusSound, float, 1);
+       ATTRIB(ListBox, selectedItem, int, 0);
+       ATTRIB(ListBox, size, vector, '0 0 0');
+       ATTRIB(ListBox, origin, vector, '0 0 0');
+       ATTRIB(ListBox, scrollPos, float, 0);  // measured in window heights, fixed when needed
+       ATTRIB(ListBox, scrollPosTarget, float, 0);
        METHOD(ListBox, isScrolling, bool(entity));
-       ATTRIB(ListBox, needScrollToItem, float, -1)
+       ATTRIB(ListBox, needScrollToItem, float, -1);
        METHOD(ListBox, scrollToItem, void(entity, int));
-       ATTRIB(ListBox, previousValue, float, 0)
-       ATTRIB(ListBox, pressed, float, 0)  // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released
-       ATTRIB(ListBox, pressOffset, float, 0)
+       ATTRIB(ListBox, previousValue, float, 0);
+       ATTRIB(ListBox, pressed, float, 0);  // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released
+       ATTRIB(ListBox, pressOffset, float, 0);
 
        METHOD(ListBox, updateControlTopBottom, void(entity));
-       ATTRIB(ListBox, controlTop, float, 0)
-       ATTRIB(ListBox, controlBottom, float, 0)
-       ATTRIB(ListBox, controlWidth, float, 0)
-       ATTRIB(ListBox, dragScrollPos, vector, '0 0 0')
-       ATTRIB(ListBox, selectionDoesntMatter, bool, false) // improves scrolling by keys for lists that don't need to show an active selection
+       ATTRIB(ListBox, controlTop, float, 0);
+       ATTRIB(ListBox, controlBottom, float, 0);
+       ATTRIB(ListBox, controlWidth, float, 0);
+       ATTRIB(ListBox, dragScrollPos, vector, '0 0 0');
+       ATTRIB(ListBox, selectionDoesntMatter, bool, false); // improves scrolling by keys for lists that don't need to show an active selection
 
-       ATTRIB(ListBox, src, string, string_null)           // scrollbar
-       ATTRIB(ListBox, color, vector, '1 1 1')
-       ATTRIB(ListBox, color2, vector, '1 1 1')
-       ATTRIB(ListBox, colorC, vector, '1 1 1')
-       ATTRIB(ListBox, colorF, vector, '1 1 1')
-       ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance
-       ATTRIB(ListBox, scrollbarWidth, float, 0)   // pixels
-       ATTRIB(ListBox, nItems, float, 42)          // FIXME: why?!?
-       ATTRIB(ListBox, itemHeight, float, 0)
-    ATTRIB(ListBox, itemAbsSize, vector, '0 0 0')
-       ATTRIB(ListBox, colorBG, vector, '0 0 0')
-       ATTRIB(ListBox, alphaBG, float, 0)
+       ATTRIB(ListBox, src, string);           // scrollbar
+       ATTRIB(ListBox, color, vector, '1 1 1');
+       ATTRIB(ListBox, color2, vector, '1 1 1');
+       ATTRIB(ListBox, colorC, vector, '1 1 1');
+       ATTRIB(ListBox, colorF, vector, '1 1 1');
+       ATTRIB(ListBox, tolerance, vector, '0 0 0'); // drag tolerance
+       ATTRIB(ListBox, scrollbarWidth, float, 0);   // pixels
+       ATTRIB(ListBox, nItems, float, 42);          // FIXME: why?!?
+       ATTRIB(ListBox, itemHeight, float, 0);
+    ATTRIB(ListBox, itemAbsSize, vector, '0 0 0');
+       ATTRIB(ListBox, colorBG, vector, '0 0 0');
+       ATTRIB(ListBox, alphaBG, float, 0);
 
-       ATTRIB(ListBox, lastClickedItem, float, -1)
-       ATTRIB(ListBox, lastClickedTime, float, 0)
+       ATTRIB(ListBox, lastClickedItem, float, -1);
+       ATTRIB(ListBox, lastClickedTime, float, 0);
 
        METHOD(ListBox, drawListBoxItem, void(entity, int, vector, bool, bool)); // item number, width/height, isSelected, isFocused
        METHOD(ListBox, clickListBoxItem, void(entity, float, vector));          // item number, relative clickpos
index 27faaa86bfbc15b2df1e97d163198e35c1cd4d96..e950b8d87ebc99cbb5e00e030e995e72a6c9f55c 100644 (file)
@@ -34,10 +34,10 @@ CLASS(ModalController, Container)
        METHOD(ModalController, initializeDialog, void(entity, entity));
 
        METHOD(ModalController, switchState, void(entity, entity, float, float));
-       ATTRIB(ModalController, origin, vector, '0 0 0')
-       ATTRIB(ModalController, size, vector, '0 0 0')
-       ATTRIB(ModalController, previousButton, entity, NULL)
-       ATTRIB(ModalController, fadedAlpha, float, 0.3)
+       ATTRIB(ModalController, origin, vector, '0 0 0');
+       ATTRIB(ModalController, size, vector, '0 0 0');
+       ATTRIB(ModalController, previousButton, entity);
+       ATTRIB(ModalController, fadedAlpha, float, 0.3);
 ENDCLASS(ModalController)
 
 .float ModalController_state;
index 2d8e3ec49dfc1d8ea60bfdd23440bdaff74c548e..4159648f974207260d502ab8efb4ad90907258f4 100644 (file)
@@ -13,14 +13,14 @@ CLASS(Nexposee, Container)
        METHOD(Nexposee, focusEnter, void(entity));
        METHOD(Nexposee, close, void(entity));
 
-       ATTRIB(Nexposee, animationState, float, -1)
-       ATTRIB(Nexposee, animationFactor, float, 0)
-       ATTRIB(Nexposee, selectedChild, entity, NULL)
-       ATTRIB(Nexposee, mouseFocusedChild, entity, NULL)
+       ATTRIB(Nexposee, animationState, float, -1);
+       ATTRIB(Nexposee, animationFactor, float, 0);
+       ATTRIB(Nexposee, selectedChild, entity);
+       ATTRIB(Nexposee, mouseFocusedChild, entity);
        METHOD(Nexposee, addItem, void(entity, entity, vector, vector, float));
        METHOD(Nexposee, calc, void(entity));
        METHOD(Nexposee, setNexposee, void(entity, entity, vector, float, float));
-       ATTRIB(Nexposee, mousePosition, vector, '0 0 0')
+       ATTRIB(Nexposee, mousePosition, vector, '0 0 0');
        METHOD(Nexposee, pullNexposee, void(entity, entity, vector));
 ENDCLASS(Nexposee)
 
index 53aa7ff2e8af8da330637c597c3ddf4b5db021d7..7bd737787657ff8f74eb642c5a22f600f69e8a2d 100644 (file)
@@ -4,8 +4,8 @@
 void RadioButton_Click(entity me, entity other);
 CLASS(RadioButton, CheckBox)
        METHOD(RadioButton, configureRadioButton, void(entity, string, float, string, float, float));
-       ATTRIB(RadioButton, checked, float, 0)
-       ATTRIB(RadioButton, group, float, 0)
-       ATTRIB(RadioButton, allowDeselect, float, 0)
-       ATTRIB(RadioButton, onClick, void(entity, entity), RadioButton_Click)
+       ATTRIB(RadioButton, checked, float, 0);
+       ATTRIB(RadioButton, group, float, 0);
+       ATTRIB(RadioButton, allowDeselect, float, 0);
+       ATTRIB(RadioButton, onClick, void(entity, entity), RadioButton_Click);
 ENDCLASS(RadioButton)
index f5e8fd496f7f446092d752ecb026ae6da762827e..3b7327788c0d130afb230471b076b8b8262587e9 100644 (file)
@@ -20,31 +20,31 @@ CLASS(Slider, Label)
        METHOD(Slider, setValue, void(entity, float));
        METHOD(Slider, setSliderValue, void(entity, float));
        METHOD(Slider, showNotify, void(entity));
-       ATTRIB(Slider, src, string, string_null)
-       ATTRIB(Slider, focusable, float, 1)
-       ATTRIB(Slider, allowFocusSound, float, 1)
-       ATTRIB(Slider, value, float, 0)
-       ATTRIB(Slider, animated, float, 1)
-       ATTRIB(Slider, sliderValue, float, 0)
-       ATTRIB(Slider, sliderAnim, entity, NULL)
-       ATTRIB(Slider, valueMin, float, 0)
-       ATTRIB(Slider, valueMax, float, 0)
-       ATTRIB(Slider, valueStep, float, 0)
-       ATTRIB(Slider, valueDigits, float, 0)
-       ATTRIB(Slider, valueKeyStep, float, 0)
-       ATTRIB(Slider, valuePageStep, float, 0)
-       ATTRIB(Slider, valueDisplayMultiplier, float, 1.0)
-       ATTRIB(Slider, textSpace, float, 0)
-       ATTRIB(Slider, controlWidth, float, 0)
-       ATTRIB(Slider, pressed, float, 0)
-       ATTRIB(Slider, pressOffset, float, 0)
-       ATTRIB(Slider, previousValue, float, 0)
-       ATTRIB(Slider, tolerance, vector, '0 0 0')
-       ATTRIB(Slider, disabled, float, 0)
-       ATTRIB(Slider, color, vector, '1 1 1')
-       ATTRIB(Slider, color2, vector, '1 1 1')
-       ATTRIB(Slider, colorD, vector, '1 1 1')
-       ATTRIB(Slider, colorC, vector, '1 1 1')
-       ATTRIB(Slider, colorF, vector, '1 1 1')
-       ATTRIB(Slider, disabledAlpha, float, 0.3)
+       ATTRIB(Slider, src, string);
+       ATTRIB(Slider, focusable, float, 1);
+       ATTRIB(Slider, allowFocusSound, float, 1);
+       ATTRIB(Slider, value, float, 0);
+       ATTRIB(Slider, animated, float, 1);
+       ATTRIB(Slider, sliderValue, float, 0);
+       ATTRIB(Slider, sliderAnim, entity);
+       ATTRIB(Slider, valueMin, float, 0);
+       ATTRIB(Slider, valueMax, float, 0);
+       ATTRIB(Slider, valueStep, float, 0);
+       ATTRIB(Slider, valueDigits, float, 0);
+       ATTRIB(Slider, valueKeyStep, float, 0);
+       ATTRIB(Slider, valuePageStep, float, 0);
+       ATTRIB(Slider, valueDisplayMultiplier, float, 1.0);
+       ATTRIB(Slider, textSpace, float, 0);
+       ATTRIB(Slider, controlWidth, float, 0);
+       ATTRIB(Slider, pressed, float, 0);
+       ATTRIB(Slider, pressOffset, float, 0);
+       ATTRIB(Slider, previousValue, float, 0);
+       ATTRIB(Slider, tolerance, vector, '0 0 0');
+       ATTRIB(Slider, disabled, float, 0);
+       ATTRIB(Slider, color, vector, '1 1 1');
+       ATTRIB(Slider, color2, vector, '1 1 1');
+       ATTRIB(Slider, colorD, vector, '1 1 1');
+       ATTRIB(Slider, colorC, vector, '1 1 1');
+       ATTRIB(Slider, colorF, vector, '1 1 1');
+       ATTRIB(Slider, disabledAlpha, float, 0.3);
 ENDCLASS(Slider)
index e9ba81a90387f1d71cce1aabba9d4f584bf4feda..18c9e495979f1ae24a5b2a495bf575c4571006eb 100644 (file)
@@ -2,25 +2,25 @@
 
 #include "dialog.qh"
 CLASS(Tab, Dialog)
-       ATTRIB(Tab, isTabRoot, float, 0)
-       ATTRIB(Tab, closable, float, 0)
-       ATTRIB(Tab, rootDialog, float, 0)
-       ATTRIB(Tab, title, string, string_null)
-       ATTRIB(Tab, titleFontSize, float, 0)  // pixels
+       ATTRIB(Tab, isTabRoot, float, 0);
+       ATTRIB(Tab, closable, float, 0);
+       ATTRIB(Tab, rootDialog, float, 0);
+       ATTRIB(Tab, title, string);
+       ATTRIB(Tab, titleFontSize, float, 0);  // pixels
 
        // still to be customized
-       ATTRIB(Tab, intendedWidth, float, 0)
-       ATTRIB(Tab, rows, float, 3)
-       ATTRIB(Tab, columns, float, 2)
+       ATTRIB(Tab, intendedWidth, float, 0);
+       ATTRIB(Tab, rows, float, 3);
+       ATTRIB(Tab, columns, float, 2);
 
-       ATTRIB(Tab, marginTop, float, 0)     // pixels
-       ATTRIB(Tab, marginBottom, float, 0)  // pixels
-       ATTRIB(Tab, marginLeft, float, 0)    // pixels
-       ATTRIB(Tab, marginRight, float, 0)   // pixels
-       ATTRIB(Tab, columnSpacing, float, 0) // pixels
-       ATTRIB(Tab, rowSpacing, float, 0)    // pixels
-       ATTRIB(Tab, rowHeight, float, 0)     // pixels
-       ATTRIB(Tab, titleHeight, float, 0)   // pixels
+       ATTRIB(Tab, marginTop, float, 0);     // pixels
+       ATTRIB(Tab, marginBottom, float, 0);  // pixels
+       ATTRIB(Tab, marginLeft, float, 0);    // pixels
+       ATTRIB(Tab, marginRight, float, 0);   // pixels
+       ATTRIB(Tab, columnSpacing, float, 0); // pixels
+       ATTRIB(Tab, rowSpacing, float, 0);    // pixels
+       ATTRIB(Tab, rowHeight, float, 0);     // pixels
+       ATTRIB(Tab, titleHeight, float, 0);   // pixels
 
-       ATTRIB(Tab, backgroundImage, string, string_null)
+       ATTRIB(Tab, backgroundImage, string);
 ENDCLASS(Tab)
index 0703942f95b03b5b733741416a55a5c082c0a72e..9a6a586a16c9bd2eed839628d4ccb8f44cf8e109 100644 (file)
@@ -14,7 +14,7 @@ CLASS(TextSlider, Slider)
        METHOD(TextSlider, addValue, void(entity, string, string));
        METHOD(TextSlider, insertValue, void(entity, float, string, string));
        METHOD(TextSlider, configureTextSliderValues, void(entity, string));
-       ATTRIBARRAY(TextSlider, valueStrings, string, 256)
-       ATTRIBARRAY(TextSlider, valueIdentifiers, string, 256)
-       ATTRIB(TextSlider, nValues, int, 0)
+       ATTRIBARRAY(TextSlider, valueStrings, string, 256);
+       ATTRIBARRAY(TextSlider, valueIdentifiers, string, 256);
+       ATTRIB(TextSlider, nValues, int, 0);
 ENDCLASS(TextSlider)
index b0c4ec880c1b2da9c3bb014270f4e6fc5c2b8e80..3039b4a7d4222bda22044f2dc60c8aee5a89c7ad 100644 (file)
@@ -1,3 +1,5 @@
+#include "matrix.qh"
+
 var void MX_Handle(int buf, string ancestor)
 {
     string type = json_get(buf, strcat(ancestor, ".type"));
diff --git a/qcsrc/menu/matrix.qh b/qcsrc/menu/matrix.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 96d98b9c502149cfe54d18461cbc08ec9ff0c1e6..f0e6646ca93d7115063ea9d36018565271229ad3 100644 (file)
@@ -17,8 +17,8 @@
 
 #include "xonotic/util.qh"
 
-#include "../common/items/all.qh"
-#include <common/weapons/all.qh>
+#include "../common/items/_mod.qh"
+#include <common/weapons/_all.qh>
 #include "../common/mapinfo.qh"
 #include "../common/mutators/base.qh"
 
@@ -237,7 +237,7 @@ void m_keyup(float key, float ascii)
                if (mouseButtonsPressed < 0)
                {
                        mouseButtonsPressed = 0;
-                       LOG_TRACE("Warning: released an already released button\n");
+                       LOG_TRACE("Warning: released an already released button");
                }
        }
        if (key == K_ALT) menuShiftState &= ~S_ALT;
@@ -270,7 +270,8 @@ void m_keydown(float key, float ascii)
        else
        {
                draw_reset_cropped();
-               if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3) main.mousePress(main, menuMousePos);
+               if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3)
+                       main.mousePress(main, menuMousePos);
                if (!main.keyDown(main, key, ascii, menuShiftState))
                {
                        // disable menu on unhandled ESC
@@ -285,7 +286,7 @@ void m_keydown(float key, float ascii)
                if (mouseButtonsPressed > 10)
                {
                        mouseButtonsPressed = 10;
-                       LOG_TRACE("Warning: pressed an already pressed button\n");
+                       LOG_TRACE("Warning: pressed an already pressed button");
                }
        }
        if (key == K_ALT) menuShiftState |= S_ALT;
@@ -848,6 +849,7 @@ void m_draw(float width, float height)
        postMenuDraw();
 
        frametime = 0;
+       IL_ENDFRAME();
 }
 
 void m_display()
@@ -975,6 +977,8 @@ void m_goto(string itemname)
 
                if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
                {
+                       if(!Menu_Active)
+                               e.hideMenuOnClose = true;
                        m_hide();
                        m_activate_window(e);
                        m_setpointerfocus(e);
index 9b15d8caa6345efb2792e3d336e4d037489a3bfd..404c6f2e5a409a588e1014849337e1133fca81fd 100644 (file)
@@ -1,14 +1,9 @@
 #include <lib/_all.inc>
 
-#include "../menu/_mod.inc"
-#include "anim/_mod.inc"
-#include "command/_mod.inc"
-#include "item/_mod.inc"
-#include "mutators/_mod.inc"
-#include "xonotic/_mod.inc"
-
-#include <common/_all.inc>
+#if XONOTIC
+#include <menu/_all.inc>
+#endif
 
-#if BUILD_MOD
-#include "../../mod/menu/progs.inc"
+#ifdef BUILD_MOD
+#include <mod/menu/progs.inc>
 #endif
index 7cc2d2d3b4d49084f8bbbd2c4fce75cdf100c3d6..4287e24e59f3ddfbc156f04b3a287fcdd894b113 100644 (file)
@@ -19,7 +19,7 @@
 //#define SKINSTRING(name,def) case #name: break
 #define SKINSTRING(name,def) case #name: SKIN##name = strzone(_value); break
        // I know this leaks memory when skin is read multiple times. Screw it.
-#define SKINEND case "": break; case "//": break; default: LOG_TRACE("Invalid key in skin file: ", key, "\n"); } }
+#define SKINEND case "": break; case "//": break; default: LOG_TRACE("Invalid key in skin file: ", key); } }
 #include "skin-customizables.inc"
 #undef SKINEND
 #undef SKINSTRING
index 867f77b8402b797db0297b0e34c7343aff1a8b20..577c8225803461225c818ce07f6cf396d1806f10 100644 (file)
@@ -79,6 +79,7 @@
 #include <menu/xonotic/dialog_singleplayer.qc>
 #include <menu/xonotic/dialog_singleplayer_winner.qc>
 #include <menu/xonotic/dialog_teamselect.qc>
+#include <menu/xonotic/dialog_uid2name.qc>
 #include <menu/xonotic/gametypelist.qc>
 #include <menu/xonotic/hudskinlist.qc>
 #include <menu/xonotic/image.qc>
index adfefc05d7d5210ff1edd713b751a79afca1bf05..b6e34eff247606d774eb1d321fe9f7539071d3ff 100644 (file)
@@ -79,6 +79,7 @@
 #include <menu/xonotic/dialog_singleplayer.qh>
 #include <menu/xonotic/dialog_singleplayer_winner.qh>
 #include <menu/xonotic/dialog_teamselect.qh>
+#include <menu/xonotic/dialog_uid2name.qh>
 #include <menu/xonotic/gametypelist.qh>
 #include <menu/xonotic/hudskinlist.qh>
 #include <menu/xonotic/image.qh>
index 14b6893a7d16f93a56725c737c49563ce8cd7746..b34014bc3879fb4497642a0304d50966e90f5d9b 100644 (file)
@@ -3,7 +3,7 @@
 #include "button.qh"
 CLASS(XonoticBigButton, XonoticButton)
        METHOD(XonoticBigButton, configureXonoticBigButton, void(entity, string, vector));
-       ATTRIB(XonoticBigButton, image, string, SKINGFX_BUTTON_BIG)
-       ATTRIB(XonoticBigButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY)
+       ATTRIB(XonoticBigButton, image, string, SKINGFX_BUTTON_BIG);
+       ATTRIB(XonoticBigButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY);
 ENDCLASS(XonoticBigButton)
 entity makeXonoticBigButton(string theText, vector theColor);
index 4713fa3709765d050bc70576282975c23d8b37c7..b6bb782aee8639335411309dec8a31dacdff8d7d 100644 (file)
@@ -3,8 +3,8 @@
 #include "commandbutton.qh"
 CLASS(XonoticBigCommandButton, XonoticCommandButton)
        METHOD(XonoticBigCommandButton, configureXonoticBigCommandButton, void(entity, string, vector, string, float, string));
-       ATTRIB(XonoticBigCommandButton, image, string, SKINGFX_BUTTON_BIG)
-       ATTRIB(XonoticBigCommandButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY)
+       ATTRIB(XonoticBigCommandButton, image, string, SKINGFX_BUTTON_BIG);
+       ATTRIB(XonoticBigCommandButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY);
 ENDCLASS(XonoticBigCommandButton)
 entity makeXonoticBigCommandButton_T(string theText, vector theColor, string theCommand, float closesMenu, string theTooltip);
 entity makeXonoticBigCommandButton(string theText, vector theColor, string theCommand, float closesMenu);
index c7e85fabe0838b8c9659d379c32e40c4258e8283..e4a520d2b67afcee5d76610999e4231e6faeedf9 100644 (file)
@@ -3,17 +3,17 @@
 #include "../item/button.qh"
 CLASS(XonoticButton, Button)
        METHOD(XonoticButton, configureXonoticButton, void(entity, string, vector, string));
-       ATTRIB(XonoticButton, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticButton, image, string, SKINGFX_BUTTON)
-       ATTRIB(XonoticButton, grayImage, string, SKINGFX_BUTTON_GRAY)
-       ATTRIB(XonoticButton, color, vector, SKINCOLOR_BUTTON_N)
-       ATTRIB(XonoticButton, colorC, vector, SKINCOLOR_BUTTON_C)
-       ATTRIB(XonoticButton, colorF, vector, SKINCOLOR_BUTTON_F)
-       ATTRIB(XonoticButton, colorD, vector, SKINCOLOR_BUTTON_D)
-       ATTRIB(XonoticButton, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticButton, disabledAlpha, float, SKINALPHA_DISABLED)
-       ATTRIB(XonoticButton, marginLeft, float, SKINMARGIN_BUTTON)  // chars
-       ATTRIB(XonoticButton, marginRight, float, SKINMARGIN_BUTTON) // chars
+       ATTRIB(XonoticButton, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticButton, image, string, SKINGFX_BUTTON);
+       ATTRIB(XonoticButton, grayImage, string, SKINGFX_BUTTON_GRAY);
+       ATTRIB(XonoticButton, color, vector, SKINCOLOR_BUTTON_N);
+       ATTRIB(XonoticButton, colorC, vector, SKINCOLOR_BUTTON_C);
+       ATTRIB(XonoticButton, colorF, vector, SKINCOLOR_BUTTON_F);
+       ATTRIB(XonoticButton, colorD, vector, SKINCOLOR_BUTTON_D);
+       ATTRIB(XonoticButton, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticButton, disabledAlpha, float, SKINALPHA_DISABLED);
+       ATTRIB(XonoticButton, marginLeft, float, SKINMARGIN_BUTTON);  // chars
+       ATTRIB(XonoticButton, marginRight, float, SKINMARGIN_BUTTON); // chars
 ENDCLASS(XonoticButton)
 
 entity makeXonoticButton_T(string theText, vector theColor, string theTooltip);
index 17585a86d4f51233f0d6963b3dee85d4ec1d81db..784926f7df8636cd58d8513a24e5aed8b0dc475c 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticCampaignList, XonoticListBox)
        METHOD(XonoticCampaignList, configureXonoticCampaignList, void(entity));
-       ATTRIB(XonoticCampaignList, rowsPerItem, float, 10)
+       ATTRIB(XonoticCampaignList, rowsPerItem, float, 10);
        METHOD(XonoticCampaignList, draw, void(entity));
        METHOD(XonoticCampaignList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticCampaignList, doubleClickListBoxItem, void(entity, float, vector));
@@ -13,31 +13,31 @@ CLASS(XonoticCampaignList, XonoticListBox)
        METHOD(XonoticCampaignList, campaignGo, void(entity, float));
        METHOD(XonoticCampaignList, destroy, void(entity));
 
-       ATTRIB(XonoticCampaignList, campaignGlob, float, 0)
-       ATTRIB(XonoticCampaignList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticCampaignList, columnPreviewOrigin, float, 0)
-       ATTRIB(XonoticCampaignList, columnPreviewSize, float, 0)
-       ATTRIB(XonoticCampaignList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticCampaignList, columnNameSize, float, 0)
-       ATTRIB(XonoticCampaignList, columnCheckMarkOrigin, float, 0)
-       ATTRIB(XonoticCampaignList, columnCheckMarkSize, float, 0)
-       ATTRIB(XonoticCampaignList, checkMarkOrigin, vector, '0 0 0')
-       ATTRIB(XonoticCampaignList, checkMarkSize, vector, '0 0 0')
-       ATTRIB(XonoticCampaignList, realUpperMargin1, float, 0)
-       ATTRIB(XonoticCampaignList, realUpperMargin2, float, 0)
+       ATTRIB(XonoticCampaignList, campaignGlob, float, 0);
+       ATTRIB(XonoticCampaignList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticCampaignList, columnPreviewOrigin, float, 0);
+       ATTRIB(XonoticCampaignList, columnPreviewSize, float, 0);
+       ATTRIB(XonoticCampaignList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticCampaignList, columnNameSize, float, 0);
+       ATTRIB(XonoticCampaignList, columnCheckMarkOrigin, float, 0);
+       ATTRIB(XonoticCampaignList, columnCheckMarkSize, float, 0);
+       ATTRIB(XonoticCampaignList, checkMarkOrigin, vector, '0 0 0');
+       ATTRIB(XonoticCampaignList, checkMarkSize, vector, '0 0 0');
+       ATTRIB(XonoticCampaignList, realUpperMargin1, float, 0);
+       ATTRIB(XonoticCampaignList, realUpperMargin2, float, 0);
 
-       ATTRIB(XonoticCampaignList, origin, vector, '0 0 0')
-       ATTRIB(XonoticCampaignList, itemAbsSize, vector, '0 0 0')
-       ATTRIB(XonoticCampaignList, emptyLineHeight, float, 0.5)
+       ATTRIB(XonoticCampaignList, origin, vector, '0 0 0');
+       ATTRIB(XonoticCampaignList, itemAbsSize, vector, '0 0 0');
+       ATTRIB(XonoticCampaignList, emptyLineHeight, float, 0.5);
 
-       ATTRIB(XonoticCampaignList, campaignIndex, float, 0)
-       ATTRIB(XonoticCampaignList, cvarName, string, string_null)
+       ATTRIB(XonoticCampaignList, campaignIndex, float, 0);
+       ATTRIB(XonoticCampaignList, cvarName, string);
        METHOD(XonoticCampaignList, loadCvars, void(entity));
        METHOD(XonoticCampaignList, saveCvars, void(entity));
 
-       ATTRIB(XonoticCampaignList, buttonNext, entity, NULL)
-       ATTRIB(XonoticCampaignList, buttonPrev, entity, NULL)
-       ATTRIB(XonoticCampaignList, labelTitle, entity, NULL)
+       ATTRIB(XonoticCampaignList, buttonNext, entity);
+       ATTRIB(XonoticCampaignList, buttonPrev, entity);
+       ATTRIB(XonoticCampaignList, labelTitle, entity);
 ENDCLASS(XonoticCampaignList)
 entity makeXonoticCampaignList();
 void CampaignList_LoadMap(entity btn, entity me);
index c8d5b313492076922bd4fdf026069aa6d39ba8e1..3993e1dbc0a727c1ba3272a21ce8ae1b53eda8e1 100644 (file)
@@ -6,15 +6,15 @@ CLASS(XonoticCharmap, XonoticPicker)
        METHOD(XonoticCharmap, focusLeave, void(entity));
        METHOD(XonoticCharmap, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticCharmap, keyDown, float(entity, float, float, float));
-       ATTRIB(XonoticCharmap, inputBox, entity, NULL)
-       ATTRIB(XonoticCharmap, realFontSize, vector, '0 0 0')
+       ATTRIB(XonoticCharmap, inputBox, entity);
+       ATTRIB(XonoticCharmap, realFontSize, vector, '0 0 0');
 
-       ATTRIB(XonoticCharmap, rows, float, 10)
-       ATTRIB(XonoticCharmap, columns, float, 14)
+       ATTRIB(XonoticCharmap, rows, float, 10);
+       ATTRIB(XonoticCharmap, columns, float, 14);
 
        METHOD(XonoticCharmap, cellSelect, void(entity, vector));
        METHOD(XonoticCharmap, cellIsValid, bool(entity, vector));
        METHOD(XonoticCharmap, cellDraw, void(entity, vector, vector));
-       ATTRIB(XonoticCharmap, charOffset, vector, '0 0 0')
+       ATTRIB(XonoticCharmap, charOffset, vector, '0 0 0');
 ENDCLASS(XonoticCharmap)
 entity makeXonoticCharmap(entity controlledInputBox);
index b863518981b7b9fb5023e881fc695754e7d7cfcb..949b01c41da50c353beb177a70991d4683f28741 100644 (file)
@@ -2,28 +2,28 @@
 
 entity makeXonoticCheckBox_T(float isInverted, string theCvar, string theText, string theTooltip)
 {
-       float y, n;
+       float m, n;
        if(isInverted > 1)
        {
                n = isInverted - 1;
-               y = -n;
+               m = -n;
        }
        else if(isInverted < -1)
        {
                n = isInverted + 1;
-               y = -n;
+               m = -n;
        }
        else if(isInverted == 1)
        {
                n = 1;
-               y = 0;
+               m = 0;
        }
        else
        {
                n = 0;
-               y = 1;
+               m = 1;
        }
-       return makeXonoticCheckBoxEx_T(y, n, theCvar, theText, theTooltip);
+       return makeXonoticCheckBoxEx_T(m, n, theCvar, theText, theTooltip);
 }
 entity makeXonoticCheckBox(float isInverted, string theCvar, string theText)
 {
index 90a1bf5d4b67e4906223b4c8bf5392e5e82e0dab..e46c9ce4a0bb0f20c342c7f5737cc7ec531ffeb9 100644 (file)
@@ -4,24 +4,24 @@
 CLASS(XonoticCheckBox, CheckBox)
        METHOD(XonoticCheckBox, configureXonoticCheckBox, void(entity, float, float, string, string, string));
        METHOD(XonoticCheckBox, setChecked, void(entity, float));
-       ATTRIB(XonoticCheckBox, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticCheckBox, image, string, SKINGFX_CHECKBOX)
-       ATTRIB(XonoticCheckBox, yesValue, float, 1)
-       ATTRIB(XonoticCheckBox, noValue, float, 0)
+       ATTRIB(XonoticCheckBox, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticCheckBox, image, string, SKINGFX_CHECKBOX);
+       ATTRIB(XonoticCheckBox, yesValue, float, 1);
+       ATTRIB(XonoticCheckBox, noValue, float, 0);
 
-       ATTRIB(XonoticCheckBox, color, vector, SKINCOLOR_CHECKBOX_N)
-       ATTRIB(XonoticCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C)
-       ATTRIB(XonoticCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F)
-       ATTRIB(XonoticCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D)
+       ATTRIB(XonoticCheckBox, color, vector, SKINCOLOR_CHECKBOX_N);
+       ATTRIB(XonoticCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C);
+       ATTRIB(XonoticCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F);
+       ATTRIB(XonoticCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D);
 
-       ATTRIB(XonoticCheckBox, cvarName, string, string_null)
+       ATTRIB(XonoticCheckBox, cvarName, string);
        METHOD(XonoticCheckBox, loadCvars, void(entity));
        METHOD(XonoticCheckBox, saveCvars, void(entity));
-       ATTRIB(XonoticCheckBox, sendCvars, float, 0)
+       ATTRIB(XonoticCheckBox, sendCvars, float, 0);
 
-       ATTRIB(XonoticCheckBox, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticCheckBox, disabledAlpha, float, SKINALPHA_DISABLED)
-       ATTRIB(XonoticCheckBox, linkedCheckBox, entity, NULL)
+       ATTRIB(XonoticCheckBox, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticCheckBox, disabledAlpha, float, SKINALPHA_DISABLED);
+       ATTRIB(XonoticCheckBox, linkedCheckBox, entity);
 ENDCLASS(XonoticCheckBox)
 entity makeXonoticCheckBox_T(float, string, string, string);
 entity makeXonoticCheckBox(float, string, string);
index d9c144c6ab763c35616db64d97b54d8e8f8b5f7f..259c2104fea2ac2abd33a232ace783c0f02ea2f4 100644 (file)
@@ -5,20 +5,20 @@ CLASS(XonoticSliderCheckBox, CheckBox)
        METHOD(XonoticSliderCheckBox, configureXonoticSliderCheckBox, void(entity, float, float, entity, string));
        METHOD(XonoticSliderCheckBox, setChecked, void(entity, float));
        METHOD(XonoticSliderCheckBox, draw, void(entity));
-       ATTRIB(XonoticSliderCheckBox, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticSliderCheckBox, image, string, SKINGFX_CHECKBOX)
+       ATTRIB(XonoticSliderCheckBox, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticSliderCheckBox, image, string, SKINGFX_CHECKBOX);
 
-       ATTRIB(XonoticSliderCheckBox, color, vector, SKINCOLOR_CHECKBOX_N)
-       ATTRIB(XonoticSliderCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C)
-       ATTRIB(XonoticSliderCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F)
-       ATTRIB(XonoticSliderCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D)
+       ATTRIB(XonoticSliderCheckBox, color, vector, SKINCOLOR_CHECKBOX_N);
+       ATTRIB(XonoticSliderCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C);
+       ATTRIB(XonoticSliderCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F);
+       ATTRIB(XonoticSliderCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D);
 
-       ATTRIB(XonoticSliderCheckBox, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticSliderCheckBox, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticSliderCheckBox, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticSliderCheckBox, disabledAlpha, float, SKINALPHA_DISABLED);
 
-       ATTRIB(XonoticSliderCheckBox, controlledSlider, entity, NULL)
-       ATTRIB(XonoticSliderCheckBox, offValue, float, -1)
-       ATTRIB(XonoticSliderCheckBox, inverted, float, 0)
-       ATTRIB(XonoticSliderCheckBox, savedValue, float, -1)
+       ATTRIB(XonoticSliderCheckBox, controlledSlider, entity);
+       ATTRIB(XonoticSliderCheckBox, offValue, float, -1);
+       ATTRIB(XonoticSliderCheckBox, inverted, float, 0);
+       ATTRIB(XonoticSliderCheckBox, savedValue, float, -1);
 ENDCLASS(XonoticSliderCheckBox)
 entity makeXonoticSliderCheckBox(float, float, entity, string);
index b4dc8e5d20e6aefe2db6657dd1982e4ace9d64fe..e73a7144fc4a0cdd0ff643e600832b5ac2bd775c 100644 (file)
@@ -4,22 +4,22 @@
 CLASS(XonoticCheckBoxString, CheckBox)
        METHOD(XonoticCheckBoxString, configureXonoticCheckBoxString, void(entity, string, string, string, string));
        METHOD(XonoticCheckBoxString, setChecked, void(entity, float));
-       ATTRIB(XonoticCheckBoxString, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticCheckBoxString, image, string, SKINGFX_CHECKBOX)
-       ATTRIB(XonoticCheckBoxString, yesString, string, string_null)
-       ATTRIB(XonoticCheckBoxString, noString, string, string_null)
+       ATTRIB(XonoticCheckBoxString, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticCheckBoxString, image, string, SKINGFX_CHECKBOX);
+       ATTRIB(XonoticCheckBoxString, yesString, string);
+       ATTRIB(XonoticCheckBoxString, noString, string);
 
-       ATTRIB(XonoticCheckBoxString, color, vector, SKINCOLOR_CHECKBOX_N)
-       ATTRIB(XonoticCheckBoxString, colorC, vector, SKINCOLOR_CHECKBOX_C)
-       ATTRIB(XonoticCheckBoxString, colorF, vector, SKINCOLOR_CHECKBOX_F)
-       ATTRIB(XonoticCheckBoxString, colorD, vector, SKINCOLOR_CHECKBOX_D)
+       ATTRIB(XonoticCheckBoxString, color, vector, SKINCOLOR_CHECKBOX_N);
+       ATTRIB(XonoticCheckBoxString, colorC, vector, SKINCOLOR_CHECKBOX_C);
+       ATTRIB(XonoticCheckBoxString, colorF, vector, SKINCOLOR_CHECKBOX_F);
+       ATTRIB(XonoticCheckBoxString, colorD, vector, SKINCOLOR_CHECKBOX_D);
 
-       ATTRIB(XonoticCheckBoxString, cvarName, string, string_null)
+       ATTRIB(XonoticCheckBoxString, cvarName, string);
        METHOD(XonoticCheckBoxString, loadCvars, void(entity));
        METHOD(XonoticCheckBoxString, saveCvars, void(entity));
-       ATTRIB(XonoticCheckBoxString, sendCvars, float, 0)
+       ATTRIB(XonoticCheckBoxString, sendCvars, float, 0);
 
-       ATTRIB(XonoticCheckBoxString, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticCheckBoxString, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticCheckBoxString, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticCheckBoxString, disabledAlpha, float, SKINALPHA_DISABLED);
 ENDCLASS(XonoticCheckBoxString)
 entity makeXonoticCheckBoxString(string, string, string, string);
index c253f8f11293329bcf1faeb0573f897061a251c9..9ff9639c588b0d0100ff36617ed45365c02a5885 100644 (file)
@@ -5,14 +5,14 @@ CLASS(XonoticColorButton, RadioButton)
        METHOD(XonoticColorButton, configureXonoticColorButton, void(entity, float, float, float));
        METHOD(XonoticColorButton, setChecked, void(entity, float));
        METHOD(XonoticColorButton, draw, void(entity));
-       ATTRIB(XonoticColorButton, fontSize, float, 0)
-       ATTRIB(XonoticColorButton, image, string, SKINGFX_COLORBUTTON)
+       ATTRIB(XonoticColorButton, fontSize, float, 0);
+       ATTRIB(XonoticColorButton, image, string, SKINGFX_COLORBUTTON);
 
-       ATTRIB(XonoticColorButton, useDownAsChecked, float, 1)
+       ATTRIB(XonoticColorButton, useDownAsChecked, float, 1);
 
-       ATTRIB(XonoticColorButton, cvarPart, float, 0)
-       ATTRIB(XonoticColorButton, cvarName, string, string_null)
-       ATTRIB(XonoticColorButton, cvarValueFloat, float, 0)
+       ATTRIB(XonoticColorButton, cvarPart, float, 0);
+       ATTRIB(XonoticColorButton, cvarName, string);
+       ATTRIB(XonoticColorButton, cvarValueFloat, float, 0);
        METHOD(XonoticColorButton, loadCvars, void(entity));
        METHOD(XonoticColorButton, saveCvars, void(entity));
 ENDCLASS(XonoticColorButton)
index b88d5d9a7d8f3973e5d828c0da66cac207db309a..357276e16adf3794334eb7d640089b78350504ca 100644 (file)
@@ -70,7 +70,7 @@ float XonoticColorpicker_mouseDrag(entity me, vector coords)
                                while (i - 2 - carets >= 0 && substring(me.controlledTextbox.text, i - 2 - carets, 1) == "^")
                                        ++carets;
                                if (carets & 1)
-                                       if(strstrofs("0123456789", substring(me.controlledTextbox.text, i-1, 1), 0) >= 0)
+                                       if(IS_DIGIT(substring(me.controlledTextbox.text, i-1, 1)))
                                        {
                                                me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
                                                me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
@@ -87,9 +87,9 @@ float XonoticColorpicker_mouseDrag(entity me, vector coords)
                                while (i - 5 - carets >= 0 && substring(me.controlledTextbox.text, i - 5 - carets, 1) == "^")
                                        ++carets;
                                if (carets & 1)
-                                       if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-3, 1), 0) >= 0)
-                                               if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-2, 1), 0) >= 0)
-                                                       if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-1, 1), 0) >= 0)
+                                       if(IS_HEXDIGIT(substring(me.controlledTextbox.text, i - 3, 1)))
+                                               if(IS_HEXDIGIT(substring(me.controlledTextbox.text, i - 2, 1)))
+                                                       if(IS_HEXDIGIT(substring(me.controlledTextbox.text, i - 1, 1)))
                                                        {
                                                                me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
                                                                me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
index 03567dfa45d929d5f2f60c55fbcb24d444fda602..b1c9d1e80ae566dd5a5dcddc8fb569f46bb3f3b3 100644 (file)
@@ -6,10 +6,10 @@ CLASS(XonoticColorpicker, Image)
        METHOD(XonoticColorpicker, mousePress, float(entity, vector));
        METHOD(XonoticColorpicker, mouseRelease, float(entity, vector));
        METHOD(XonoticColorpicker, mouseDrag, float(entity, vector));
-       ATTRIB(XonoticColorpicker, controlledTextbox, entity, NULL)
-       ATTRIB(XonoticColorpicker, image, string, SKINGFX_COLORPICKER)
-       ATTRIB(XonoticColorpicker, imagemargin, vector, SKINMARGIN_COLORPICKER)
-       ATTRIB(XonoticColorpicker, focusable, float, 1)
+       ATTRIB(XonoticColorpicker, controlledTextbox, entity);
+       ATTRIB(XonoticColorpicker, image, string, SKINGFX_COLORPICKER);
+       ATTRIB(XonoticColorpicker, imagemargin, vector, SKINMARGIN_COLORPICKER);
+       ATTRIB(XonoticColorpicker, focusable, float, 1);
        METHOD(XonoticColorpicker, focusLeave, void(entity));
        METHOD(XonoticColorpicker, keyDown, float(entity, float, float, float));
        METHOD(XonoticColorpicker, draw, void(entity));
index 3caf9d9bf0d5a9d891a828527cefa6bd3aa6c570..e990234b3f0a24779b9e3b40da6850b31299cd82 100644 (file)
@@ -9,15 +9,15 @@ CLASS(XonoticColorpickerString, Image)
        METHOD(XonoticColorpickerString, mouseRelease, float(entity, vector));
        METHOD(XonoticColorpickerString, mouseDrag, float(entity, vector));
 
-       ATTRIB(XonoticColorpickerString, cvarName, string, string_null)
+       ATTRIB(XonoticColorpickerString, cvarName, string);
        METHOD(XonoticColorpickerString, loadCvars, void(entity));
        METHOD(XonoticColorpickerString, saveCvars, void(entity));
 
-       ATTRIB(XonoticColorpickerString, prevcoords, vector, '0 0 0')
-       ATTRIB(XonoticColorpickerString, image, string, SKINGFX_COLORPICKER)
-       ATTRIB(XonoticColorpickerString, imagemargin, vector, SKINMARGIN_COLORPICKER)
-       ATTRIB(XonoticColorpickerString, focusable, float, 1)
+       ATTRIB(XonoticColorpickerString, prevcoords, vector, '0 0 0');
+       ATTRIB(XonoticColorpickerString, image, string, SKINGFX_COLORPICKER);
+       ATTRIB(XonoticColorpickerString, imagemargin, vector, SKINMARGIN_COLORPICKER);
+       ATTRIB(XonoticColorpickerString, focusable, float, 1);
        METHOD(XonoticColorpickerString, draw, void(entity));
-       ATTRIB(XonoticColorpickerString, disabledAlpha, float, 0.3)
+       ATTRIB(XonoticColorpickerString, disabledAlpha, float, 0.3);
 ENDCLASS(XonoticColorpickerString)
 entity makeXonoticColorpickerString(string theCvar, string theDefaultCvar);
index 072890ab1e23b60738f631fed68e61c8a8556169..5de59ee07b9e6d3d6d80920422e807b260255dcd 100644 (file)
@@ -3,8 +3,8 @@
 #include "button.qh"
 CLASS(XonoticCommandButton, XonoticButton)
        METHOD(XonoticCommandButton, configureXonoticCommandButton, void(entity, string, vector, string, float, string));
-       ATTRIB(XonoticCommandButton, onClickCommand, string, string_null)
-       ATTRIB(XonoticCommandButton, flags, float, 0)
+       ATTRIB(XonoticCommandButton, onClickCommand, string);
+       ATTRIB(XonoticCommandButton, flags, float, 0);
 ENDCLASS(XonoticCommandButton)
 
 entity makeXonoticCommandButton_T(string theText, vector theColor, string theCommand, float closesMenu, string theTooltip);
index d30ab3d447bb57d9343e21551aabc9a68b755d63..a28b0863861891b8c6e9361916b628f4926c65ba 100644 (file)
                PERSON(Zac "Mario" Jardine) \
        NL() \
        TITLE(_("Extended Team")) \
+               PERSON(AllieWay) \
                PERSON(Antonio "terencehill" Piu) \
                PERSON(Archer) \
                PERSON(BuddyFriendGuy) \
                PERSON(Debugger) \
+               PERSON(Diomedes) \
+               PERSON(Freddy) \
                PERSON(GATTS) \
                PERSON(Halogene) \
                PERSON(IDWMaster) \
                PERSON(Jan "zykure" Behrens) \
                PERSON(JH0nny) \
-               PERSON(Luigi) \
+               PERSON(Jubilant) \
                PERSON(Łukasz "kuniu the frogg" Polek) \
+               PERSON(martin-t) \
                PERSON(Matthias "matthiaskrgr" Krüger) \
                PERSON(Mattia "Melanosuchus" Basaglia) \
                PERSON(MrBougo) \
        FUNCTION(_("Level Design")) \
                PERSON(Amadeusz "amade/proraide" Sławiński) \
                PERSON(Ben "MooKow" Banker) \
-               PERSON(Calinou) \
                PERSON(Cortez) \
                PERSON(Cuinn "Cuinnton" Herrick) \
                PERSON(Debugger) \
+               PERSON(Hugo "Calinou" Locurcio) \
                PERSON(Jakob "tZork" Markström Gröhn) \
                PERSON(Konrad "Justin" Slawinski) \
                PERSON(Maddin) \
                        PERSON(Mihail "meequz" Varantsou) \
                NL() \
                FUNCTION(_("Bulgarian")) \
+                       PERSON(Alexander "alex4o" Bonin) \
+                       PERSON(ifohancroft) \
                        PERSON(lokster) \
                        PERSON(set_killer) \
+                       PERSON(ubone) \
                NL() \
                FUNCTION(_("Chinese (China)")) \
-                       PERSON(Antonidas) \
                        PERSON(kalawore) \
                        PERSON(sapphireliu) \
                NL() \
+               FUNCTION(_("Chinese (Taiwan)")) \
+                       PERSON(Alisha) \
+                       PERSON(Armcoon) \
+                       PERSON(Jeff "s8321414" Huang) \
+               NL() \
+               FUNCTION(_("Cornish")) \
+                       PERSON(Nicky "nrowe" Rowe) \
+               NL() \
                FUNCTION(_("Czech")) \
                        PERSON(shogun assassin/woky) \
                        PERSON(Superovoce) \
                NL() \
                FUNCTION(_("Dutch")) \
                        PERSON(Alexander "freefang" van Dam) \
+                       PERSON(Jonathan "Jonakeys" van der Steege) \
                        PERSON(PinkRobot) \
                        PERSON(vegiburger) \
                NL() \
                FUNCTION(_("English (Australia)")) \
                        PERSON(Laurene "sunflowers" Albrand) \
+                       PERSON(Stuart "Cefiar" Young) \
                        PERSON(Zac "Mario" Jardine) \
                NL() \
                FUNCTION(_("Finnish")) \
+                       PERSON(Jonas "PowaTree" Sahlberg) \
                        PERSON(Henry "Exitium" Sanmark) \
                        PERSON(Rasmus "FruitieX" Eskola) \
                NL() \
                FUNCTION(_("French")) \
-                       PERSON(Calinou) \
+                       PERSON(Hugo "Calinou" Locurcio) \
+                       PERSON(Kim "coughingmouse" Lee) \
                        PERSON(Maxime "Taximus" Paradis) \
                        PERSON(RedGuff) \
+                       PERSON(Thomas "illwieckz" Debesse) \
                        PERSON(Yannick "SpiKe" Le Guen) \
                NL() \
                FUNCTION(_("German")) \
                        PERSON(Erik "Ablu" Schilling) \
                        PERSON(Jope "Sless" Withers) \
                        PERSON(Marvin "Mirio" Beck) \
+                       PERSON(Paul "Snapper") \
                        PERSON(Rudolf "divVerent" Polzer) \
+                       PERSON(Wuzzy) \
                        PERSON(Yepoleb) \
                NL() \
                FUNCTION(_("Greek")) \
                        PERSON(stdi) \
                        PERSON(XCostaX) \
                NL() \
+               FUNCTION(_("Kazakh")) \
+                       PERSON("Артем \"bystrov.arterm\" Быстров") \
+               NL() \
+               FUNCTION(_("Korean")) \
+                       PERSON(Jisoo "s6e9x" Lim) \
+                       PERSON(Kim "coughingmouse" Lee) \
+               NL() \
                FUNCTION(_("Polish")) \
                        PERSON(4m) \
                        PERSON(Alex "tiprogrammierer.alex" Progger) \
                        PERSON(Amadeusz "amade/proraide" Sławiński) \
+                       PERSON(Artur "artur9010" Motyka) \
+                       PERSON(Jakub "KubeQ11" Pędziszewski) \
                NL() \
                FUNCTION(_("Portuguese")) \
+                       PERSON(Ivan Paulos "greylica" Tomé) \
+                       PERSON(Jean Trindade "Muleke_Trairao" Pereira) \
                        PERSON(Ricardo Manuel "Hellgardia" da Cruz Coelho da Silva) \
                        PERSON(xXxCHAOTICxXx) \
                NL() \
                        PERSON(Adrian-Ciprian "adrian.tinjala" Tînjală) \
                        PERSON(BusterDBK) \
                        PERSON(Mircea "Taoki" Kitsune) \
+                       PERSON(Sorin "unic_sorin" Botirla) \
                        PERSON(Tudor "TropiKo" Ionel) \
                NL() \
                FUNCTION(_("Russian")) \
                        PERSON(Alex "alextalker7" Talker) \
                        PERSON(Alexandr "zrg") \
                        PERSON(Andrei "adem4ik" Stepanov) \
+                       PERSON(Andrey "dekrY" P.) \
                        PERSON(gravicappa) \
                        PERSON(Hot Dog) \
                        PERSON(Lord Canistra) \
                        PERSON(marcus256) \
                NL() \
                FUNCTION(_("Ukrainian")) \
+                       PERSON(Dmitro "Gamebot" Sokhin) \
                        PERSON(Oleh "BlaXpirit" Prypin) \
                        PERSON(Vasyl "Harmata" Melnyk) \
                        PERSON(Yuriy "herrniemand" Ackermann) \
@@ -402,7 +437,6 @@ void XonoticCreditsList_resizeNotify(entity me, vector relOrigin, vector relSize
 }
 void XonoticCreditsList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
 {
-       // layout: Ping, Credits name, Map name, NP, TP, MP
        string s;
        float theAlpha;
        vector theColor;
index d376beaa774adecd6e6fd7c91991548fd17d5ddc..8abc12c1998bc675d3c68f4fb2ca4dec751cd90b 100644 (file)
@@ -3,20 +3,20 @@
 #include "listbox.qh"
 CLASS(XonoticCreditsList, XonoticListBox)
        METHOD(XonoticCreditsList, configureXonoticCreditsList, void(entity));
-       ATTRIB(XonoticCreditsList, rowsPerItem, float, 1)
+       ATTRIB(XonoticCreditsList, rowsPerItem, float, 1);
        METHOD(XonoticCreditsList, draw, void(entity));
        METHOD(XonoticCreditsList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticCreditsList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticCreditsList, keyDown, float(entity, float, float, float));
        METHOD(XonoticCreditsList, destroy, void(entity));
-       ATTRIB(XonoticCreditsList, selectionDoesntMatter, bool, true)
+       ATTRIB(XonoticCreditsList, selectionDoesntMatter, bool, true);
 
-       ATTRIB(XonoticCreditsList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticCreditsList, realUpperMargin, float, 0)
-       ATTRIB(XonoticCreditsList, bufferIndex, float, 0)
-       ATTRIB(XonoticCreditsList, scrolling, float, 0)
+       ATTRIB(XonoticCreditsList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticCreditsList, realUpperMargin, float, 0);
+       ATTRIB(XonoticCreditsList, bufferIndex, float, 0);
+       ATTRIB(XonoticCreditsList, scrolling, float, 0);
 
-       ATTRIB(XonoticCreditsList, alphaBG, float, 0)
+       ATTRIB(XonoticCreditsList, alphaBG, float, 0);
 ENDCLASS(XonoticCreditsList)
 entity makeXonoticCreditsList();
 
index b6f39457d2ff30a6b2de249422015b3d88e22870..78cb5df0c50ce601a9299f52dca8a996638cd260 100644 (file)
@@ -4,8 +4,8 @@
 CLASS(XonoticCrosshairPicker, XonoticPicker)
        METHOD(XonoticCrosshairPicker, configureXonoticCrosshairPicker, void(entity));
 
-       ATTRIB(XonoticCrosshairPicker, rows, float, 3)
-       ATTRIB(XonoticCrosshairPicker, columns, float, 12)
+       ATTRIB(XonoticCrosshairPicker, rows, float, 3);
+       ATTRIB(XonoticCrosshairPicker, columns, float, 12);
 
        METHOD(XonoticCrosshairPicker, cellSelect, void(entity, vector));
        METHOD(XonoticCrosshairPicker, cellIsValid, bool(entity, vector));
index dcc7be5ac6bff16490b6f72b7fe0c7018b1c74b4..7bf363def7f4b42ebf413bcea99b7871a7418a39 100644 (file)
@@ -4,9 +4,9 @@
 CLASS(XonoticCrosshairPreview, Item)
        METHOD(XonoticCrosshairPreview, configureXonoticCrosshairPreview, void(entity));
        METHOD(XonoticCrosshairPreview, draw, void(entity));
-       ATTRIB(XonoticCrosshairPreview, src, string, string_null)
-       ATTRIB(XonoticCrosshairPreview, src2, string, string_null)
-       ATTRIB(XonoticCrosshairPreview, disabled, float, 0)
-       ATTRIB(XonoticCrosshairPreview, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticCrosshairPreview, src, string);
+       ATTRIB(XonoticCrosshairPreview, src2, string);
+       ATTRIB(XonoticCrosshairPreview, disabled, float, 0);
+       ATTRIB(XonoticCrosshairPreview, disabledAlpha, float, SKINALPHA_DISABLED);
 ENDCLASS(XonoticCrosshairPreview)
 entity makeXonoticCrosshairPreview();
index 2c99866cf7671d7bf35c9cdf438c7f2dbcdeeea7..1260ae447a42d0162c4b9969e06217886fe7675a 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticCvarList, XonoticListBox)
        METHOD(XonoticCvarList, configureXonoticCvarList, void(entity));
-       ATTRIB(XonoticCvarList, rowsPerItem, float, 1)
+       ATTRIB(XonoticCvarList, rowsPerItem, float, 1);
        METHOD(XonoticCvarList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticCvarList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticCvarList, keyDown, float(entity, float, float, float));
@@ -12,30 +12,30 @@ CLASS(XonoticCvarList, XonoticListBox)
 
        METHOD(XonoticCvarList, destroy, void(entity));
 
-       ATTRIB(XonoticCvarList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticCvarList, realUpperMargin, float, 0)
-       ATTRIB(XonoticCvarList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticCvarList, columnNameSize, float, 0)
-       ATTRIB(XonoticCvarList, columnValueOrigin, float, 0)
-       ATTRIB(XonoticCvarList, columnValueSize, float, 0)
+       ATTRIB(XonoticCvarList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticCvarList, realUpperMargin, float, 0);
+       ATTRIB(XonoticCvarList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticCvarList, columnNameSize, float, 0);
+       ATTRIB(XonoticCvarList, columnValueOrigin, float, 0);
+       ATTRIB(XonoticCvarList, columnValueSize, float, 0);
 
        METHOD(XonoticCvarList, mouseRelease, float(entity, vector));
        METHOD(XonoticCvarList, setSelected, void(entity, float));
        METHOD(XonoticCvarList, updateCvarType, float(entity));
 
-       ATTRIB(XonoticCvarList, controlledTextbox, entity, NULL)
-       ATTRIB(XonoticCvarList, cvarNameBox, entity, NULL)
-       ATTRIB(XonoticCvarList, cvarDescriptionBox, entity, NULL)
-       ATTRIB(XonoticCvarList, cvarTypeBox, entity, NULL)
-       ATTRIB(XonoticCvarList, cvarValueBox, entity, NULL)
-       ATTRIB(XonoticCvarList, cvarDefaultBox, entity, NULL)
-       ATTRIB(XonoticCvarList, cvarNeedsForcing, float, 0)
+       ATTRIB(XonoticCvarList, controlledTextbox, entity);
+       ATTRIB(XonoticCvarList, cvarNameBox, entity);
+       ATTRIB(XonoticCvarList, cvarDescriptionBox, entity);
+       ATTRIB(XonoticCvarList, cvarTypeBox, entity);
+       ATTRIB(XonoticCvarList, cvarValueBox, entity);
+       ATTRIB(XonoticCvarList, cvarDefaultBox, entity);
+       ATTRIB(XonoticCvarList, cvarNeedsForcing, float, 0);
 
-       ATTRIB(XonoticCvarList, handle, float, -1)
-       ATTRIB(XonoticCvarList, cvarName, string, string_null)
-       ATTRIB(XonoticCvarList, cvarDescription, string, string_null)
-       ATTRIB(XonoticCvarList, cvarType, string, string_null)
-       ATTRIB(XonoticCvarList, cvarDefault, string, string_null)
+       ATTRIB(XonoticCvarList, handle, float, -1);
+       ATTRIB(XonoticCvarList, cvarName, string);
+       ATTRIB(XonoticCvarList, cvarDescription, string);
+       ATTRIB(XonoticCvarList, cvarType, string);
+       ATTRIB(XonoticCvarList, cvarDefault, string);
 ENDCLASS(XonoticCvarList)
 entity makeXonoticCvarList();
 void CvarList_Filter_Change(entity box, entity me);
index 1ca3d5811fd316192c7ca4b86bbf5b10ada6ca5c..87c614ab737b31a34d5a2c447693b69679e6a3a7 100644 (file)
@@ -19,15 +19,15 @@ ENDCLASS(DataSource)
 
 
 CLASS(StringSource, DataSource)
-    ATTRIB(StringSource, StringSource_str, string, string_null)
-    ATTRIB(StringSource, StringSource_sep, string, string_null)
+    ATTRIB(StringSource, StringSource_str, string);
+    ATTRIB(StringSource, StringSource_sep, string);
     CONSTRUCTOR(StringSource, string str, string sep);
     METHOD(StringSource, getEntry, entity(entity this, int i, void(string name, string icon) returns));
     METHOD(StringSource, reload, int(entity this, string filter));
 ENDCLASS(StringSource)
 
 CLASS(CvarStringSource, StringSource)
-    ATTRIB(CvarStringSource, CvarStringSource_cvar, string, string_null)
+    ATTRIB(CvarStringSource, CvarStringSource_cvar, string);
     CONSTRUCTOR(CvarStringSource, string cv, string sep);
     METHOD(CvarStringSource, getEntry, entity(entity this, int i, void(string name, string icon) returns));
     METHOD(CvarStringSource, reload, int(entity this, string filter));
index 547bf6695dff6ea62df429f7432679e3a204fe77..6341db1a3e1da23f081c28044d4b057e43d0243d 100644 (file)
@@ -4,7 +4,7 @@
 #include "listbox.qh"
 CLASS(XonoticDemoList, XonoticListBox)
        METHOD(XonoticDemoList, configureXonoticDemoList, void(entity));
-       ATTRIB(XonoticDemoList, rowsPerItem, float, 1)
+       ATTRIB(XonoticDemoList, rowsPerItem, float, 1);
        METHOD(XonoticDemoList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticDemoList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticDemoList, getDemos, void(entity));
@@ -16,15 +16,15 @@ CLASS(XonoticDemoList, XonoticListBox)
        METHOD(XonoticDemoList, destroy, void(entity));
        METHOD(XonoticDemoList, showNotify, void(entity));
 
-       ATTRIB(XonoticDemoList, listDemo, float, -1)
-       ATTRIB(XonoticDemoList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticDemoList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticDemoList, columnNameSize, float, 0)
-       ATTRIB(XonoticDemoList, realUpperMargin, float, 0)
-       ATTRIB(XonoticDemoList, origin, vector, '0 0 0')
-       ATTRIB(XonoticDemoList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticDemoList, listDemo, float, -1);
+       ATTRIB(XonoticDemoList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticDemoList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticDemoList, columnNameSize, float, 0);
+       ATTRIB(XonoticDemoList, realUpperMargin, float, 0);
+       ATTRIB(XonoticDemoList, origin, vector, '0 0 0');
+       ATTRIB(XonoticDemoList, itemAbsSize, vector, '0 0 0');
 
-       ATTRIB(XonoticDemoList, filterString, string, string_null)
+       ATTRIB(XonoticDemoList, filterString, string);
 ENDCLASS(XonoticDemoList)
 
 entity demolist;  // for reference elsewhere
index a3612698ff80e93a8f1b0236b9c952abb93f9da8..847233d6ac908615abcf348d4e49dfe1ba2b7721 100644 (file)
@@ -4,30 +4,30 @@
 CLASS(XonoticDialog, Dialog)
        // still to be customized by user
        /*
-       ATTRIB(XonoticDialog, closable, float, 1)
-       ATTRIB(XonoticDialog, title, string, _("Form1")) // ;)
-       ATTRIB(XonoticDialog, color, vector, '1 0.5 1')
-       ATTRIB(XonoticDialog, intendedWidth, float, 0)
-       ATTRIB(XonoticDialog, rows, float, 3)
-       ATTRIB(XonoticDialog, columns, float, 2)
+       ATTRIB(XonoticDialog, closable, float, 1);
+       ATTRIB(XonoticDialog, title, string, _("Form1")); // ;);
+       ATTRIB(XonoticDialog, color, vector, '1 0.5 1');
+       ATTRIB(XonoticDialog, intendedWidth, float, 0);
+       ATTRIB(XonoticDialog, rows, float, 3);
+       ATTRIB(XonoticDialog, columns, float, 2);
        */
-       ATTRIB(XonoticDialog, marginTop, float, SKINMARGIN_TOP)                          // pixels
-       ATTRIB(XonoticDialog, marginBottom, float, SKINMARGIN_BOTTOM)                    // pixels
-       ATTRIB(XonoticDialog, marginLeft, float, SKINMARGIN_LEFT)                        // pixels
-       ATTRIB(XonoticDialog, marginRight, float, SKINMARGIN_RIGHT)                      // pixels
-       ATTRIB(XonoticDialog, columnSpacing, float, SKINMARGIN_COLUMNS)                  // pixels
-       ATTRIB(XonoticDialog, rowSpacing, float, SKINMARGIN_ROWS)                        // pixels
-       ATTRIB(XonoticDialog, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL) // pixels
-       ATTRIB(XonoticDialog, titleHeight, float, SKINFONTSIZE_TITLE * SKINHEIGHT_TITLE) // pixels
-       ATTRIB(XonoticDialog, titleFontSize, float, SKINFONTSIZE_TITLE)                  // pixels
+       ATTRIB(XonoticDialog, marginTop, float, SKINMARGIN_TOP);                          // pixels
+       ATTRIB(XonoticDialog, marginBottom, float, SKINMARGIN_BOTTOM);                    // pixels
+       ATTRIB(XonoticDialog, marginLeft, float, SKINMARGIN_LEFT);                        // pixels
+       ATTRIB(XonoticDialog, marginRight, float, SKINMARGIN_RIGHT);                      // pixels
+       ATTRIB(XonoticDialog, columnSpacing, float, SKINMARGIN_COLUMNS);                  // pixels
+       ATTRIB(XonoticDialog, rowSpacing, float, SKINMARGIN_ROWS);                        // pixels
+       ATTRIB(XonoticDialog, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL); // pixels
+       ATTRIB(XonoticDialog, titleHeight, float, SKINFONTSIZE_TITLE * SKINHEIGHT_TITLE); // pixels
+       ATTRIB(XonoticDialog, titleFontSize, float, SKINFONTSIZE_TITLE);                  // pixels
 
-       ATTRIB(XonoticDialog, backgroundImage, string, SKINGFX_DIALOGBORDER)
-       ATTRIB(XonoticDialog, borderLines, float, SKINHEIGHT_DIALOGBORDER)
-       ATTRIB(XonoticDialog, closeButtonImage, string, SKINGFX_CLOSEBUTTON)
-       ATTRIB(XonoticDialog, zoomedOutTitleBarPosition, float, SKINHEIGHT_ZOOMEDTITLE * 0.5 - 0.5)
-       ATTRIB(XonoticDialog, zoomedOutTitleBar, float, SKINHEIGHT_ZOOMEDTITLE != 0)
+       ATTRIB(XonoticDialog, backgroundImage, string, SKINGFX_DIALOGBORDER);
+       ATTRIB(XonoticDialog, borderLines, float, SKINHEIGHT_DIALOGBORDER);
+       ATTRIB(XonoticDialog, closeButtonImage, string, SKINGFX_CLOSEBUTTON);
+       ATTRIB(XonoticDialog, zoomedOutTitleBarPosition, float, SKINHEIGHT_ZOOMEDTITLE * 0.5 - 0.5);
+       ATTRIB(XonoticDialog, zoomedOutTitleBar, float, SKINHEIGHT_ZOOMEDTITLE != 0);
 
-       ATTRIB(XonoticDialog, alpha, float, SKINALPHA_TEXT)
+       ATTRIB(XonoticDialog, alpha, float, SKINALPHA_TEXT);
 
        METHOD(XonoticDialog, configureDialog, void(entity));
 ENDCLASS(XonoticDialog)
index 90d722ed35aa3fe624274e83f4eee5f1fc0a7fca..3be2e2b3f5873b5e3f7160c32150caa4383c6b4d 100644 (file)
@@ -4,11 +4,11 @@
 CLASS(XonoticCreditsDialog, XonoticDialog)
        METHOD(XonoticCreditsDialog, fill, void(entity));
        METHOD(XonoticCreditsDialog, focusEnter, void(entity));
-       ATTRIB(XonoticCreditsDialog, title, string, _("Credits"))
-       ATTRIB(XonoticCreditsDialog, tooltip, string, _("The Xonotic credits"))
-       ATTRIB(XonoticCreditsDialog, color, vector, SKINCOLOR_DIALOG_CREDITS)
-       ATTRIB(XonoticCreditsDialog, intendedWidth, float, SKINWIDTH_CREDITS)
-       ATTRIB(XonoticCreditsDialog, rows, float, SKINROWS_CREDITS)
-       ATTRIB(XonoticCreditsDialog, columns, float, 2)
-       ATTRIB(XonoticCreditsDialog, creditsList, entity, NULL)
+       ATTRIB(XonoticCreditsDialog, title, string, _("Credits"));
+       ATTRIB(XonoticCreditsDialog, tooltip, string, _("The Xonotic credits"));
+       ATTRIB(XonoticCreditsDialog, color, vector, SKINCOLOR_DIALOG_CREDITS);
+       ATTRIB(XonoticCreditsDialog, intendedWidth, float, SKINWIDTH_CREDITS);
+       ATTRIB(XonoticCreditsDialog, rows, float, SKINROWS_CREDITS);
+       ATTRIB(XonoticCreditsDialog, columns, float, 2);
+       ATTRIB(XonoticCreditsDialog, creditsList, entity);
 ENDCLASS(XonoticCreditsDialog)
index ff7099b6b98b398a90da70dc3cba3273eaea275e..51a56bb6aa66a7e3d9b1411d5351015a1904c11f 100644 (file)
@@ -3,14 +3,14 @@
 #include "rootdialog.qh"
 CLASS(XonoticFirstRunDialog, XonoticRootDialog)
        METHOD(XonoticFirstRunDialog, fill, void(entity));
-       ATTRIB(XonoticFirstRunDialog, title, string, _("Welcome"))
-       ATTRIB(XonoticFirstRunDialog, color, vector, SKINCOLOR_DIALOG_FIRSTRUN)
-       ATTRIB(XonoticFirstRunDialog, intendedWidth, float, 0.7)
-       ATTRIB(XonoticFirstRunDialog, rows, float, 16)
-       ATTRIB(XonoticFirstRunDialog, columns, float, 6)
-       ATTRIB(XonoticFirstRunDialog, name, string, "FirstRun")
-       ATTRIB(XonoticFirstRunDialog, playerNameLabel, entity, NULL)
-       ATTRIB(XonoticFirstRunDialog, playerNameLabelAlpha, float, 0)
+       ATTRIB(XonoticFirstRunDialog, title, string, _("Welcome"));
+       ATTRIB(XonoticFirstRunDialog, color, vector, SKINCOLOR_DIALOG_FIRSTRUN);
+       ATTRIB(XonoticFirstRunDialog, intendedWidth, float, 0.7);
+       ATTRIB(XonoticFirstRunDialog, rows, float, 16);
+       ATTRIB(XonoticFirstRunDialog, columns, float, 6);
+       ATTRIB(XonoticFirstRunDialog, name, string, "FirstRun");
+       ATTRIB(XonoticFirstRunDialog, playerNameLabel, entity);
+       ATTRIB(XonoticFirstRunDialog, playerNameLabelAlpha, float, 0);
 
-       ATTRIB(XonoticFirstRunDialog, closable, float, 0)
+       ATTRIB(XonoticFirstRunDialog, closable, float, 0);
 ENDCLASS(XonoticFirstRunDialog)
index 237bcbbd152c923f700ce01952b95a68ece3e97c..9113a02794b1b08f810b009dd491c06fd8be7ab6 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDAmmoDialog, XonoticRootDialog)
        METHOD(XonoticHUDAmmoDialog, fill, void(entity));
-       ATTRIB(XonoticHUDAmmoDialog, title, string, _("Ammo Panel"))
-       ATTRIB(XonoticHUDAmmoDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDAmmoDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDAmmoDialog, rows, float, 15)
-       ATTRIB(XonoticHUDAmmoDialog, columns, float, 4)
-       ATTRIB(XonoticHUDAmmoDialog, name, string, "HUDammo")
-       ATTRIB(XonoticHUDAmmoDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDAmmoDialog, title, string, _("Ammo Panel"));
+       ATTRIB(XonoticHUDAmmoDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDAmmoDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDAmmoDialog, rows, float, 15);
+       ATTRIB(XonoticHUDAmmoDialog, columns, float, 4);
+       ATTRIB(XonoticHUDAmmoDialog, name, string, "HUDammo");
+       ATTRIB(XonoticHUDAmmoDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDAmmoDialog)
index 370dbe4eb84d098ad4a21dab582f4c5164d3b74e..9fc6846eb9a0d5d044a27ed4c99937190ab3137c 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDCenterprintDialog, XonoticRootDialog)
        METHOD(XonoticHUDCenterprintDialog, fill, void(entity));
-       ATTRIB(XonoticHUDCenterprintDialog, title, string, _("Centerprint Panel"))
-       ATTRIB(XonoticHUDCenterprintDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDCenterprintDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDCenterprintDialog, rows, float, 15)
-       ATTRIB(XonoticHUDCenterprintDialog, columns, float, 4)
-       ATTRIB(XonoticHUDCenterprintDialog, name, string, "HUDcenterprint")
-       ATTRIB(XonoticHUDCenterprintDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDCenterprintDialog, title, string, _("Centerprint Panel"));
+       ATTRIB(XonoticHUDCenterprintDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDCenterprintDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDCenterprintDialog, rows, float, 15);
+       ATTRIB(XonoticHUDCenterprintDialog, columns, float, 4);
+       ATTRIB(XonoticHUDCenterprintDialog, name, string, "HUDcenterprint");
+       ATTRIB(XonoticHUDCenterprintDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDCenterprintDialog)
index b30ea9fb2cc435bc616e8bfe49e06632b1bc11a6..570c2d616cde334e7402acc4d061ccad7c299d5d 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDChatDialog, XonoticRootDialog)
        METHOD(XonoticHUDChatDialog, fill, void(entity));
-       ATTRIB(XonoticHUDChatDialog, title, string, _("Chat Panel"))
-       ATTRIB(XonoticHUDChatDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDChatDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDChatDialog, rows, float, 15)
-       ATTRIB(XonoticHUDChatDialog, columns, float, 4)
-       ATTRIB(XonoticHUDChatDialog, name, string, "HUDchat")
-       ATTRIB(XonoticHUDChatDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDChatDialog, title, string, _("Chat Panel"));
+       ATTRIB(XonoticHUDChatDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDChatDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDChatDialog, rows, float, 15);
+       ATTRIB(XonoticHUDChatDialog, columns, float, 4);
+       ATTRIB(XonoticHUDChatDialog, name, string, "HUDchat");
+       ATTRIB(XonoticHUDChatDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDChatDialog)
index a28897d8c526f6e581ec7b7d0a80a8d8c7b757ef..b741465024d78a9917639aedabd61c209ae026ca 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDEngineInfoDialog, XonoticRootDialog)
        METHOD(XonoticHUDEngineInfoDialog, fill, void(entity));
-       ATTRIB(XonoticHUDEngineInfoDialog, title, string, _("Engine Info Panel"))
-       ATTRIB(XonoticHUDEngineInfoDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDEngineInfoDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDEngineInfoDialog, rows, float, 15)
-       ATTRIB(XonoticHUDEngineInfoDialog, columns, float, 4)
-       ATTRIB(XonoticHUDEngineInfoDialog, name, string, "HUDengineinfo")
-       ATTRIB(XonoticHUDEngineInfoDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDEngineInfoDialog, title, string, _("Engine Info Panel"));
+       ATTRIB(XonoticHUDEngineInfoDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDEngineInfoDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDEngineInfoDialog, rows, float, 15);
+       ATTRIB(XonoticHUDEngineInfoDialog, columns, float, 4);
+       ATTRIB(XonoticHUDEngineInfoDialog, name, string, "HUDengineinfo");
+       ATTRIB(XonoticHUDEngineInfoDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDEngineInfoDialog)
index 495047c5fca1788ff6cb4e21b338fd0c8b0e9878..b37f41b76ef5f1ecf91a8d2056d2fe8cab68a98e 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDHealthArmorDialog, XonoticRootDialog)
        METHOD(XonoticHUDHealthArmorDialog, fill, void(entity));
-       ATTRIB(XonoticHUDHealthArmorDialog, title, string, _("Health/Armor Panel"))
-       ATTRIB(XonoticHUDHealthArmorDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDHealthArmorDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDHealthArmorDialog, rows, float, 16)
-       ATTRIB(XonoticHUDHealthArmorDialog, columns, float, 4)
-       ATTRIB(XonoticHUDHealthArmorDialog, name, string, "HUDhealtharmor")
-       ATTRIB(XonoticHUDHealthArmorDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDHealthArmorDialog, title, string, _("Health/Armor Panel"));
+       ATTRIB(XonoticHUDHealthArmorDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDHealthArmorDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDHealthArmorDialog, rows, float, 16);
+       ATTRIB(XonoticHUDHealthArmorDialog, columns, float, 4);
+       ATTRIB(XonoticHUDHealthArmorDialog, name, string, "HUDhealtharmor");
+       ATTRIB(XonoticHUDHealthArmorDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDHealthArmorDialog)
index 093bc00dfdf522c4ce0f2d0f266e352f51cd08ab..5d9032ffb0fdf3e307190fd5a77353b425b5a099 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDInfoMessagesDialog, XonoticRootDialog)
        METHOD(XonoticHUDInfoMessagesDialog, fill, void(entity));
-       ATTRIB(XonoticHUDInfoMessagesDialog, title, string, _("Info Messages Panel"))
-       ATTRIB(XonoticHUDInfoMessagesDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDInfoMessagesDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDInfoMessagesDialog, rows, float, 15)
-       ATTRIB(XonoticHUDInfoMessagesDialog, columns, float, 4)
-       ATTRIB(XonoticHUDInfoMessagesDialog, name, string, "HUDinfomessages")
-       ATTRIB(XonoticHUDInfoMessagesDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDInfoMessagesDialog, title, string, _("Info Messages Panel"));
+       ATTRIB(XonoticHUDInfoMessagesDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDInfoMessagesDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDInfoMessagesDialog, rows, float, 15);
+       ATTRIB(XonoticHUDInfoMessagesDialog, columns, float, 4);
+       ATTRIB(XonoticHUDInfoMessagesDialog, name, string, "HUDinfomessages");
+       ATTRIB(XonoticHUDInfoMessagesDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDInfoMessagesDialog)
index f06038377561bb5fea908ac39e0e8e6c12370c00..aeb8c8c774d639fa11a2073135e343e9d8c78f33 100644 (file)
@@ -34,7 +34,7 @@ void XonoticHUDItemsTimeDialog_fill(entity me)
        me.TR(me);
                me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidespawned", _("Hide spawned items")));
        me.TR(me);
-               me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidelarge", _("Hide large armor and health")));
+               me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidebig", _("Hide big armor and health")));
        me.TR(me);
                me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_dynamicsize", _("Dynamic size")));
 }
index 71b53e6909aa43c5a504207adc57ecc274e4e608..507dedb7c95cc068adc2d478cf4a83795c72e620 100644 (file)
@@ -3,10 +3,10 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDItemsTimeDialog, XonoticRootDialog)
        METHOD(XonoticHUDItemsTimeDialog, fill, void(entity));
-       ATTRIB(XonoticHUDItemsTimeDialog, title, string, _("Items Time Panel"))
-       ATTRIB(XonoticHUDItemsTimeDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDItemsTimeDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDItemsTimeDialog, rows, float, 15)
-       ATTRIB(XonoticHUDItemsTimeDialog, columns, float, 4)
-       ATTRIB(XonoticHUDItemsTimeDialog, name, string, "HUDitemstime")
+       ATTRIB(XonoticHUDItemsTimeDialog, title, string, _("Items Time Panel"));
+       ATTRIB(XonoticHUDItemsTimeDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDItemsTimeDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDItemsTimeDialog, rows, float, 15);
+       ATTRIB(XonoticHUDItemsTimeDialog, columns, float, 4);
+       ATTRIB(XonoticHUDItemsTimeDialog, name, string, "HUDitemstime");
 ENDCLASS(XonoticHUDItemsTimeDialog)
index feb36e206cb3765d15c8479b8e0fe1fad5f8c8c8..3a3b72f6c9989df94d4695b135ed30f96f6186e5 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDModIconsDialog, XonoticRootDialog)
        METHOD(XonoticHUDModIconsDialog, fill, void(entity));
-       ATTRIB(XonoticHUDModIconsDialog, title, string, _("Mod Icons Panel"))
-       ATTRIB(XonoticHUDModIconsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDModIconsDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDModIconsDialog, rows, float, 15)
-       ATTRIB(XonoticHUDModIconsDialog, columns, float, 4)
-       ATTRIB(XonoticHUDModIconsDialog, name, string, "HUDmodicons")
-       ATTRIB(XonoticHUDModIconsDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDModIconsDialog, title, string, _("Mod Icons Panel"));
+       ATTRIB(XonoticHUDModIconsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDModIconsDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDModIconsDialog, rows, float, 15);
+       ATTRIB(XonoticHUDModIconsDialog, columns, float, 4);
+       ATTRIB(XonoticHUDModIconsDialog, name, string, "HUDmodicons");
+       ATTRIB(XonoticHUDModIconsDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDModIconsDialog)
index ad150076d04f936f0fda499222480d07c3c949d5..f816e4ffe042fd9d6f6f6ba84c46cecbdddc97b6 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDNotificationDialog, XonoticRootDialog)
        METHOD(XonoticHUDNotificationDialog, fill, void(entity));
-       ATTRIB(XonoticHUDNotificationDialog, title, string, _("Notification Panel"))
-       ATTRIB(XonoticHUDNotificationDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDNotificationDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDNotificationDialog, rows, float, 15)
-       ATTRIB(XonoticHUDNotificationDialog, columns, float, 4)
-       ATTRIB(XonoticHUDNotificationDialog, name, string, "HUDnotify")
-       ATTRIB(XonoticHUDNotificationDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDNotificationDialog, title, string, _("Notification Panel"));
+       ATTRIB(XonoticHUDNotificationDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDNotificationDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDNotificationDialog, rows, float, 15);
+       ATTRIB(XonoticHUDNotificationDialog, columns, float, 4);
+       ATTRIB(XonoticHUDNotificationDialog, name, string, "HUDnotify");
+       ATTRIB(XonoticHUDNotificationDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDNotificationDialog)
index 1d9c29289c285325cdf78c685dcd69d403995e82..f6f19135d1f7a257d001d4af567bd01d50d03adc 100644 (file)
@@ -3,12 +3,12 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDPhysicsDialog, XonoticRootDialog)
        METHOD(XonoticHUDPhysicsDialog, fill, void(entity));
-       ATTRIB(XonoticHUDPhysicsDialog, title, string, _("Physics Panel"))
-       ATTRIB(XonoticHUDPhysicsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDPhysicsDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDPhysicsDialog, rows, float, 15)
-       ATTRIB(XonoticHUDPhysicsDialog, columns, float, 4)
-       ATTRIB(XonoticHUDPhysicsDialog, name, string, "HUDphysics")
-       ATTRIB(XonoticHUDPhysicsDialog, sliderTopspeedTime, entity, NULL)
-       ATTRIB(XonoticHUDPhysicsDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDPhysicsDialog, title, string, _("Physics Panel"));
+       ATTRIB(XonoticHUDPhysicsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDPhysicsDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDPhysicsDialog, rows, float, 15);
+       ATTRIB(XonoticHUDPhysicsDialog, columns, float, 4);
+       ATTRIB(XonoticHUDPhysicsDialog, name, string, "HUDphysics");
+       ATTRIB(XonoticHUDPhysicsDialog, sliderTopspeedTime, entity);
+       ATTRIB(XonoticHUDPhysicsDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDPhysicsDialog)
index ebb09b0ba5cf7118fb0d7bdc52f7c2470421adfb..7f67fa6007b6f2423dfd54158ead5a20f038f36d 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDPowerupsDialog, XonoticRootDialog)
        METHOD(XonoticHUDPowerupsDialog, fill, void(entity));
-       ATTRIB(XonoticHUDPowerupsDialog, title, string, _("Powerups Panel"))
-       ATTRIB(XonoticHUDPowerupsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDPowerupsDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDPowerupsDialog, rows, float, 14)
-       ATTRIB(XonoticHUDPowerupsDialog, columns, float, 4)
-       ATTRIB(XonoticHUDPowerupsDialog, name, string, "HUDpowerups")
-       ATTRIB(XonoticHUDPowerupsDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDPowerupsDialog, title, string, _("Powerups Panel"));
+       ATTRIB(XonoticHUDPowerupsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDPowerupsDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDPowerupsDialog, rows, float, 14);
+       ATTRIB(XonoticHUDPowerupsDialog, columns, float, 4);
+       ATTRIB(XonoticHUDPowerupsDialog, name, string, "HUDpowerups");
+       ATTRIB(XonoticHUDPowerupsDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDPowerupsDialog)
index c4b7a90969b10498b62cc0b99137c814e86fef02..cc82959e6443af3c3ffc594da63504f66cf399dd 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDPressedKeysDialog, XonoticRootDialog)
        METHOD(XonoticHUDPressedKeysDialog, fill, void(entity));
-       ATTRIB(XonoticHUDPressedKeysDialog, title, string, _("Pressed Keys Panel"))
-       ATTRIB(XonoticHUDPressedKeysDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDPressedKeysDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDPressedKeysDialog, rows, float, 15)
-       ATTRIB(XonoticHUDPressedKeysDialog, columns, float, 4)
-       ATTRIB(XonoticHUDPressedKeysDialog, name, string, "HUDpressedkeys")
-       ATTRIB(XonoticHUDPressedKeysDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDPressedKeysDialog, title, string, _("Pressed Keys Panel"));
+       ATTRIB(XonoticHUDPressedKeysDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDPressedKeysDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDPressedKeysDialog, rows, float, 15);
+       ATTRIB(XonoticHUDPressedKeysDialog, columns, float, 4);
+       ATTRIB(XonoticHUDPressedKeysDialog, name, string, "HUDpressedkeys");
+       ATTRIB(XonoticHUDPressedKeysDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDPressedKeysDialog)
index 396f62b2638579ece5c2e21cccf89c3b0f9b775a..16f93c13d584579397f754ca3a78ec9dcdba7de4 100644 (file)
@@ -3,10 +3,10 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDQuickMenuDialog, XonoticRootDialog)
        METHOD(XonoticHUDQuickMenuDialog, fill, void(entity));
-       ATTRIB(XonoticHUDQuickMenuDialog, title, string, _("Quick Menu Panel"))
-       ATTRIB(XonoticHUDQuickMenuDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDQuickMenuDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDQuickMenuDialog, rows, float, 15)
-       ATTRIB(XonoticHUDQuickMenuDialog, columns, float, 4)
-       ATTRIB(XonoticHUDQuickMenuDialog, name, string, "HUDquickmenu")
+       ATTRIB(XonoticHUDQuickMenuDialog, title, string, _("Quick Menu Panel"));
+       ATTRIB(XonoticHUDQuickMenuDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDQuickMenuDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDQuickMenuDialog, rows, float, 15);
+       ATTRIB(XonoticHUDQuickMenuDialog, columns, float, 4);
+       ATTRIB(XonoticHUDQuickMenuDialog, name, string, "HUDquickmenu");
 ENDCLASS(XonoticHUDQuickMenuDialog)
index 81dca5126162f20b85c66deef4828c4dc6ca8d2a..7c814e3823e3586d7a2c9d14edb9a2a688f67cd0 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDRaceTimerDialog, XonoticRootDialog)
        METHOD(XonoticHUDRaceTimerDialog, fill, void(entity));
-       ATTRIB(XonoticHUDRaceTimerDialog, title, string, _("Race Timer Panel"))
-       ATTRIB(XonoticHUDRaceTimerDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDRaceTimerDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDRaceTimerDialog, rows, float, 15)
-       ATTRIB(XonoticHUDRaceTimerDialog, columns, float, 4)
-       ATTRIB(XonoticHUDRaceTimerDialog, name, string, "HUDracetimer")
-       ATTRIB(XonoticHUDRaceTimerDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDRaceTimerDialog, title, string, _("Race Timer Panel"));
+       ATTRIB(XonoticHUDRaceTimerDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDRaceTimerDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDRaceTimerDialog, rows, float, 15);
+       ATTRIB(XonoticHUDRaceTimerDialog, columns, float, 4);
+       ATTRIB(XonoticHUDRaceTimerDialog, name, string, "HUDracetimer");
+       ATTRIB(XonoticHUDRaceTimerDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDRaceTimerDialog)
index 6744e22741e4837fdb5c8d04272297ab775cfab9..04617b677f4c04a7444cf5de08444c1c67532f25 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDRadarDialog, XonoticRootDialog)
        METHOD(XonoticHUDRadarDialog, fill, void(entity));
-       ATTRIB(XonoticHUDRadarDialog, title, string, _("Radar Panel"))
-       ATTRIB(XonoticHUDRadarDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDRadarDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDRadarDialog, rows, float, 15)
-       ATTRIB(XonoticHUDRadarDialog, columns, float, 4)
-       ATTRIB(XonoticHUDRadarDialog, name, string, "HUDradar")
-       ATTRIB(XonoticHUDRadarDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDRadarDialog, title, string, _("Radar Panel"));
+       ATTRIB(XonoticHUDRadarDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDRadarDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDRadarDialog, rows, float, 15);
+       ATTRIB(XonoticHUDRadarDialog, columns, float, 4);
+       ATTRIB(XonoticHUDRadarDialog, name, string, "HUDradar");
+       ATTRIB(XonoticHUDRadarDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDRadarDialog)
index 68015eaa648d7abcc48894fb91965bb80e6c4eed..d97787404e921121ce3453be7f8a42bfd1a940fb 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDScoreDialog, XonoticRootDialog)
        METHOD(XonoticHUDScoreDialog, fill, void(entity));
-       ATTRIB(XonoticHUDScoreDialog, title, string, _("Score Panel"))
-       ATTRIB(XonoticHUDScoreDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDScoreDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDScoreDialog, rows, float, 15)
-       ATTRIB(XonoticHUDScoreDialog, columns, float, 4)
-       ATTRIB(XonoticHUDScoreDialog, name, string, "HUDscore")
-       ATTRIB(XonoticHUDScoreDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDScoreDialog, title, string, _("Score Panel"));
+       ATTRIB(XonoticHUDScoreDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDScoreDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDScoreDialog, rows, float, 15);
+       ATTRIB(XonoticHUDScoreDialog, columns, float, 4);
+       ATTRIB(XonoticHUDScoreDialog, name, string, "HUDscore");
+       ATTRIB(XonoticHUDScoreDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDScoreDialog)
index 7f750971312ba5069c2b44da2e33b7ed781414eb..61955495edd4c7ac985ee989c39fe2d23b27cf3c 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDTimerDialog, XonoticRootDialog)
        METHOD(XonoticHUDTimerDialog, fill, void(entity));
-       ATTRIB(XonoticHUDTimerDialog, title, string, _("Timer Panel"))
-       ATTRIB(XonoticHUDTimerDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDTimerDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDTimerDialog, rows, float, 15)
-       ATTRIB(XonoticHUDTimerDialog, columns, float, 4)
-       ATTRIB(XonoticHUDTimerDialog, name, string, "HUDtimer")
-       ATTRIB(XonoticHUDTimerDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDTimerDialog, title, string, _("Timer Panel"));
+       ATTRIB(XonoticHUDTimerDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDTimerDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDTimerDialog, rows, float, 15);
+       ATTRIB(XonoticHUDTimerDialog, columns, float, 4);
+       ATTRIB(XonoticHUDTimerDialog, name, string, "HUDtimer");
+       ATTRIB(XonoticHUDTimerDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDTimerDialog)
index 1ee05a2f882708a7a3c2940431bc18fa4bcc325b..061c69e56c4797b3d904739b0adcedfe97d79bba 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDVoteDialog, XonoticRootDialog)
        METHOD(XonoticHUDVoteDialog, fill, void(entity));
-       ATTRIB(XonoticHUDVoteDialog, title, string, _("Vote Panel"))
-       ATTRIB(XonoticHUDVoteDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDVoteDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDVoteDialog, rows, float, 15)
-       ATTRIB(XonoticHUDVoteDialog, columns, float, 4)
-       ATTRIB(XonoticHUDVoteDialog, name, string, "HUDvote")
-       ATTRIB(XonoticHUDVoteDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDVoteDialog, title, string, _("Vote Panel"));
+       ATTRIB(XonoticHUDVoteDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDVoteDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDVoteDialog, rows, float, 15);
+       ATTRIB(XonoticHUDVoteDialog, columns, float, 4);
+       ATTRIB(XonoticHUDVoteDialog, name, string, "HUDvote");
+       ATTRIB(XonoticHUDVoteDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDVoteDialog)
index 649f7ddd1c1e5dea99df638b5b5e43a3e45544cc..fad14749e6f4665f00b4c460c238a018811d0f37 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDWeaponsDialog, XonoticRootDialog)
        METHOD(XonoticHUDWeaponsDialog, fill, void(entity));
-       ATTRIB(XonoticHUDWeaponsDialog, title, string, _("Weapons Panel"))
-       ATTRIB(XonoticHUDWeaponsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDWeaponsDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticHUDWeaponsDialog, rows, float, 21)
-       ATTRIB(XonoticHUDWeaponsDialog, columns, float, 4)
-       ATTRIB(XonoticHUDWeaponsDialog, name, string, "HUDweapons")
-       ATTRIB(XonoticHUDWeaponsDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDWeaponsDialog, title, string, _("Weapons Panel"));
+       ATTRIB(XonoticHUDWeaponsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDWeaponsDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticHUDWeaponsDialog, rows, float, 21);
+       ATTRIB(XonoticHUDWeaponsDialog, columns, float, 4);
+       ATTRIB(XonoticHUDWeaponsDialog, name, string, "HUDweapons");
+       ATTRIB(XonoticHUDWeaponsDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDWeaponsDialog)
index e26cfad9b649b607ba467d55ad28880902e8b4ad..8a4479c6f88cc0ce8ed71e8880fa63602fb3c64a 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticHUDExitDialog, XonoticRootDialog)
        METHOD(XonoticHUDExitDialog, fill, void(entity));
-       ATTRIB(XonoticHUDExitDialog, title, string, _("Panel HUD Setup"))
-       ATTRIB(XonoticHUDExitDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticHUDExitDialog, intendedWidth, float, 0.8)
-       ATTRIB(XonoticHUDExitDialog, rows, float, 18)
-       ATTRIB(XonoticHUDExitDialog, columns, float, 8.2)
-       ATTRIB(XonoticHUDExitDialog, name, string, "HUDExit")
-       ATTRIB(XonoticHUDExitDialog, requiresConnection, float, true)
+       ATTRIB(XonoticHUDExitDialog, title, string, _("Panel HUD Setup"));
+       ATTRIB(XonoticHUDExitDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticHUDExitDialog, intendedWidth, float, 0.8);
+       ATTRIB(XonoticHUDExitDialog, rows, float, 18);
+       ATTRIB(XonoticHUDExitDialog, columns, float, 8.2);
+       ATTRIB(XonoticHUDExitDialog, name, string, "HUDExit");
+       ATTRIB(XonoticHUDExitDialog, requiresConnection, float, true);
 ENDCLASS(XonoticHUDExitDialog)
index 2b6f8b44da22eae4aa39be869ff9b4e0a8df6e15..1a6b3388abf4db6cd6003e4cddefe15fa9ce2501 100644 (file)
@@ -3,10 +3,10 @@
 #include "rootdialog.qh"
 CLASS(XonoticMonsterToolsDialog, XonoticRootDialog)
        METHOD(XonoticMonsterToolsDialog, fill, void(entity));
-       ATTRIB(XonoticMonsterToolsDialog, title, string, _("Monster Tools"))
-       ATTRIB(XonoticMonsterToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS)
-       ATTRIB(XonoticMonsterToolsDialog, intendedWidth, float, 0.8)
-       ATTRIB(XonoticMonsterToolsDialog, rows, float, 16)
-       ATTRIB(XonoticMonsterToolsDialog, columns, float, 4)
-       ATTRIB(XonoticMonsterToolsDialog, name, string, "MonsterTools")
+       ATTRIB(XonoticMonsterToolsDialog, title, string, _("Monster Tools"));
+       ATTRIB(XonoticMonsterToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS);
+       ATTRIB(XonoticMonsterToolsDialog, intendedWidth, float, 0.8);
+       ATTRIB(XonoticMonsterToolsDialog, rows, float, 16);
+       ATTRIB(XonoticMonsterToolsDialog, columns, float, 4);
+       ATTRIB(XonoticMonsterToolsDialog, name, string, "MonsterTools");
 ENDCLASS(XonoticMonsterToolsDialog)
index b18ca55607085d5bd3390eceb6ab9e52535cd87f..74b5285942c18238c3c72c3483dde736c64c1e4f 100644 (file)
@@ -3,10 +3,10 @@
 #include "dialog.qh"
 CLASS(XonoticMultiplayerDialog, XonoticDialog)
        METHOD(XonoticMultiplayerDialog, fill, void(entity));
-       ATTRIB(XonoticMultiplayerDialog, title, string, _("Multiplayer"))
-       ATTRIB(XonoticMultiplayerDialog, tooltip, string, _("Play online, against your friends in LAN, view demos or change player settings"))
-       ATTRIB(XonoticMultiplayerDialog, color, vector, SKINCOLOR_DIALOG_MULTIPLAYER)
-       ATTRIB(XonoticMultiplayerDialog, intendedWidth, float, 0.96)
-       ATTRIB(XonoticMultiplayerDialog, rows, float, 24)
-       ATTRIB(XonoticMultiplayerDialog, columns, float, 4)
+       ATTRIB(XonoticMultiplayerDialog, title, string, _("Multiplayer"));
+       ATTRIB(XonoticMultiplayerDialog, tooltip, string, _("Play online, against your friends in LAN, view demos or change player settings"));
+       ATTRIB(XonoticMultiplayerDialog, color, vector, SKINCOLOR_DIALOG_MULTIPLAYER);
+       ATTRIB(XonoticMultiplayerDialog, intendedWidth, float, 0.96);
+       ATTRIB(XonoticMultiplayerDialog, rows, float, 24);
+       ATTRIB(XonoticMultiplayerDialog, columns, float, 4);
 ENDCLASS(XonoticMultiplayerDialog)
index 85e0e9e69887bdf5bf5064de494eb46cbb4cfe64..481914200a99f625338e4be2466d158a1e5bcd34 100644 (file)
@@ -58,6 +58,27 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax,
        t.configureXonoticTextSliderValues(t);
 }
 
+void GameType_ConfigureSliders_for_CurrentGametype(entity me)
+{
+       switch(MapInfo_CurrentGametype())
+       {
+               case MAPINFO_TYPE_CA:              GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_ca_teams_override",          _("The amount of frags needed before the match will end")); break;
+               case MAPINFO_TYPE_FREEZETAG:       GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_freezetag_teams_override",   _("The amount of frags needed before the match will end")); break;
+               case MAPINFO_TYPE_CTF:             GameType_ConfigureSliders(me, _("Capture limit:"),   1,   20,  1, "capturelimit_override",     string_null,                    _("The amount of captures needed before the match will end")); break;
+               case MAPINFO_TYPE_DOMINATION:      GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, "g_domination_point_limit",  "g_domination_teams_override",  _("The amount of points needed before the match will end")); break;
+               case MAPINFO_TYPE_KEYHUNT:         GameType_ConfigureSliders(me, _("Point limit:"),   200, 1500, 50, "g_keyhunt_point_limit",     "g_keyhunt_teams_override",     _("The amount of points needed before the match will end")); break;
+               case MAPINFO_TYPE_LMS:             GameType_ConfigureSliders(me, _("Lives:"),           3,   50,  1, "g_lms_lives_override",      string_null,                    string_null); break;
+               case MAPINFO_TYPE_RACE:            GameType_ConfigureSliders(me, _("Laps:"),            1,   25,  1, "g_race_laps_limit",         string_null,                    string_null); break;
+               case MAPINFO_TYPE_NEXBALL:         GameType_ConfigureSliders(me, _("Goals:"),           1,   50,  1, "g_nexball_goallimit",       string_null,                    _("The amount of goals needed before the match will end")); break;
+               case MAPINFO_TYPE_ASSAULT:         GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_ONSLAUGHT:       GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_CTS:             GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_INVASION:        GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"),     5,  100,  5, "g_tdm_point_limit",         "g_tdm_teams_override",         _("The amount of points needed before the match will end")); break;
+               default:                           GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        string_null,                    _("The amount of frags needed before the match will end")); break;
+       }
+}
+
 entity makeXonoticServerCreateTab()
 {
        entity me;
@@ -210,28 +231,12 @@ void XonoticServerCreateTab_fill(entity me)
                        e.onClickEntity = me.mapListBox;
                        me.mapListBox.startButton = e;
 
-       me.gameTypeChangeNotify(me);
+       GameType_ConfigureSliders_for_CurrentGametype(me);
 }
 
 void XonoticServerCreateTab_gameTypeChangeNotify(entity me)
 {
-       switch(MapInfo_CurrentGametype())
-       {
-               case MAPINFO_TYPE_CA:              GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_ca_teams_override",          _("The amount of frags needed before the match will end")); break;
-               case MAPINFO_TYPE_FREEZETAG:       GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_freezetag_teams_override",   _("The amount of frags needed before the match will end")); break;
-               case MAPINFO_TYPE_CTF:             GameType_ConfigureSliders(me, _("Capture limit:"),   1,   20,  1, "capturelimit_override",     string_null,                    _("The amount of captures needed before the match will end")); break;
-               case MAPINFO_TYPE_DOMINATION:      GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, "g_domination_point_limit",  "g_domination_teams_override",  _("The amount of points needed before the match will end")); break;
-               case MAPINFO_TYPE_KEYHUNT:         GameType_ConfigureSliders(me, _("Point limit:"),   200, 1500, 50, "g_keyhunt_point_limit",     "g_keyhunt_teams_override",     _("The amount of points needed before the match will end")); break;
-               case MAPINFO_TYPE_LMS:             GameType_ConfigureSliders(me, _("Lives:"),           3,   50,  1, "g_lms_lives_override",      string_null,                    string_null); break;
-               case MAPINFO_TYPE_RACE:            GameType_ConfigureSliders(me, _("Laps:"),            1,   25,  1, "g_race_laps_limit",         string_null,                    string_null); break;
-               case MAPINFO_TYPE_NEXBALL:         GameType_ConfigureSliders(me, _("Goals:"),           1,   50,  1, "g_nexball_goallimit",       string_null,                    _("The amount of goals needed before the match will end")); break;
-               case MAPINFO_TYPE_ASSAULT:         GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_ONSLAUGHT:       GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_CTS:             GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_INVASION:        GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"),     5,  100,  5, "g_tdm_point_limit",         "g_tdm_teams_override",         _("The amount of points needed before the match will end")); break;
-               default:                           GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        string_null,                    _("The amount of frags needed before the match will end")); break;
-       }
+       GameType_ConfigureSliders_for_CurrentGametype(me);
 
        me.mapListBox.refilter(me.mapListBox);
 }
index 5a747a94530c041a7f630500dc61d02de8a954bb..e275522864a84ee9af820e0cb6f55f7323df9efb 100644 (file)
@@ -5,15 +5,15 @@ CLASS(XonoticServerCreateTab, XonoticTab)
        METHOD(XonoticServerCreateTab, fill, void(entity));
        METHOD(XonoticServerCreateTab, gameTypeChangeNotify, void(entity));
        METHOD(XonoticServerCreateTab, gameTypeSelectNotify, void(entity));
-       ATTRIB(XonoticServerCreateTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticServerCreateTab, rows, float, 23)
-       ATTRIB(XonoticServerCreateTab, columns, float, 6.2)  // added extra .2 for center space
+       ATTRIB(XonoticServerCreateTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticServerCreateTab, rows, float, 23);
+       ATTRIB(XonoticServerCreateTab, columns, float, 6.2);  // added extra .2 for center space
 
-       ATTRIB(XonoticServerCreateTab, mapListBox, entity, NULL)
-       ATTRIB(XonoticServerCreateTab, sliderFraglimit, entity, NULL)
-       ATTRIB(XonoticServerCreateTab, sliderTeams, entity, NULL)
-       ATTRIB(XonoticServerCreateTab, sliderTimelimit, entity, NULL)
-       ATTRIB(XonoticServerCreateTab, labelFraglimit, entity, NULL)
-       ATTRIB(XonoticServerCreateTab, labelTeams, entity, NULL)
+       ATTRIB(XonoticServerCreateTab, mapListBox, entity);
+       ATTRIB(XonoticServerCreateTab, sliderFraglimit, entity);
+       ATTRIB(XonoticServerCreateTab, sliderTeams, entity);
+       ATTRIB(XonoticServerCreateTab, sliderTimelimit, entity);
+       ATTRIB(XonoticServerCreateTab, labelFraglimit, entity);
+       ATTRIB(XonoticServerCreateTab, labelTeams, entity);
 ENDCLASS(XonoticServerCreateTab)
 entity makeXonoticServerCreateTab();
index 9bc82ebc28754bb8bf5ebbb87c697a3d50b7f813..2f2ab901a84239a9c37c21866552b449f7089ed6 100644 (file)
@@ -35,11 +35,11 @@ void XonoticMapInfoDialog_loadMapInfo(entity me, int i, entity mlb)
        else
                me.previewImage.src = me.currentMapPreviewImage;
 
-       for(i = 0; i < GameType_GetCount(); ++i)
+       for(i = 0; i < GameType_GetTotalCount(); ++i)
        {
                entity e;
                e = me.(typeLabels[i]);
-               e.disabled = !(MapInfo_Map_supportedGametypes & GameType_GetID(i));
+               e.disabled = !(MapInfo_Map_supportedGametypes & GameType_GetID(i).m_flags);
        }
 
        MapInfo_ClearTemps();
@@ -69,9 +69,9 @@ void XonoticMapInfoDialog_fill(entity me)
        me.TR(me);
                me.TD(me, 1, w, e = makeXonoticTextLabel(0, _("Game types:")));
 
-       n = ceil(GameType_GetCount() / (me.rows - 6));
+       n = ceil(GameType_GetTotalCount() / (me.rows - 6));
        wgt = (w - 0.2) / n;
-       for(i = 0; i < GameType_GetCount(); ++i)
+       for(i = 0; i < GameType_GetTotalCount(); ++i)
        {
                if(mod(i, n) == 0)
                {
index 13acfcc777b744066d82ab074942091bef85ee40..b2efb97619c8c8b7d7ea1d47482e669e1deabb6a 100644 (file)
@@ -4,24 +4,24 @@
 CLASS(XonoticMapInfoDialog, XonoticDialog)
        METHOD(XonoticMapInfoDialog, fill, void(entity));
        METHOD(XonoticMapInfoDialog, loadMapInfo, void(entity, float, entity));
-       ATTRIB(XonoticMapInfoDialog, title, string, _("Map Information"))
-       ATTRIB(XonoticMapInfoDialog, color, vector, SKINCOLOR_DIALOG_MAPINFO)
-       ATTRIB(XonoticMapInfoDialog, intendedWidth, float, 1.0)
-       ATTRIB(XonoticMapInfoDialog, rows, float, 11)
-       ATTRIB(XonoticMapInfoDialog, columns, float, 10)
+       ATTRIB(XonoticMapInfoDialog, title, string, _("Map Information"));
+       ATTRIB(XonoticMapInfoDialog, color, vector, SKINCOLOR_DIALOG_MAPINFO);
+       ATTRIB(XonoticMapInfoDialog, intendedWidth, float, 1.0);
+       ATTRIB(XonoticMapInfoDialog, rows, float, 11);
+       ATTRIB(XonoticMapInfoDialog, columns, float, 10);
 
-       ATTRIB(XonoticMapInfoDialog, previewImage, entity, NULL)
-       ATTRIB(XonoticMapInfoDialog, titleLabel, entity, NULL)
-       ATTRIB(XonoticMapInfoDialog, authorLabel, entity, NULL)
-       ATTRIB(XonoticMapInfoDialog, descriptionLabel, entity, NULL)
-       ATTRIB(XonoticMapInfoDialog, featuresLabel, entity, NULL)
+       ATTRIB(XonoticMapInfoDialog, previewImage, entity);
+       ATTRIB(XonoticMapInfoDialog, titleLabel, entity);
+       ATTRIB(XonoticMapInfoDialog, authorLabel, entity);
+       ATTRIB(XonoticMapInfoDialog, descriptionLabel, entity);
+       ATTRIB(XonoticMapInfoDialog, featuresLabel, entity);
 
-       ATTRIBARRAY(XonoticMapInfoDialog, typeLabels, entity, 24)
+       ATTRIBARRAY(XonoticMapInfoDialog, typeLabels, entity, 24);
 
-       ATTRIB(XonoticMapInfoDialog, currentMapIndex, float, 0)
-       ATTRIB(XonoticMapInfoDialog, currentMapBSPName, string, string_null)
-       ATTRIB(XonoticMapInfoDialog, currentMapTitle, string, string_null)
-       ATTRIB(XonoticMapInfoDialog, currentMapAuthor, string, string_null)
-       ATTRIB(XonoticMapInfoDialog, currentMapDescription, string, string_null)
-       ATTRIB(XonoticMapInfoDialog, currentMapPreviewImage, string, string_null)
+       ATTRIB(XonoticMapInfoDialog, currentMapIndex, float, 0);
+       ATTRIB(XonoticMapInfoDialog, currentMapBSPName, string);
+       ATTRIB(XonoticMapInfoDialog, currentMapTitle, string);
+       ATTRIB(XonoticMapInfoDialog, currentMapAuthor, string);
+       ATTRIB(XonoticMapInfoDialog, currentMapDescription, string);
+       ATTRIB(XonoticMapInfoDialog, currentMapPreviewImage, string);
 ENDCLASS(XonoticMapInfoDialog)
index f2f7f5c8e4862fe96cb94e4aaf5ccff6f23221d5..ab52949741be985d26707db57a35fd6262182f58 100644 (file)
@@ -1,5 +1,5 @@
 #include "dialog_multiplayer_create_mutators.qh"
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 #include "weaponarenacheckbox.qh"
 #include "checkbox.qh"
@@ -53,8 +53,6 @@ string WeaponArenaString()
        return weaponarenastring;
 }
 
-AUTOCVAR(g_grappling_hook, bool, _("let players spawn with the grappling hook which allows them to pull themselves up"));
-
 string XonoticMutatorsDialog_toString(entity me)
 {
        string s;
@@ -73,13 +71,13 @@ string XonoticMutatorsDialog_toString(entity me)
                s = strcat(s, ", ", _("Invincible Projectiles"));
        if(cvar_string("g_weaponarena") != "0")
                s = strcat(s, ", ", WeaponArenaString());
-       else if(cvar("g_balance_blaster_weaponstart") == 0)
+       else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
                s = strcat(s, ", ", _("No start weapons"));
        if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
                s = strcat(s, ", ", _("Low gravity"));
        if(cvar("g_cloaked"))
                s = strcat(s, ", ", _("Cloaked"));
-       if(autocvar_g_grappling_hook)
+       if(cvar("g_grappling_hook"))
                s = strcat(s, ", ", _("Hook"));
        if(cvar("g_midair"))
                s = strcat(s, ", ", _("Midair"));
@@ -145,7 +143,7 @@ float checkCompatibility_weaponarena_weapon(entity me)
                return 0;
        if(cvar_string("g_weaponarena") == "0")
                return 0;
-       if(cvar_string("g_balance_blaster_weaponstart") == "0")
+       if(cvar_string("g_balance_blaster_weaponstartoverride") == "0")
                return 0;
        return 1;
 }
@@ -281,9 +279,9 @@ void XonoticMutatorsDialog_fill(entity me)
                        setDependent(e, "g_nix", 1, 1);
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstart", "0", _("No start weapons"), "-"));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstartoverride", "0", _("No start weapons"), "-"));
                        e.cvarOffValue = "-1";
-                       makeMulti(e, "g_balance_shotgun_weaponstart g_balance_machinegun_weaponstart g_balance_devastator_weaponstart g_balance_minelayer_weaponstart g_balance_electro_weaponstart g_balance_crylink_weaponstart g_balance_hagar_weaponstart g_balance_porto_weaponstart g_balance_vaporizer_weaponstart g_balance_hook_weaponstart g_balance_rifle_weaponstart g_balance_fireball_weaponstart g_balance_seeker_weaponstart g_balance_tuba_weaponstart g_balance_arc_weaponstart g_balance_vortex_weaponstart g_balance_mortar_weaponstart");
+                       makeMulti(e, "g_balance_shotgun_weaponstartoverride g_balance_machinegun_weaponstartoverride g_balance_devastator_weaponstartoverride g_balance_minelayer_weaponstartoverride g_balance_electro_weaponstartoverride g_balance_crylink_weaponstartoverride g_balance_hagar_weaponstartoverride g_balance_porto_weaponstartoverride g_balance_vaporizer_weaponstartoverride g_balance_hook_weaponstartoverride g_balance_rifle_weaponstartoverride g_balance_fireball_weaponstartoverride g_balance_seeker_weaponstartoverride g_balance_tuba_weaponstartoverride g_balance_arc_weaponstartoverride g_balance_vortex_weaponstartoverride g_balance_mortar_weaponstartoverride");
 
        me.gotoRC(me, me.rows - 1, 0);
                me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
index ede8f4cb1fcdab15a2cfe91b3282093b774cc142..48ccc6b48ea0ca853e7710efcf18210af2662ef4 100644 (file)
@@ -6,10 +6,10 @@ CLASS(XonoticMutatorsDialog, XonoticDialog)
        METHOD(XonoticMutatorsDialog, fill, void(entity));
        METHOD(XonoticMutatorsDialog, showNotify, void(entity));
        METHOD(XonoticMutatorsDialog, close, void(entity));
-       ATTRIB(XonoticMutatorsDialog, title, string, _("Mutators"))
-       ATTRIB(XonoticMutatorsDialog, color, vector, SKINCOLOR_DIALOG_MUTATORS)
-       ATTRIB(XonoticMutatorsDialog, intendedWidth, float, 0.9)
-       ATTRIB(XonoticMutatorsDialog, rows, float, 20)
-       ATTRIB(XonoticMutatorsDialog, columns, float, 6)
-       ATTRIB(XonoticMutatorsDialog, refilterEntity, entity, NULL)
+       ATTRIB(XonoticMutatorsDialog, title, string, _("Mutators"));
+       ATTRIB(XonoticMutatorsDialog, color, vector, SKINCOLOR_DIALOG_MUTATORS);
+       ATTRIB(XonoticMutatorsDialog, intendedWidth, float, 0.9);
+       ATTRIB(XonoticMutatorsDialog, rows, float, 20);
+       ATTRIB(XonoticMutatorsDialog, columns, float, 6);
+       ATTRIB(XonoticMutatorsDialog, refilterEntity, entity);
 ENDCLASS(XonoticMutatorsDialog)
index f387f429b651f62895b974dc3550aa58b295bf8a..a34d0d80a1cb12a9e713efb213d147b5f272226d 100644 (file)
@@ -14,6 +14,13 @@ entity makeXonoticServerListTab()
        me.configureDialog(me);
        return me;
 }
+
+void XonoticServerListTab_refresh(entity this, entity slist)
+{
+    bool clear = false;
+    slist.refreshServerList(slist, clear ? REFRESHSERVERLIST_RESET : REFRESHSERVERLIST_ASK);
+}
+
 void XonoticServerListTab_fill(entity me)
 {
        entity e, slist;
@@ -21,13 +28,13 @@ void XonoticServerListTab_fill(entity me)
        slist  = makeXonoticServerList();
 
        me.gotoRC(me, 0.5, 0);
-               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Filter:")));
-               me.TD(me, 1, 2.8, e = makeXonoticInputBox(0, string_null));
+               me.TD(me, 1, 0.5, e = makeXonoticTextLabel(1, _("Filter:")));
+               me.TD(me, 1, 2, e = makeXonoticInputBox(0, string_null));
                        e.onChange = ServerList_Filter_Change;
                        e.onChangeEntity = slist;
                        slist.controlledTextbox = e;
 
-       me.gotoRC(me, 0.5, 3.6);
+       me.gotoRC(me, 0.5, 2.6);
                me.TD(me, 1, 0.9, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories"))));
                        e.onClickEntity = slist;
                        e.onClick = ServerList_Categories_Click;
@@ -43,6 +50,9 @@ void XonoticServerListTab_fill(entity me)
                        e.onClick = ServerList_ShowFull_Click;
                me.TD(me, 1, 0.6, e = makeXonoticCheckBox_T(0, "net_slist_pause", _("Pause"),
                        _("Pause updating the server list to prevent servers from \"jumping around\"")));
+               me.TD(me, 1, 1, e = makeXonoticButton_T(_("Refresh"), '0 0 0', _("Reload the server list")));
+                       e.onClick = XonoticServerListTab_refresh;
+                       e.onClickEntity = slist;
 
        me.gotoRC(me, 2, 0);
                me.TD(me, 1, 1, slist.sortButton1 = makeXonoticButton(string_null, '0 0 0'));
index 7fa837976cfb506f2303cbed9c0ef856f65e4b06..a663eb527be116ba9e0b75bf953e6aba47f2dc2c 100644 (file)
@@ -3,8 +3,8 @@
 #include "tab.qh"
 CLASS(XonoticServerListTab, XonoticTab)
        METHOD(XonoticServerListTab, fill, void(entity));
-       ATTRIB(XonoticServerListTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticServerListTab, rows, float, 23)
-       ATTRIB(XonoticServerListTab, columns, float, 6.5)
+       ATTRIB(XonoticServerListTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticServerListTab, rows, float, 23);
+       ATTRIB(XonoticServerListTab, columns, float, 6.5);
 ENDCLASS(XonoticServerListTab)
 entity makeXonoticServerListTab();
index d5532bcd92319757be8e3eea5b9f6ce7a13f39e8..2f37eb7e2b665c260d6e5174797eaaec3d58cda4 100644 (file)
@@ -10,7 +10,7 @@
 void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
 {
        bool pure_available;
-       float m, pure_violations, freeslots, j, numh, maxp, numb, sflags;
+       float m, pure_violations, freeslots, numh, maxp, numb, sflags;
        string s, typestr, versionstr, k, v, modname;
 
        // ====================================
@@ -96,7 +96,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
        freeslots = -1;
        sflags = -1;
        modname = "";
-       for(j = 2; j < m; ++j)
+       for(int j = 2; j < m; ++j)
        {
                if(argv(j) == "")
                        break;
@@ -124,7 +124,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
        if(s != "data")
                modname = sprintf("%s (%s)", modname, s);
 
-       j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type
+       Gametype j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type
        if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it
 
        me.currentServerType = strzone(typestr);
index 201f737e9125c80f80b93b66775e215386634fad..68f5ab8ca0c08cc95084715202fbb9ec5e442f36 100644 (file)
@@ -4,42 +4,42 @@
 CLASS(XonoticServerInfoDialog, XonoticDialog)
        METHOD(XonoticServerInfoDialog, fill, void(entity));
        METHOD(XonoticServerInfoDialog, loadServerInfo, void(entity, float));
-       ATTRIB(XonoticServerInfoDialog, title, string, _("Server Information"))
-       ATTRIB(XonoticServerInfoDialog, color, vector, SKINCOLOR_DIALOG_SERVERINFO)
-       ATTRIB(XonoticServerInfoDialog, intendedWidth, float, 0.8)
-       ATTRIB(XonoticServerInfoDialog, rows, float, 18)
-       ATTRIB(XonoticServerInfoDialog, columns, float, 6.2)
+       ATTRIB(XonoticServerInfoDialog, title, string, _("Server Information"));
+       ATTRIB(XonoticServerInfoDialog, color, vector, SKINCOLOR_DIALOG_SERVERINFO);
+       ATTRIB(XonoticServerInfoDialog, intendedWidth, float, 0.8);
+       ATTRIB(XonoticServerInfoDialog, rows, float, 18);
+       ATTRIB(XonoticServerInfoDialog, columns, float, 6.2);
 
-       ATTRIB(XonoticServerInfoDialog, currentServerName, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerCName, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerType, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerMap, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerPlayers, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerNumPlayers, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerNumBots, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerNumFreeSlots, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerMod, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerVersion, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerKey, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerID, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerEncrypt, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerPure, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerName, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerCName, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerType, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerMap, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerPlayers, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerNumPlayers, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerNumBots, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerNumFreeSlots, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerMod, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerVersion, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerKey, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerID, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerEncrypt, string);
+       ATTRIB(XonoticServerInfoDialog, currentServerPure, string);
 
-       ATTRIB(XonoticServerInfoDialog, nameLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, cnameLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, typeLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, mapLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, rawPlayerList, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, numPlayersLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, numBotsLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, numFreeSlotsLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, modLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, versionLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, keyLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, idLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, encryptLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, canConnectLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, pureLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, nameLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, cnameLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, typeLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, mapLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, rawPlayerList, entity);
+       ATTRIB(XonoticServerInfoDialog, numPlayersLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, numBotsLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, numFreeSlotsLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, modLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, versionLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, keyLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, idLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, encryptLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, canConnectLabel, entity);
+       ATTRIB(XonoticServerInfoDialog, pureLabel, entity);
 ENDCLASS(XonoticServerInfoDialog)
 
 void Join_Click(entity btn, entity me);
index 96c1aeb75dd74a4aac9d279557fb582ac7bcca91..9713e82c2da595fc6833dc287021a00cae5f7f00 100644 (file)
@@ -3,9 +3,9 @@
 #include "tab.qh"
 CLASS(XonoticMediaTab, XonoticTab)
        METHOD(XonoticMediaTab, fill, void(entity));
-       ATTRIB(XonoticMediaTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticMediaTab, rows, float, 23)
-       ATTRIB(XonoticMediaTab, columns, float, 3)
-       ATTRIB(XonoticMediaTab, name, string, "Media")
+       ATTRIB(XonoticMediaTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticMediaTab, rows, float, 23);
+       ATTRIB(XonoticMediaTab, columns, float, 3);
+       ATTRIB(XonoticMediaTab, name, string, "Media");
 ENDCLASS(XonoticMediaTab)
 entity makeXonoticMediaTab();
index f41bb9e812f31ef0567bc655b478c551429057d5..cf3548d77409e791fecf40d9bf1caec2cab8371d 100644 (file)
@@ -3,10 +3,10 @@
 #include "tab.qh"
 CLASS(XonoticDemoBrowserTab, XonoticTab)
        METHOD(XonoticDemoBrowserTab, fill, void(entity));
-       ATTRIB(XonoticDemoBrowserTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticDemoBrowserTab, rows, float, 21)
-       ATTRIB(XonoticDemoBrowserTab, columns, float, 6.5)
-       ATTRIB(XonoticDemoBrowserTab, name, string, "DemoBrowser")
-       ATTRIB(XonoticDemoBrowserTab, democlicktype, float, 0)
+       ATTRIB(XonoticDemoBrowserTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticDemoBrowserTab, rows, float, 21);
+       ATTRIB(XonoticDemoBrowserTab, columns, float, 6.5);
+       ATTRIB(XonoticDemoBrowserTab, name, string, "DemoBrowser");
+       ATTRIB(XonoticDemoBrowserTab, democlicktype, float, 0);
 ENDCLASS(XonoticDemoBrowserTab)
 entity makeXonoticDemoBrowserTab();
index 598dd5503f8f918f333a14a42d665ce26b8c23f6..934490f755ece18731820912a21228671fec5109 100644 (file)
@@ -3,9 +3,9 @@
 #include "dialog.qh"
 CLASS(XonoticDemoStartConfirmDialog, XonoticDialog)
        METHOD(XonoticDemoStartConfirmDialog, fill, void(entity));
-       ATTRIB(XonoticDemoStartConfirmDialog, title, string, _("Disconnect"))
-       ATTRIB(XonoticDemoStartConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM)
-       ATTRIB(XonoticDemoStartConfirmDialog, intendedWidth, float, 0.5)
-       ATTRIB(XonoticDemoStartConfirmDialog, rows, float, 4)
-       ATTRIB(XonoticDemoStartConfirmDialog, columns, float, 2)
+       ATTRIB(XonoticDemoStartConfirmDialog, title, string, _("Disconnect"));
+       ATTRIB(XonoticDemoStartConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM);
+       ATTRIB(XonoticDemoStartConfirmDialog, intendedWidth, float, 0.5);
+       ATTRIB(XonoticDemoStartConfirmDialog, rows, float, 4);
+       ATTRIB(XonoticDemoStartConfirmDialog, columns, float, 2);
 ENDCLASS(XonoticDemoStartConfirmDialog)
index 96e2d631036698fa71441f70f3618e632da72ef1..80c2766de2e4047b20f6df735863d823efc22439 100644 (file)
@@ -3,9 +3,9 @@
 #include "dialog.qh"
 CLASS(XonoticDemoTimeConfirmDialog, XonoticDialog)
        METHOD(XonoticDemoTimeConfirmDialog, fill, void(entity));
-       ATTRIB(XonoticDemoTimeConfirmDialog, title, string, _("Disconnect"))
-       ATTRIB(XonoticDemoTimeConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM)
-       ATTRIB(XonoticDemoTimeConfirmDialog, intendedWidth, float, 0.5)
-       ATTRIB(XonoticDemoTimeConfirmDialog, rows, float, 4)
-       ATTRIB(XonoticDemoTimeConfirmDialog, columns, float, 2)
+       ATTRIB(XonoticDemoTimeConfirmDialog, title, string, _("Disconnect"));
+       ATTRIB(XonoticDemoTimeConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM);
+       ATTRIB(XonoticDemoTimeConfirmDialog, intendedWidth, float, 0.5);
+       ATTRIB(XonoticDemoTimeConfirmDialog, rows, float, 4);
+       ATTRIB(XonoticDemoTimeConfirmDialog, columns, float, 2);
 ENDCLASS(XonoticDemoTimeConfirmDialog)
index 4d98f6ec17ba051ae165a2ee0e6995e7fe583460..6172dae03cbff4bffadab55e694ab68fd9859c72 100644 (file)
@@ -3,9 +3,9 @@
 #include "tab.qh"
 CLASS(XonoticMusicPlayerTab, XonoticTab)
        METHOD(XonoticMusicPlayerTab, fill, void(entity));
-       ATTRIB(XonoticMusicPlayerTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticMusicPlayerTab, rows, float, 21)
-       ATTRIB(XonoticMusicPlayerTab, columns, float, 6.5)
-       ATTRIB(XonoticMusicPlayerTab, name, string, "MusicPlayer")
+       ATTRIB(XonoticMusicPlayerTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticMusicPlayerTab, rows, float, 21);
+       ATTRIB(XonoticMusicPlayerTab, columns, float, 6.5);
+       ATTRIB(XonoticMusicPlayerTab, name, string, "MusicPlayer");
 ENDCLASS(XonoticMusicPlayerTab)
 entity makeXonoticMusicPlayerTab();
index 62ea8438a069a65cc5e1cdb70c6f8f30f1079f61..66dc3cf077fac5591b20881fcb52d70cf53b8575 100644 (file)
@@ -3,13 +3,13 @@
 #include "tab.qh"
 CLASS(XonoticScreenshotBrowserTab, XonoticTab)
        METHOD(XonoticScreenshotBrowserTab, fill, void(entity));
-       ATTRIB(XonoticScreenshotBrowserTab, intendedWidth, float, 1)
-       ATTRIB(XonoticScreenshotBrowserTab, rows, float, 21)
-       ATTRIB(XonoticScreenshotBrowserTab, columns, float, 6.5)
-       ATTRIB(XonoticScreenshotBrowserTab, name, string, "ScreenshotBrowser")
+       ATTRIB(XonoticScreenshotBrowserTab, intendedWidth, float, 1);
+       ATTRIB(XonoticScreenshotBrowserTab, rows, float, 21);
+       ATTRIB(XonoticScreenshotBrowserTab, columns, float, 6.5);
+       ATTRIB(XonoticScreenshotBrowserTab, name, string, "ScreenshotBrowser");
 
        METHOD(XonoticScreenshotBrowserTab, loadPreviewScreenshot, void(entity, string));
-       ATTRIB(XonoticScreenshotBrowserTab, screenshotImage, entity, NULL)
-       ATTRIB(XonoticScreenshotBrowserTab, currentScrPath, string, string_null)
+       ATTRIB(XonoticScreenshotBrowserTab, screenshotImage, entity);
+       ATTRIB(XonoticScreenshotBrowserTab, currentScrPath, string);
 ENDCLASS(XonoticScreenshotBrowserTab)
 entity makeXonoticScreenshotBrowserTab();
index c4f7e61262eca64c609e94d117853a91ad566a27..ad1656e4f71555e28b268adcf5a1316fcd0aa3f0 100644 (file)
@@ -9,14 +9,14 @@ CLASS(XonoticScreenshotViewerDialog, XonoticDialog)
        METHOD(XonoticScreenshotViewerDialog, keyDown, float(entity, float, float, float));
        METHOD(XonoticScreenshotViewerDialog, loadScreenshot, void(entity, string));
        METHOD(XonoticScreenshotViewerDialog, close, void(entity));
-       ATTRIB(XonoticScreenshotViewerDialog, title, string, "Screenshot Viewer")
-       ATTRIB(XonoticScreenshotViewerDialog, name, string, "ScreenshotViewer")
-       ATTRIB(XonoticScreenshotViewerDialog, intendedWidth, float, 1)
-       ATTRIB(XonoticScreenshotViewerDialog, rows, float, 25)
-       ATTRIB(XonoticScreenshotViewerDialog, columns, float, 4)
-       ATTRIB(XonoticScreenshotViewerDialog, color, vector, SKINCOLOR_DIALOG_SCREENSHOTVIEWER)
-       ATTRIB(XonoticScreenshotViewerDialog, scrList, entity, NULL)
-       ATTRIB(XonoticScreenshotViewerDialog, screenshotImage, entity, NULL)
-       ATTRIB(XonoticScreenshotViewerDialog, slideShowButton, entity, NULL)
-       ATTRIB(XonoticScreenshotViewerDialog, currentScrPath, string, string_null)
+       ATTRIB(XonoticScreenshotViewerDialog, title, string, "Screenshot Viewer");
+       ATTRIB(XonoticScreenshotViewerDialog, name, string, "ScreenshotViewer");
+       ATTRIB(XonoticScreenshotViewerDialog, intendedWidth, float, 1);
+       ATTRIB(XonoticScreenshotViewerDialog, rows, float, 25);
+       ATTRIB(XonoticScreenshotViewerDialog, columns, float, 4);
+       ATTRIB(XonoticScreenshotViewerDialog, color, vector, SKINCOLOR_DIALOG_SCREENSHOTVIEWER);
+       ATTRIB(XonoticScreenshotViewerDialog, scrList, entity);
+       ATTRIB(XonoticScreenshotViewerDialog, screenshotImage, entity);
+       ATTRIB(XonoticScreenshotViewerDialog, slideShowButton, entity);
+       ATTRIB(XonoticScreenshotViewerDialog, currentScrPath, string);
 ENDCLASS(XonoticScreenshotViewerDialog)
index f60e9e0bb3450b59a28a8a5e3725654506a86a63..2285efcafa370cb6a2da6e8a0cc6c895176b96b6 100644 (file)
@@ -4,10 +4,10 @@
 CLASS(XonoticProfileTab, XonoticTab)
        METHOD(XonoticProfileTab, fill, void(entity));
        METHOD(XonoticProfileTab, draw, void(entity));
-       ATTRIB(XonoticProfileTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticProfileTab, rows, float, 23)
-       ATTRIB(XonoticProfileTab, columns, float, 6.1)  // added extra .2 for center space
-       ATTRIB(XonoticProfileTab, playerNameLabel, entity, NULL)
-       ATTRIB(XonoticProfileTab, playerNameLabelAlpha, float, SKINALPHA_HEADER)
+       ATTRIB(XonoticProfileTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticProfileTab, rows, float, 23);
+       ATTRIB(XonoticProfileTab, columns, float, 6.1);  // added extra .2 for center space
+       ATTRIB(XonoticProfileTab, playerNameLabel, entity);
+       ATTRIB(XonoticProfileTab, playerNameLabelAlpha, float, SKINALPHA_HEADER);
 ENDCLASS(XonoticProfileTab)
 entity makeXonoticProfileTab();
index 0b06c30d6a41e236db274c220dbf7906b675801d..6e8c9fea9a9748d7346f6969f4be0ace5b4fe635 100644 (file)
@@ -3,11 +3,11 @@
 #include "dialog.qh"
 CLASS(XonoticQuitDialog, XonoticDialog)
        METHOD(XonoticQuitDialog, fill, void(entity));
-       ATTRIB(XonoticQuitDialog, title, string, _("Quit"))
-       ATTRIB(XonoticQuitDialog, tooltip, string, _("Quit the game"))
-       ATTRIB(XonoticQuitDialog, color, vector, SKINCOLOR_DIALOG_QUIT)
-       ATTRIB(XonoticQuitDialog, intendedWidth, float, 0.5)
-       ATTRIB(XonoticQuitDialog, rows, float, 3)
-       ATTRIB(XonoticQuitDialog, columns, float, 2)
-       ATTRIB(XonoticQuitDialog, name, string, "Quit")
+       ATTRIB(XonoticQuitDialog, title, string, _("Quit"));
+       ATTRIB(XonoticQuitDialog, tooltip, string, _("Quit the game"));
+       ATTRIB(XonoticQuitDialog, color, vector, SKINCOLOR_DIALOG_QUIT);
+       ATTRIB(XonoticQuitDialog, intendedWidth, float, 0.5);
+       ATTRIB(XonoticQuitDialog, rows, float, 3);
+       ATTRIB(XonoticQuitDialog, columns, float, 2);
+       ATTRIB(XonoticQuitDialog, name, string, "Quit");
 ENDCLASS(XonoticQuitDialog)
index ab49ec867d688a694b62df1aaaad1ac545c2b6b2..2ef0d797390a218dcf2e4b6d0c46be595f4cc947 100644 (file)
@@ -3,11 +3,11 @@
 #include "rootdialog.qh"
 CLASS(XonoticSandboxToolsDialog, XonoticRootDialog)
        METHOD(XonoticSandboxToolsDialog, fill, void(entity));
-       ATTRIB(XonoticSandboxToolsDialog, title, string, _("Sandbox Tools"))
-       ATTRIB(XonoticSandboxToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS)
-       ATTRIB(XonoticSandboxToolsDialog, intendedWidth, float, 0.8)
-       ATTRIB(XonoticSandboxToolsDialog, rows, float, 16)
-       ATTRIB(XonoticSandboxToolsDialog, columns, float, 4)
-       ATTRIB(XonoticSandboxToolsDialog, name, string, "SandboxTools")
-       ATTRIB(XonoticSandboxToolsDialog, requiresConnection, float, true)
+       ATTRIB(XonoticSandboxToolsDialog, title, string, _("Sandbox Tools"));
+       ATTRIB(XonoticSandboxToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS);
+       ATTRIB(XonoticSandboxToolsDialog, intendedWidth, float, 0.8);
+       ATTRIB(XonoticSandboxToolsDialog, rows, float, 16);
+       ATTRIB(XonoticSandboxToolsDialog, columns, float, 4);
+       ATTRIB(XonoticSandboxToolsDialog, name, string, "SandboxTools");
+       ATTRIB(XonoticSandboxToolsDialog, requiresConnection, float, true);
 ENDCLASS(XonoticSandboxToolsDialog)
index 6c7eacb7ca42328fb5ff5236a981f78139bc559d..3564a02e87dd5bb9140f60d7216198992cd6969e 100644 (file)
@@ -3,10 +3,10 @@
 #include "dialog.qh"
 CLASS(XonoticSettingsDialog, XonoticDialog)
        METHOD(XonoticSettingsDialog, fill, void(entity));
-       ATTRIB(XonoticSettingsDialog, title, string, _("Settings"))
-       ATTRIB(XonoticSettingsDialog, tooltip, string, _("Change the game settings"))
-       ATTRIB(XonoticSettingsDialog, color, vector, SKINCOLOR_DIALOG_SETTINGS)
-       ATTRIB(XonoticSettingsDialog, intendedWidth, float, 0.96)
-       ATTRIB(XonoticSettingsDialog, rows, float, 18)
-       ATTRIB(XonoticSettingsDialog, columns, float, 6)
+       ATTRIB(XonoticSettingsDialog, title, string, _("Settings"));
+       ATTRIB(XonoticSettingsDialog, tooltip, string, _("Change the game settings"));
+       ATTRIB(XonoticSettingsDialog, color, vector, SKINCOLOR_DIALOG_SETTINGS);
+       ATTRIB(XonoticSettingsDialog, intendedWidth, float, 0.96);
+       ATTRIB(XonoticSettingsDialog, rows, float, 18);
+       ATTRIB(XonoticSettingsDialog, columns, float, 6);
 ENDCLASS(XonoticSettingsDialog)
index a3e8a8c532d8566a65d5a66cd217d6995ec7b0a2..5794a70ccfb8e1852b59dc15b226d24fbdf846e0 100644 (file)
@@ -3,9 +3,9 @@
 #include "tab.qh"
 CLASS(XonoticAudioSettingsTab, XonoticTab)
        METHOD(XonoticAudioSettingsTab, fill, void(entity));
-       ATTRIB(XonoticAudioSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticAudioSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticAudioSettingsTab, columns, float, 6.2)  // added extra .2 for center space
-       ATTRIB(XonoticAudioSettingsTab, hiddenMenuSoundsSlider, entity, NULL)
+       ATTRIB(XonoticAudioSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticAudioSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticAudioSettingsTab, columns, float, 6.2);  // added extra .2 for center space
+       ATTRIB(XonoticAudioSettingsTab, hiddenMenuSoundsSlider, entity);
 ENDCLASS(XonoticAudioSettingsTab)
 entity makeXonoticAudioSettingsTab();
index 97f3854fbba89c73f07321951ffa6b5964a51411..369c68d9132932d434315e3b630098b57807fbfb 100644 (file)
@@ -3,8 +3,8 @@
 #include "tab.qh"
 CLASS(XonoticEffectsSettingsTab, XonoticTab)
        METHOD(XonoticEffectsSettingsTab, fill, void(entity));
-       ATTRIB(XonoticEffectsSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticEffectsSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticEffectsSettingsTab, columns, float, 6.2)  // added extra .2 for center space
+       ATTRIB(XonoticEffectsSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticEffectsSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticEffectsSettingsTab, columns, float, 6.2);  // added extra .2 for center space
 ENDCLASS(XonoticEffectsSettingsTab)
 entity makeXonoticEffectsSettingsTab();
index c415e96732cee4c6c796ae4d8cc64bde5006be3c..b9231642b18e9b016afb5277531a6e15fe1bea84 100644 (file)
@@ -9,19 +9,19 @@ ENDCLASS(SettingSource)
 
 #include "listbox.qh"
 CLASS(XonoticRegisteredSettingsList, XonoticListBox)
-       ATTRIB(XonoticRegisteredSettingsList, alphaBG, float, 0)
-       ATTRIB(XonoticRegisteredSettingsList, itemAbsSize, vector, '0 0 0')
-       ATTRIB(XonoticRegisteredSettingsList, origin, vector, '0 0 0')
-       ATTRIB(XonoticRegisteredSettingsList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticRegisteredSettingsList, realUpperMargin, float, 0)
-       ATTRIB(XonoticRegisteredSettingsList, rowsPerItem, float, 2)
-       ATTRIB(XonoticRegisteredSettingsList, stringFilterBox, entity, NULL)
-       ATTRIB(XonoticRegisteredSettingsList, stringFilter, string, string_null)
-       ATTRIB(XonoticRegisteredSettingsList, typeToSearchString, string, string_null)
-       ATTRIB(XonoticRegisteredSettingsList, typeToSearchTime, float, 0)
-       ATTRIB(XonoticRegisteredSettingsList, source, DataSource, NULL)
-       ATTRIB(XonoticRegisteredSettingsList, onChange, void(entity, entity), func_null)
-       ATTRIB(XonoticRegisteredSettingsList, onChangeEntity, entity, NULL)
+       ATTRIB(XonoticRegisteredSettingsList, alphaBG, float, 0);
+       ATTRIB(XonoticRegisteredSettingsList, itemAbsSize, vector, '0 0 0');
+       ATTRIB(XonoticRegisteredSettingsList, origin, vector, '0 0 0');
+       ATTRIB(XonoticRegisteredSettingsList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticRegisteredSettingsList, realUpperMargin, float, 0);
+       ATTRIB(XonoticRegisteredSettingsList, rowsPerItem, float, 2);
+       ATTRIB(XonoticRegisteredSettingsList, stringFilterBox, entity);
+       ATTRIB(XonoticRegisteredSettingsList, stringFilter, string);
+       ATTRIB(XonoticRegisteredSettingsList, typeToSearchString, string);
+       ATTRIB(XonoticRegisteredSettingsList, typeToSearchTime, float, 0);
+       ATTRIB(XonoticRegisteredSettingsList, source, DataSource);
+       ATTRIB(XonoticRegisteredSettingsList, onChange, void(entity, entity));
+       ATTRIB(XonoticRegisteredSettingsList, onChangeEntity, entity);
        METHOD(XonoticRegisteredSettingsList, focusedItemChangeNotify, void(entity));
        METHOD(XonoticRegisteredSettingsList, drawListBoxItem, void(entity this, int i, vector absSize, bool isSelected, bool isFocused));
        METHOD(XonoticRegisteredSettingsList, focusedItemChangeNotify, void(entity this));
@@ -33,13 +33,13 @@ ENDCLASS(XonoticRegisteredSettingsList)
 
 #include "tab.qh"
 CLASS(XonoticGameSettingsTab, XonoticTab)
-       ATTRIB(XonoticGameSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameSettingsTab, columns, float, 6.5)
-       ATTRIB(XonoticGameSettingsTab, source, DataSource, NEW(SettingSource))
-       ATTRIB(XonoticGameSettingsTab, topicList, entity, NEW(XonoticRegisteredSettingsList, this.source))
-       ATTRIB(XonoticGameSettingsTab, currentPanel, entity, NEW(XonoticTab))
-       ATTRIB(XonoticGameSettingsTab, currentItem, entity, NULL)
+       ATTRIB(XonoticGameSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameSettingsTab, columns, float, 6.5);
+       ATTRIB(XonoticGameSettingsTab, source, DataSource, NEW(SettingSource));
+       ATTRIB(XonoticGameSettingsTab, topicList, entity, NEW(XonoticRegisteredSettingsList, this.source));
+       ATTRIB(XonoticGameSettingsTab, currentPanel, entity, NEW(XonoticTab));
+       ATTRIB(XonoticGameSettingsTab, currentItem, entity);
        METHOD(XonoticGameSettingsTab, topicChangeNotify, void(entity, entity this));
        METHOD(XonoticGameSettingsTab, fill, void(entity this));
        INIT(XonoticGameSettingsTab)
index 3e1222056143917af19b0d8f2c1299407a639260..33576a3ec0a3ffdd0a5bd5ce7f59f4b07afb51fa 100644 (file)
@@ -4,10 +4,10 @@
 CLASS(XonoticGameCrosshairSettingsTab, XonoticTab)
        METHOD(XonoticGameCrosshairSettingsTab, fill, void(entity));
        METHOD(XonoticGameCrosshairSettingsTab, showNotify, void(entity));
-       ATTRIB(XonoticGameCrosshairSettingsTab, title, string, _("Crosshair"))
-       ATTRIB(XonoticGameCrosshairSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameCrosshairSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameCrosshairSettingsTab, columns, float, 6.2)
+       ATTRIB(XonoticGameCrosshairSettingsTab, title, string, _("Crosshair"));
+       ATTRIB(XonoticGameCrosshairSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameCrosshairSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameCrosshairSettingsTab, columns, float, 6.2);
 ENDCLASS(XonoticGameCrosshairSettingsTab)
 entity makeXonoticGameCrosshairSettingsTab();
 
index 6525e2779c5139f1e80b278eeff797dd85d09976..e16693675b91cafa32da4601b4436a30e0698626 100644 (file)
@@ -43,20 +43,18 @@ void XonoticGameHUDSettingsTab_fill(entity me)
                me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Scoreboard")));
        me.TR(me);
        me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Alpha:")));
-       me.TD(me, 1, 2, e = makeXonoticSlider(0, 1, 0.05, "scoreboard_alpha_bg"));
+       me.TD(me, 1, 2, e = makeXonoticSlider(0, 1, 0.05, "hud_panel_scoreboard_bg_alpha"));
        me.TR(me);
                me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Fading speed:")));
                me.TD(me, 1, 2, e = makeXonoticScoreboardFadeTimeSlider());
        me.TR(me);
-               me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Side padding:")));
-               me.TD(me, 1, 2, e = makeXonoticSlider(0.05, 0.3, 0.01, "scoreboard_offset_left"));
-                       makeMulti(e, "scoreboard_offset_right");
+               me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "hud_panel_scoreboard_table_highlight", _("Enable rows / columns highlighting")));
 
        me.TR(me);
        //me.TR(me);
-               me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "scoreboard_respawntime_decimals", _("Show decimals in respawn countdown")));
+               me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "hud_panel_scoreboard_respawntime_decimals", _("Show decimals in respawn countdown")));
        me.TR(me);
-               me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "scoreboard_accuracy", _("Show accuracy underneath scoreboard")));
+               me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "hud_panel_scoreboard_accuracy", _("Show accuracy underneath scoreboard")));
 
        me.TR(me);
        me.TR(me);
index 1db5890be8d0e013ea6f086e206cf8d482f62129..a9851dc63414255e95a64b301797ee9c8d9c0462 100644 (file)
@@ -4,10 +4,10 @@
 CLASS(XonoticGameHUDSettingsTab, XonoticTab)
        METHOD(XonoticGameHUDSettingsTab, fill, void(entity));
        METHOD(XonoticGameHUDSettingsTab, showNotify, void(entity));
-       ATTRIB(XonoticGameHUDSettingsTab, title, string, _("HUD"))
-       ATTRIB(XonoticGameHUDSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameHUDSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameHUDSettingsTab, columns, float, 6.2)
+       ATTRIB(XonoticGameHUDSettingsTab, title, string, _("HUD"));
+       ATTRIB(XonoticGameHUDSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameHUDSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameHUDSettingsTab, columns, float, 6.2);
 ENDCLASS(XonoticGameHUDSettingsTab)
 entity makeXonoticGameHUDSettingsTab();
 void HUDSetup_Start(entity me, entity btn);
index 6e8421eecd0877d444742cc99af40cd49701713f..bef13432ecf44b87b5fbfda5e22d4f274fd60272 100644 (file)
@@ -3,9 +3,9 @@
 #include "dialog.qh"
 CLASS(XonoticHUDConfirmDialog, XonoticDialog)
        METHOD(XonoticHUDConfirmDialog, fill, void(entity));
-       ATTRIB(XonoticHUDConfirmDialog, title, string, _("Enter HUD editor"))
-       ATTRIB(XonoticHUDConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM)
-       ATTRIB(XonoticHUDConfirmDialog, intendedWidth, float, 0.5)
-       ATTRIB(XonoticHUDConfirmDialog, rows, float, 4)
-       ATTRIB(XonoticHUDConfirmDialog, columns, float, 2)
+       ATTRIB(XonoticHUDConfirmDialog, title, string, _("Enter HUD editor"));
+       ATTRIB(XonoticHUDConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM);
+       ATTRIB(XonoticHUDConfirmDialog, intendedWidth, float, 0.5);
+       ATTRIB(XonoticHUDConfirmDialog, rows, float, 4);
+       ATTRIB(XonoticHUDConfirmDialog, columns, float, 2);
 ENDCLASS(XonoticHUDConfirmDialog)
index 0ea55f637e86e783f5853f4124b3888f616d12a9..dc28dc9ba62458bf820fd01cce0626b601a3dc33 100644 (file)
@@ -56,7 +56,7 @@ void XonoticGameMessageSettingsTab_fill(entity me)
                        setDependentAND(e, "notification_show_sprees", 1, 1, "notification_show_sprees_info", 1, 3);
        me.TR(me);
                me.TD(me, 1, 3, e = makeXonoticCheckBoxEx_T(2, 1, "notification_CHOICE_FRAG", _("Add extra frag information to centerprint when available"), "-"));
-                       makeMulti(e, "notification_CHOICE_FRAGGED notification_CHOICE_TYPEFRAG notification_CHOICE_TYPEFRAGGED");
+                       makeMulti(e, "notification_CHOICE_FRAGGED notification_CHOICE_TYPEFRAG notification_CHOICE_TYPEFRAGGED notification_CHOICE_FRAG_FIRE notification_CHOICE_FRAGGED_FIRE notification_CHOICE_FRAG_FREEZE notification_CHOICE_FRAGGED_FREEZE");
                        e.sendCvars = true;
        me.TR(me);
                me.TD(me, 1, 3, e = makeXonoticCheckBox_T(0, "notification_show_location", _("Add frag location to death messages when available"), "-"));
index 56d13bcb960e4d6dfe8011f2119358e28be9c9b6..28a7698626368175b407ae3b2e2accc7c26084e8 100644 (file)
@@ -4,11 +4,11 @@
 CLASS(XonoticGameMessageSettingsTab, XonoticTab)
        METHOD(XonoticGameMessageSettingsTab, fill, void(entity));
        METHOD(XonoticGameMessageSettingsTab, showNotify, void(entity));
-       ATTRIB(XonoticGameMessageSettingsTab, title, string, _("Messages"))
-       ATTRIB(XonoticGameMessageSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameMessageSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameMessageSettingsTab, columns, float, 6)
-       ATTRIB(XonoticGameMessageSettingsTab, weaponsList, entity, NULL)
+       ATTRIB(XonoticGameMessageSettingsTab, title, string, _("Messages"));
+       ATTRIB(XonoticGameMessageSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameMessageSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameMessageSettingsTab, columns, float, 6);
+       ATTRIB(XonoticGameMessageSettingsTab, weaponsList, entity);
 ENDCLASS(XonoticGameMessageSettingsTab)
 entity makeXonoticGameMessageSettingsTab();
 #include "../gamesettings.qh"
index f0f95d74b95963fb540c5e330a538dc891f58199..b57d7cae8a8f536a643b67d25c1c7275a61801b6 100644 (file)
@@ -50,7 +50,12 @@ void XonoticGameModelSettingsTab_fill(entity me)
        me.TR(me);
                me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_forceplayermodels", _("Force player models to mine")));
        me.TR(me);
-               me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_forceplayercolors", _("Force player colors to mine")));
+               me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Force player colors to mine")));
+               me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_forceplayercolors"));
+                       e.addValue(e, _("Never"), "0");
+                       e.addValue(e, _("In non teamplay modes only"), "1");
+                       e.addValue(e, _("Always"), "2");
+                       e.configureXonoticTextSliderValues(e);
        me.TR(me);
                me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Body fading:")));
                me.TD(me, 1, 2, e = makeXonoticSlider(0, 2, 0.2, "cl_deathglow"));
index 8dd6eaecb321cbb50f490e9de0c7b25d6c4f0ada..c3caabee724b3f4a4c7bc620a0e09f1e7f55d93b 100644 (file)
@@ -4,11 +4,11 @@
 CLASS(XonoticGameModelSettingsTab, XonoticTab)
        METHOD(XonoticGameModelSettingsTab, fill, void(entity));
        METHOD(XonoticGameModelSettingsTab, showNotify, void(entity));
-       ATTRIB(XonoticGameModelSettingsTab, title, string, _("Models"))
-       ATTRIB(XonoticGameModelSettingsTab, titleTooltip, string, _("Customize how players and items are displayed in game"))
-       ATTRIB(XonoticGameModelSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameModelSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameModelSettingsTab, columns, float, 5)
+       ATTRIB(XonoticGameModelSettingsTab, title, string, _("Models"));
+       ATTRIB(XonoticGameModelSettingsTab, titleTooltip, string, _("Customize how players and items are displayed in game"));
+       ATTRIB(XonoticGameModelSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameModelSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameModelSettingsTab, columns, float, 5);
 ENDCLASS(XonoticGameModelSettingsTab)
 entity makeXonoticGameModelSettingsTab();
 #include "../gamesettings.qh"
index 7ec4c79366b9ffcb2cddad2f29b49a44c83a9a63..f9d6001199b740f6555230112c19fd616fc22971 100644 (file)
@@ -4,10 +4,10 @@
 CLASS(XonoticGameViewSettingsTab, XonoticTab)
        METHOD(XonoticGameViewSettingsTab, fill, void(entity));
        METHOD(XonoticGameViewSettingsTab, showNotify, void(entity));
-       ATTRIB(XonoticGameViewSettingsTab, title, string, _("View"))
-       ATTRIB(XonoticGameViewSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameViewSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameViewSettingsTab, columns, float, 6.2)
+       ATTRIB(XonoticGameViewSettingsTab, title, string, _("View"));
+       ATTRIB(XonoticGameViewSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameViewSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameViewSettingsTab, columns, float, 6.2);
 ENDCLASS(XonoticGameViewSettingsTab)
 entity makeXonoticGameViewSettingsTab();
 #include "../gamesettings.qh"
index 73ee66fb27e42138e926ffeb15127130a7000242..ded41a5abdeb25963cccdef129ba591642df879a 100644 (file)
@@ -4,11 +4,11 @@
 CLASS(XonoticGameWeaponsSettingsTab, XonoticTab)
        METHOD(XonoticGameWeaponsSettingsTab, fill, void(entity));
        METHOD(XonoticGameWeaponsSettingsTab, showNotify, void(entity));
-       ATTRIB(XonoticGameWeaponsSettingsTab, title, string, _("Weapons"))
-       ATTRIB(XonoticGameWeaponsSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticGameWeaponsSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticGameWeaponsSettingsTab, columns, float, 6)
-       ATTRIB(XonoticGameWeaponsSettingsTab, weaponsList, entity, NULL)
+       ATTRIB(XonoticGameWeaponsSettingsTab, title, string, _("Weapons"));
+       ATTRIB(XonoticGameWeaponsSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticGameWeaponsSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticGameWeaponsSettingsTab, columns, float, 6);
+       ATTRIB(XonoticGameWeaponsSettingsTab, weaponsList, entity);
 ENDCLASS(XonoticGameWeaponsSettingsTab)
 entity makeXonoticGameWeaponsSettingsTab();
 #include "../gamesettings.qh"
index f8e6feda6c9489f080da7db1d3451b5dcdf6b432..040908298976dd5784f3b7708ba6af6ae2d9e530 100644 (file)
@@ -3,8 +3,8 @@
 #include "tab.qh"
 CLASS(XonoticInputSettingsTab, XonoticTab)
        METHOD(XonoticInputSettingsTab, fill, void(entity));
-       ATTRIB(XonoticInputSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticInputSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticInputSettingsTab, columns, float, 6.2)  // added extra .2 for center space
+       ATTRIB(XonoticInputSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticInputSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticInputSettingsTab, columns, float, 6.2);  // added extra .2 for center space
 ENDCLASS(XonoticInputSettingsTab)
 entity makeXonoticInputSettingsTab();
index 5ff3a2e0bcc2edbce1936036fbc7c2423cf91223..561a04b32b2326f0a9a9226df550a1b8d2790a5c 100644 (file)
@@ -4,14 +4,14 @@
 CLASS(XonoticUserbindEditDialog, XonoticDialog)
        METHOD(XonoticUserbindEditDialog, loadUserBind, void(entity, string, string, string));
        METHOD(XonoticUserbindEditDialog, fill, void(entity));
-       ATTRIB(XonoticUserbindEditDialog, title, string, _("User defined key bind"))
-       ATTRIB(XonoticUserbindEditDialog, color, vector, SKINCOLOR_DIALOG_USERBIND)
-       ATTRIB(XonoticUserbindEditDialog, intendedWidth, float, 0.7)
-       ATTRIB(XonoticUserbindEditDialog, rows, float, 4)
-       ATTRIB(XonoticUserbindEditDialog, columns, float, 3)
-       ATTRIB(XonoticUserbindEditDialog, keybindBox, entity, NULL)
+       ATTRIB(XonoticUserbindEditDialog, title, string, _("User defined key bind"));
+       ATTRIB(XonoticUserbindEditDialog, color, vector, SKINCOLOR_DIALOG_USERBIND);
+       ATTRIB(XonoticUserbindEditDialog, intendedWidth, float, 0.7);
+       ATTRIB(XonoticUserbindEditDialog, rows, float, 4);
+       ATTRIB(XonoticUserbindEditDialog, columns, float, 3);
+       ATTRIB(XonoticUserbindEditDialog, keybindBox, entity);
 
-       ATTRIB(XonoticUserbindEditDialog, nameBox, entity, NULL)
-       ATTRIB(XonoticUserbindEditDialog, commandPressBox, entity, NULL)
-       ATTRIB(XonoticUserbindEditDialog, commandReleaseBox, entity, NULL)
+       ATTRIB(XonoticUserbindEditDialog, nameBox, entity);
+       ATTRIB(XonoticUserbindEditDialog, commandPressBox, entity);
+       ATTRIB(XonoticUserbindEditDialog, commandReleaseBox, entity);
 ENDCLASS(XonoticUserbindEditDialog)
index d951275db15a8c1c21cb6953d6ddb65024414093..8f98df58164e2fbd9933d17492ee6ea2f5ef4c05 100644 (file)
@@ -9,6 +9,8 @@
 #include "mainwindow.qh"
 
 #define ADDVALUE_FPS(i) e.addValue(e, strzone(sprintf(_("%d fps"), i)), #i)
+#define ADDVALUE_SPEED_KB(i) e.addValue(e, strzone(sprintf(_("%d kb/s"), i)), #i)
+#define ADDVALUE_SPEED_MB(i, j) e.addValue(e, strzone(sprintf(_("%d MB/s"), i)), #j)
 entity makeXonoticMiscSettingsTab()
 {
        entity me;
@@ -51,9 +53,16 @@ void XonoticMiscSettingsTab_fill(entity me)
                me.TD(me, 1, 2, e = makeXonoticSlider_T(1, 5, 1, "cl_curl_maxdownloads",
                        _("Maximum number of concurrent HTTP/FTP downloads")));
        me.TR(me);
-               me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Speed (kB/s):")));
-               me.TD(me, 1, 2, e = makeXonoticSlider_T(10, 2000, 50, "cl_curl_maxspeed",
-                       _("Maximum download speed")));
+               me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Download speed:")));
+               me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_curl_maxspeed"));
+               ADDVALUE_SPEED_KB(50);
+               ADDVALUE_SPEED_KB(100);
+               ADDVALUE_SPEED_KB(300);
+               ADDVALUE_SPEED_KB(500);
+               ADDVALUE_SPEED_MB(1, 1000);
+               ADDVALUE_SPEED_MB(2, 2000);
+               e.addValue(e, strzone(_("Unlimited")), "0");
+               e.configureXonoticTextSliderValues(e);
        me.TR(me);
                if(cvar("developer"))
                {
@@ -77,16 +86,15 @@ void XonoticMiscSettingsTab_fill(entity me)
        me.TR(me);
                me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Maximum:")));
                me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_maxfps"));
-                       ADDVALUE_FPS(5);
-                       ADDVALUE_FPS(10);
-                       ADDVALUE_FPS(20);
                        ADDVALUE_FPS(30);
                        ADDVALUE_FPS(40);
                        ADDVALUE_FPS(50);
                        ADDVALUE_FPS(60);
                        ADDVALUE_FPS(70);
+                       ADDVALUE_FPS(80);
                        ADDVALUE_FPS(100);
                        ADDVALUE_FPS(125);
+                       ADDVALUE_FPS(150);
                        ADDVALUE_FPS(200);
                        e.addValue(e, ZCTX(_("MAXFPS^Unlimited")), "0");
                        e.configureXonoticTextSliderValues(e);
@@ -98,8 +106,10 @@ void XonoticMiscSettingsTab_fill(entity me)
                        ADDVALUE_FPS(40);
                        ADDVALUE_FPS(50);
                        ADDVALUE_FPS(60);
+                       ADDVALUE_FPS(80);
                        ADDVALUE_FPS(100);
                        ADDVALUE_FPS(125);
+                       ADDVALUE_FPS(150);
                        ADDVALUE_FPS(200);
                        e.configureXonoticTextSliderValues(e);
        me.TR(me);
index f3f367e686626a8b8b9f2dd30ff856d5126e41d7..a2a86b7e1fb956adbbb750f090168cbe8df3650d 100644 (file)
@@ -3,8 +3,8 @@
 #include "tab.qh"
 CLASS(XonoticMiscSettingsTab, XonoticTab)
        METHOD(XonoticMiscSettingsTab, fill, void(entity));
-       ATTRIB(XonoticMiscSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticMiscSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticMiscSettingsTab, columns, float, 6.2)
+       ATTRIB(XonoticMiscSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticMiscSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticMiscSettingsTab, columns, float, 6.2);
 ENDCLASS(XonoticMiscSettingsTab)
 entity makeXonoticMiscSettingsTab();
index f5011f77170e5a14a02421b83ab34425df3d7d12..4fddf65867ce4a9f808eac3fba2cdc912835fec0 100644 (file)
@@ -4,9 +4,9 @@
 CLASS(XonoticCvarsDialog, XonoticDialog)
        METHOD(XonoticCvarsDialog, fill, void(entity));
        METHOD(XonoticCvarsDialog, showNotify, void(entity));
-       ATTRIB(XonoticCvarsDialog, title, string, _("Advanced settings"))
-       ATTRIB(XonoticCvarsDialog, color, vector, SKINCOLOR_DIALOG_CVARS)
-       ATTRIB(XonoticCvarsDialog, intendedWidth, float, 0.8)
-       ATTRIB(XonoticCvarsDialog, rows, float, 24)
-       ATTRIB(XonoticCvarsDialog, columns, float, 6)
+       ATTRIB(XonoticCvarsDialog, title, string, _("Advanced settings"));
+       ATTRIB(XonoticCvarsDialog, color, vector, SKINCOLOR_DIALOG_CVARS);
+       ATTRIB(XonoticCvarsDialog, intendedWidth, float, 0.8);
+       ATTRIB(XonoticCvarsDialog, rows, float, 24);
+       ATTRIB(XonoticCvarsDialog, columns, float, 6);
 ENDCLASS(XonoticCvarsDialog)
index 1cbc7773ab9091016ba1a978bf03f9545ce36164..f1d02ff90917365cb050a5d84b8fcb93f502e2bf 100644 (file)
@@ -3,10 +3,10 @@
 #include "dialog.qh"
 CLASS(XonoticResetDialog, XonoticDialog)
        METHOD(XonoticResetDialog, fill, void(entity));
-       ATTRIB(XonoticResetDialog, title, string, _("Factory reset"))
-       ATTRIB(XonoticResetDialog, color, vector, SKINCOLOR_DIALOG_QUIT)
-       ATTRIB(XonoticResetDialog, intendedWidth, float, 0.5)
-       ATTRIB(XonoticResetDialog, rows, float, 4)
-       ATTRIB(XonoticResetDialog, columns, float, 2)
-       ATTRIB(XonoticResetDialog, name, string, "Factory reset")
+       ATTRIB(XonoticResetDialog, title, string, _("Factory reset"));
+       ATTRIB(XonoticResetDialog, color, vector, SKINCOLOR_DIALOG_QUIT);
+       ATTRIB(XonoticResetDialog, intendedWidth, float, 0.5);
+       ATTRIB(XonoticResetDialog, rows, float, 4);
+       ATTRIB(XonoticResetDialog, columns, float, 2);
+       ATTRIB(XonoticResetDialog, name, string, "Factory reset");
 ENDCLASS(XonoticResetDialog)
index 3a5ccc3b652a79a7bc9cac35f5b0b03a40a2382b..22f84d0f0b7c024b5247f17a28c46efa9848bfab 100644 (file)
@@ -3,8 +3,8 @@
 #include "tab.qh"
 CLASS(XonoticUserSettingsTab, XonoticTab)
        METHOD(XonoticUserSettingsTab, fill, void(entity));
-       ATTRIB(XonoticUserSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticUserSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticUserSettingsTab, columns, float, 6)
+       ATTRIB(XonoticUserSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticUserSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticUserSettingsTab, columns, float, 6);
 ENDCLASS(XonoticUserSettingsTab)
 entity makeXonoticUserSettingsTab();
index 6028a441cae0b65c5acbc3e2f08f033f0a3db944..ba5c8a5d1c0bc0f129e006da2a5f06ce5e23aa8f 100644 (file)
@@ -3,9 +3,9 @@
 #include "dialog.qh"
 CLASS(XonoticLanguageWarningDialog, XonoticDialog)
        METHOD(XonoticLanguageWarningDialog, fill, void(entity));
-       ATTRIB(XonoticLanguageWarningDialog, title, string, _("Warning"))
-       ATTRIB(XonoticLanguageWarningDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM)
-       ATTRIB(XonoticLanguageWarningDialog, intendedWidth, float, 0.6)
-       ATTRIB(XonoticLanguageWarningDialog, rows, float, 5)
-       ATTRIB(XonoticLanguageWarningDialog, columns, float, 4)
+       ATTRIB(XonoticLanguageWarningDialog, title, string, _("Warning"));
+       ATTRIB(XonoticLanguageWarningDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM);
+       ATTRIB(XonoticLanguageWarningDialog, intendedWidth, float, 0.6);
+       ATTRIB(XonoticLanguageWarningDialog, rows, float, 5);
+       ATTRIB(XonoticLanguageWarningDialog, columns, float, 4);
 ENDCLASS(XonoticLanguageWarningDialog)
index 9320a6ebf2101603fe79e478d3b1abf347f8fe7e..8ee51fde97405b4a424ff79f699c3fd6ded25684 100644 (file)
@@ -46,6 +46,7 @@ void XonoticVideoSettingsTab_fill(entity me)
                        e.addValue(e, ZCTX(_("SZ^Gigantic")), "0.75");
                        e.addValue(e, ZCTX(_("SZ^Colossal")), "1");
                        e.configureXonoticTextSliderValues(e);
+                       e.applyButton = videoApplyButton;
        me.TR(me);
                me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Color depth:")));
                me.TD(me, 1, 2, e = makeXonoticTextSlider_T("vid_bitsperpixel",
index 0abe322c38da0a3ccf3b47fd0f04345f198a08ee..61d8a368e0e67edd06003e6b6966fb9525f298fa 100644 (file)
@@ -3,9 +3,9 @@
 #include "tab.qh"
 CLASS(XonoticVideoSettingsTab, XonoticTab)
        METHOD(XonoticVideoSettingsTab, fill, void(entity));
-       ATTRIB(XonoticVideoSettingsTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticVideoSettingsTab, rows, float, 15.5)
-       ATTRIB(XonoticVideoSettingsTab, columns, float, 6.2)  // added extra .2 for center space
-       ATTRIB(XonoticVideoSettingsTab, name, string, "videosettings")
+       ATTRIB(XonoticVideoSettingsTab, intendedWidth, float, 0.9);
+       ATTRIB(XonoticVideoSettingsTab, rows, float, 15.5);
+       ATTRIB(XonoticVideoSettingsTab, columns, float, 6.2);  // added extra .2 for center space
+       ATTRIB(XonoticVideoSettingsTab, name, string, "videosettings");
 ENDCLASS(XonoticVideoSettingsTab)
 entity makeXonoticVideoSettingsTab();
index b3dabaf6ce160ee59c3852bec89c8492b4b04e6f..c7691fbba4e16ba81eb483b8c0f748c9e6d4f2ba 100644 (file)
@@ -3,11 +3,11 @@
 #include "dialog.qh"
 CLASS(XonoticSingleplayerDialog, XonoticDialog)
        METHOD(XonoticSingleplayerDialog, fill, void(entity));
-       ATTRIB(XonoticSingleplayerDialog, title, string, _("Singleplayer"))
-       ATTRIB(XonoticSingleplayerDialog, tooltip, string, _("Play the singleplayer campaign or instant action matches against bots"))
-       ATTRIB(XonoticSingleplayerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER)
-       ATTRIB(XonoticSingleplayerDialog, intendedWidth, float, 0.80)
-       ATTRIB(XonoticSingleplayerDialog, rows, float, 24)
-       ATTRIB(XonoticSingleplayerDialog, columns, float, 5)
-       ATTRIB(XonoticSingleplayerDialog, campaignBox, entity, NULL)
+       ATTRIB(XonoticSingleplayerDialog, title, string, _("Singleplayer"));
+       ATTRIB(XonoticSingleplayerDialog, tooltip, string, _("Play the singleplayer campaign or instant action matches against bots"));
+       ATTRIB(XonoticSingleplayerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER);
+       ATTRIB(XonoticSingleplayerDialog, intendedWidth, float, 0.80);
+       ATTRIB(XonoticSingleplayerDialog, rows, float, 24);
+       ATTRIB(XonoticSingleplayerDialog, columns, float, 5);
+       ATTRIB(XonoticSingleplayerDialog, campaignBox, entity);
 ENDCLASS(XonoticSingleplayerDialog)
index 1de958b8b7ebe6103ad72e6bd11d3feffb45b7b8..6a6b2d37bf7988956fb66de009dec5b79550a943 100644 (file)
@@ -4,9 +4,9 @@
 CLASS(XonoticWinnerDialog, XonoticDialog)
        METHOD(XonoticWinnerDialog, fill, void(entity));
        METHOD(XonoticWinnerDialog, focusEnter, void(entity));
-       ATTRIB(XonoticWinnerDialog, title, string, _("Winner"))
-       ATTRIB(XonoticWinnerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER)
-       ATTRIB(XonoticWinnerDialog, intendedWidth, float, 0.32)
-       ATTRIB(XonoticWinnerDialog, rows, float, 12)
-       ATTRIB(XonoticWinnerDialog, columns, float, 3)
+       ATTRIB(XonoticWinnerDialog, title, string, _("Winner"));
+       ATTRIB(XonoticWinnerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER);
+       ATTRIB(XonoticWinnerDialog, intendedWidth, float, 0.32);
+       ATTRIB(XonoticWinnerDialog, rows, float, 12);
+       ATTRIB(XonoticWinnerDialog, columns, float, 3);
 ENDCLASS(XonoticWinnerDialog)
index b4056bb3b1947c936566e6120ece605d562eff4b..0bb0c306e388e35e8f30d5069957e631277db572 100644 (file)
@@ -4,15 +4,15 @@
 CLASS(XonoticTeamSelectDialog, XonoticRootDialog)
        METHOD(XonoticTeamSelectDialog, fill, void(entity));
        METHOD(XonoticTeamSelectDialog, showNotify, void(entity));
-       ATTRIB(XonoticTeamSelectDialog, title, string, _("Team Selection"))
-       ATTRIB(XonoticTeamSelectDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
-       ATTRIB(XonoticTeamSelectDialog, intendedWidth, float, 0.4)
-       ATTRIB(XonoticTeamSelectDialog, rows, float, 5)
-       ATTRIB(XonoticTeamSelectDialog, columns, float, 4)
-       ATTRIB(XonoticTeamSelectDialog, name, string, "TeamSelect")
-       ATTRIB(XonoticTeamSelectDialog, team1, entity, NULL)
-       ATTRIB(XonoticTeamSelectDialog, team2, entity, NULL)
-       ATTRIB(XonoticTeamSelectDialog, team3, entity, NULL)
-       ATTRIB(XonoticTeamSelectDialog, team4, entity, NULL)
-       ATTRIB(XonoticTeamSelectDialog, requiresConnection, float, true)
+       ATTRIB(XonoticTeamSelectDialog, title, string, _("Team Selection"));
+       ATTRIB(XonoticTeamSelectDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticTeamSelectDialog, intendedWidth, float, 0.4);
+       ATTRIB(XonoticTeamSelectDialog, rows, float, 5);
+       ATTRIB(XonoticTeamSelectDialog, columns, float, 4);
+       ATTRIB(XonoticTeamSelectDialog, name, string, "TeamSelect");
+       ATTRIB(XonoticTeamSelectDialog, team1, entity);
+       ATTRIB(XonoticTeamSelectDialog, team2, entity);
+       ATTRIB(XonoticTeamSelectDialog, team3, entity);
+       ATTRIB(XonoticTeamSelectDialog, team4, entity);
+       ATTRIB(XonoticTeamSelectDialog, requiresConnection, float, true);
 ENDCLASS(XonoticTeamSelectDialog)
diff --git a/qcsrc/menu/xonotic/dialog_uid2name.qc b/qcsrc/menu/xonotic/dialog_uid2name.qc
new file mode 100644 (file)
index 0000000..1cd77b0
--- /dev/null
@@ -0,0 +1,18 @@
+#include "dialog_uid2name.qh"
+
+#include "textlabel.qh"
+#include "commandbutton.qh"
+
+void XonoticUid2NameDialog_fill(entity me)
+{
+       entity e;
+       me.TR(me);
+               me.TD(me, 1, 2, makeXonoticTextLabel(0.5, _("Allow player statistics to use your nickname?")));
+       me.TR(me);
+               me.TD(me, 1, 2, makeXonoticTextLabel(0.5, _("Answering \"No\" you will appear as \"Anonymous player\"")));
+       me.TR(me);
+       me.TR(me);
+               me.TD(me, 1, 1, e = makeXonoticCommandButton(_("Yes"), '0 0 0', "vyes; setreport cl_allow_uid2name 1", COMMANDBUTTON_CLOSE));
+                       e.preferredFocusPriority = 1;
+               me.TD(me, 1, 1, e = makeXonoticCommandButton(_("No"), '0 0 0', "vno; setreport cl_allow_uid2name 0", COMMANDBUTTON_CLOSE));
+}
diff --git a/qcsrc/menu/xonotic/dialog_uid2name.qh b/qcsrc/menu/xonotic/dialog_uid2name.qh
new file mode 100644 (file)
index 0000000..187d080
--- /dev/null
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "rootdialog.qh"
+CLASS(XonoticUid2NameDialog, XonoticRootDialog)
+       METHOD(XonoticUid2NameDialog, fill, void(entity));
+       ATTRIB(XonoticUid2NameDialog, title, string, "");
+       ATTRIB(XonoticUid2NameDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT);
+       ATTRIB(XonoticUid2NameDialog, intendedWidth, float, 0.5);
+       ATTRIB(XonoticUid2NameDialog, rows, float, 4);
+       ATTRIB(XonoticUid2NameDialog, columns, float, 2);
+       ATTRIB(XonoticUid2NameDialog, name, string, "Uid2Name");
+       ATTRIB(XonoticUid2NameDialog, closable, float, 0);
+ENDCLASS(XonoticUid2NameDialog)
index 9ee031dc783e6196dca7df6592ba6ab5f63fb6ea..a08e4dbbc5fa13dc4eb51680a1bb551a25780fc0 100644 (file)
@@ -17,7 +17,7 @@ void XonoticGametypeList_configureXonoticGametypeList(entity me)
 
        if(SKINBOOL_GAMETYPELIST_ICON_BLUR)
        {
-               for(int i = 0; i < GameType_GetCount(); ++i)
+               for(int i = 0; i < GameType_GetTotalCount(); ++i)
                        draw_PreloadPictureWithFlags(GameType_GetIcon(i), PRECACHE_PIC_MIPMAP);
        }
 
@@ -30,8 +30,7 @@ void XonoticGametypeList_setSelected(entity me, float i)
 }
 void XonoticGametypeList_loadCvars(entity me)
 {
-       float t;
-       t = MapInfo_CurrentGametype();
+       Gametype t = MapInfo_CurrentGametype();
        float i;
        for(i = 0; i < GameType_GetCount(); ++i)
                if(t == GameType_GetID(i))
@@ -49,7 +48,7 @@ void XonoticGametypeList_loadCvars(entity me)
 }
 void XonoticGametypeList_saveCvars(entity me)
 {
-       int t = GameType_GetID(me.selectedItem);
+       Gametype t = GameType_GetID(me.selectedItem);
        if (t == MapInfo_CurrentGametype()) {
                return;
        }
@@ -59,6 +58,15 @@ void XonoticGametypeList_saveCvars(entity me)
                owner.gameTypeChangeNotify(owner);
        }
 }
+void XonoticGametypeList_draw(entity me)
+{
+       if(me.nItems != GameType_GetCount())
+       {
+               me.nItems = GameType_GetCount();
+               me.setSelected(me, 0);
+       }
+       SUPER(XonoticGametypeList).draw(me);
+}
 void XonoticGametypeList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
 {
        string s1, s2;
index 95eceaa09b350abaa6c337986d5e37c41dd1631c..bbc50aba4357baed2496662563891d07a3eb2136 100644 (file)
@@ -3,7 +3,8 @@
 #include "listbox.qh"
 CLASS(XonoticGametypeList, XonoticListBox)
        METHOD(XonoticGametypeList, configureXonoticGametypeList, void(entity));
-       ATTRIB(XonoticGametypeList, rowsPerItem, float, 2)
+       ATTRIB(XonoticGametypeList, rowsPerItem, float, 2);
+       METHOD(XonoticGametypeList, draw, void(entity));
        METHOD(XonoticGametypeList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticGametypeList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticGametypeList, setSelected, void(entity, float));
@@ -13,11 +14,11 @@ CLASS(XonoticGametypeList, XonoticListBox)
        METHOD(XonoticGametypeList, clickListBoxItem, void(entity, float, vector));
        METHOD(XonoticGametypeList, focusedItemChangeNotify, void(entity));
 
-       ATTRIB(XonoticGametypeList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticGametypeList, realUpperMargin, float, 0)
-       ATTRIB(XonoticGametypeList, columnIconOrigin, float, 0)
-       ATTRIB(XonoticGametypeList, columnIconSize, float, 0)
-       ATTRIB(XonoticGametypeList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticGametypeList, columnNameSize, float, 0)
+       ATTRIB(XonoticGametypeList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticGametypeList, realUpperMargin, float, 0);
+       ATTRIB(XonoticGametypeList, columnIconOrigin, float, 0);
+       ATTRIB(XonoticGametypeList, columnIconSize, float, 0);
+       ATTRIB(XonoticGametypeList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticGametypeList, columnNameSize, float, 0);
 ENDCLASS(XonoticGametypeList)
 entity makeXonoticGametypeList();
index f5808698cce5885254ff02bb1b4f0536b0d83b72..557f2e165c6c4d823c6ca81798eaf49b95e0e01a 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticHUDSkinList, XonoticListBox)
        METHOD(XonoticHUDSkinList, configureXonoticHUDSkinList, void(entity));
-       ATTRIB(XonoticHUDSkinList, rowsPerItem, float, 1)
+       ATTRIB(XonoticHUDSkinList, rowsPerItem, float, 1);
        METHOD(XonoticHUDSkinList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticHUDSkinList, draw, void(entity));
        METHOD(XonoticHUDSkinList, drawListBoxItem, void(entity, int, vector, bool, bool));
@@ -18,17 +18,17 @@ CLASS(XonoticHUDSkinList, XonoticListBox)
        METHOD(XonoticHUDSkinList, destroy, void(entity));
        METHOD(XonoticHUDSkinList, showNotify, void(entity));
 
-       ATTRIB(XonoticHUDSkinList, listHUDSkin, float, -1)
-       ATTRIB(XonoticHUDSkinList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticHUDSkinList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticHUDSkinList, columnNameSize, float, 0)
-       ATTRIB(XonoticHUDSkinList, realUpperMargin, float, 0)
-       ATTRIB(XonoticHUDSkinList, origin, vector, '0 0 0')
-       ATTRIB(XonoticHUDSkinList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticHUDSkinList, listHUDSkin, float, -1);
+       ATTRIB(XonoticHUDSkinList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticHUDSkinList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticHUDSkinList, columnNameSize, float, 0);
+       ATTRIB(XonoticHUDSkinList, realUpperMargin, float, 0);
+       ATTRIB(XonoticHUDSkinList, origin, vector, '0 0 0');
+       ATTRIB(XonoticHUDSkinList, itemAbsSize, vector, '0 0 0');
 
-       ATTRIB(XonoticHUDSkinList, filterString, string, string_null)
-       ATTRIB(XonoticHUDSkinList, delayedRefreshTime, float, 0)
-       ATTRIB(XonoticHUDSkinList, savedName, string, string_null)
+       ATTRIB(XonoticHUDSkinList, filterString, string);
+       ATTRIB(XonoticHUDSkinList, delayedRefreshTime, float, 0);
+       ATTRIB(XonoticHUDSkinList, savedName, string);
 ENDCLASS(XonoticHUDSkinList)
 entity hudskinlist;
 entity makeXonoticHUDSkinList();
index c09817943b31cbb7748db9a7cf2d0200aee59519..7be855cd9d4667c2f42d05d44561e51e63b14623 100644 (file)
@@ -6,32 +6,32 @@ CLASS(XonoticInputBox, InputBox)
        METHOD(XonoticInputBox, focusLeave, void(entity));
        METHOD(XonoticInputBox, setText, void(entity, string));
        METHOD(XonoticInputBox, keyDown, float(entity, float, float, float));
-       ATTRIB(XonoticInputBox, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticInputBox, image, string, SKINGFX_INPUTBOX)
-       ATTRIB(XonoticInputBox, onChange, void(entity, entity), func_null)
-       ATTRIB(XonoticInputBox, onChangeEntity, entity, NULL)
-       ATTRIB(XonoticInputBox, onEnter, void(entity, entity), func_null)
-       ATTRIB(XonoticInputBox, onEnterEntity, entity, NULL)
-       ATTRIB(XonoticInputBox, marginLeft, float, SKINMARGIN_INPUTBOX_CHARS)
-       ATTRIB(XonoticInputBox, marginRight, float, SKINMARGIN_INPUTBOX_CHARS)
-       ATTRIB(XonoticInputBox, color, vector, SKINCOLOR_INPUTBOX_N)
-       ATTRIB(XonoticInputBox, colorF, vector, SKINCOLOR_INPUTBOX_F)
+       ATTRIB(XonoticInputBox, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticInputBox, image, string, SKINGFX_INPUTBOX);
+       ATTRIB(XonoticInputBox, onChange, void(entity, entity));
+       ATTRIB(XonoticInputBox, onChangeEntity, entity);
+       ATTRIB(XonoticInputBox, onEnter, void(entity, entity));
+       ATTRIB(XonoticInputBox, onEnterEntity, entity);
+       ATTRIB(XonoticInputBox, marginLeft, float, SKINMARGIN_INPUTBOX_CHARS);
+       ATTRIB(XonoticInputBox, marginRight, float, SKINMARGIN_INPUTBOX_CHARS);
+       ATTRIB(XonoticInputBox, color, vector, SKINCOLOR_INPUTBOX_N);
+       ATTRIB(XonoticInputBox, colorF, vector, SKINCOLOR_INPUTBOX_F);
 
-       ATTRIB(XonoticInputBox, alpha, float, SKINALPHA_TEXT)
+       ATTRIB(XonoticInputBox, alpha, float, SKINALPHA_TEXT);
 
        // Clear button attributes
-       ATTRIB(XonoticInputBox, cb_offset, float, SKINOFFSET_CLEARBUTTON)  // bound to range -1, 0
-       ATTRIB(XonoticInputBox, cb_src, string, SKINGFX_CLEARBUTTON)
-       ATTRIB(XonoticInputBox, cb_color, vector, SKINCOLOR_CLEARBUTTON_N)
-       ATTRIB(XonoticInputBox, cb_colorF, vector, SKINCOLOR_CLEARBUTTON_F)
-       ATTRIB(XonoticInputBox, cb_colorC, vector, SKINCOLOR_CLEARBUTTON_C)
+       ATTRIB(XonoticInputBox, cb_offset, float, SKINOFFSET_CLEARBUTTON);  // bound to range -1, 0
+       ATTRIB(XonoticInputBox, cb_src, string, SKINGFX_CLEARBUTTON);
+       ATTRIB(XonoticInputBox, cb_color, vector, SKINCOLOR_CLEARBUTTON_N);
+       ATTRIB(XonoticInputBox, cb_colorF, vector, SKINCOLOR_CLEARBUTTON_F);
+       ATTRIB(XonoticInputBox, cb_colorC, vector, SKINCOLOR_CLEARBUTTON_C);
 
-       ATTRIB(XonoticInputBox, cvarName, string, string_null)
+       ATTRIB(XonoticInputBox, cvarName, string);
        METHOD(XonoticInputBox, loadCvars, void(entity));
        METHOD(XonoticInputBox, saveCvars, void(entity));
-       ATTRIB(XonoticInputBox, sendCvars, float, 0)
+       ATTRIB(XonoticInputBox, sendCvars, float, 0);
 
-       ATTRIB(XonoticInputBox, saveImmediately, float, 0)
+       ATTRIB(XonoticInputBox, saveImmediately, float, 0);
 ENDCLASS(XonoticInputBox)
 entity makeXonoticInputBox_T(float, string, string theTooltip);
 entity makeXonoticInputBox(float, string);
index ca2db83573d02a468f7077cb5474f331979fc00d..4546a62b109503192359c91904ac49bae9b4053c 100644 (file)
@@ -1,6 +1,6 @@
 #include "keybinder.qh"
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 .int flags;
 
 #include "button.qh"
@@ -40,12 +40,13 @@ void Xonotic_KeyBinds_Read()
        KEYBIND_DEF("+fire"                                 , _("primary fire"));
        KEYBIND_DEF("+fire2"                                , _("secondary fire"));
        KEYBIND_DEF(""                                      , "");
-       KEYBIND_DEF(""                                      , _("Weapon switching"));
-       KEYBIND_DEF("weapprev"                              , _("previous"));
-       KEYBIND_DEF("weapnext"                              , _("next"));
-       KEYBIND_DEF("weaplast"                              , _("previously used"));
-       KEYBIND_DEF("weapbest"                              , _("best"));
+       KEYBIND_DEF(""                                      , _("Weapons"));
+       KEYBIND_DEF("weapprev"                              , CTX(_("WEAPON^previous")));
+       KEYBIND_DEF("weapnext"                              , CTX(_("WEAPON^next")));
+       KEYBIND_DEF("weaplast"                              , CTX(_("WEAPON^previously used")));
+       KEYBIND_DEF("weapbest"                              , CTX(_("WEAPON^best")));
        KEYBIND_DEF("reload"                                , _("reload"));
+       KEYBIND_DEF("dropweapon"                            , _("drop weapon / throw nade"));
 
        int i;
 
@@ -59,9 +60,9 @@ void Xonotic_KeyBinds_Read()
        for(int imp = 1; imp <= 9; ++imp)
        {
         string w_list = "";
-               ADD_TO_W_LIST(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_TYPE_OTHER) && !(it.spawnflags & WEP_FLAG_HIDDEN) && !(it.spawnflags & WEP_FLAG_SUPERWEAPON));
-               ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_SUPERWEAPON) && !(it.spawnflags & WEP_TYPE_OTHER) && !(it.spawnflags & WEP_FLAG_HIDDEN));
-               ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_TYPE_OTHER) && !(it.spawnflags & WEP_FLAG_HIDDEN));
+               ADD_TO_W_LIST(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_FLAG_HIDDEN) && !(it.spawnflags & WEP_FLAG_SUPERWEAPON));
+               ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_SUPERWEAPON) && !(it.spawnflags & WEP_FLAG_HIDDEN));
+               ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_FLAG_HIDDEN));
                if(w_list)
                        KEYBIND_DEF(strcat("weapon_group_", itos(imp)), substring(w_list, 0, -4));
                if(imp == 0)
@@ -78,6 +79,8 @@ void Xonotic_KeyBinds_Read()
        KEYBIND_DEF("+showscores"                           , _("show scores"));
        KEYBIND_DEF("screenshot"                            , _("screen shot"));
        KEYBIND_DEF("+hud_panel_radar_maximized"            , _("maximize radar"));
+       KEYBIND_DEF("toggle chase_active"                   , _("3rd person view"));
+       KEYBIND_DEF("spec"                                  , _("enter spectator mode"));
        KEYBIND_DEF(""                                      , "");
        KEYBIND_DEF(""                                      , _("Communicate"));
        KEYBIND_DEF("messagemode"                           , _("public chat"));
@@ -97,12 +100,12 @@ void Xonotic_KeyBinds_Read()
        KEYBIND_DEF("messagemode2"                          , _("team chat"));
        KEYBIND_DEF("team_auto"                             , _("auto-join team"));
        KEYBIND_DEF("menu_showteamselect"                   , _("team menu"));
-       KEYBIND_DEF("menu_showsandboxtools"                 , _("sandbox menu"));
-       KEYBIND_DEF("spec"                                  , _("enter spectator mode"));
-       KEYBIND_DEF("dropweapon"                            , _("drop weapon"));
        KEYBIND_DEF("+use"                                  , _("drop key / drop flag"));
+       KEYBIND_DEF(""                                      , "");
+       KEYBIND_DEF(""                                      , _("Misc"));
+       KEYBIND_DEF("quickmenu"                             , _("quick menu"));
+       KEYBIND_DEF("menu_showsandboxtools"                 , _("sandbox menu"));
        KEYBIND_DEF("+button8"                              , _("drag object"));
-       KEYBIND_DEF("toggle chase_active"                   , _("3rd person view"));
        KEYBIND_DEF(""                                      , "");
        KEYBIND_DEF(""                                      , _("User defined"));
 
index 4d0482dd9dfcbf9e1e0770ebd393a31327853185..764059f768df6d8feffedd929d164fa4d182a9e0 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticKeyBinder, XonoticListBox)
        METHOD(XonoticKeyBinder, configureXonoticKeyBinder, void(entity));
-       ATTRIB(XonoticKeyBinder, rowsPerItem, int, 1)
+       ATTRIB(XonoticKeyBinder, rowsPerItem, int, 1);
        METHOD(XonoticKeyBinder, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticKeyBinder, doubleClickListBoxItem, void(entity, float, vector));
        METHOD(XonoticKeyBinder, resizeNotify, void(entity, vector, vector, vector, vector));
@@ -13,20 +13,20 @@ CLASS(XonoticKeyBinder, XonoticListBox)
        METHOD(XonoticKeyBinder, keyGrabbed, void(entity, float, float));
        METHOD(XonoticKeyBinder, destroy, void(entity));
 
-       ATTRIB(XonoticKeyBinder, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticKeyBinder, realUpperMargin, float, 0)
-       ATTRIB(XonoticKeyBinder, columnFunctionOrigin, float, 0)
-       ATTRIB(XonoticKeyBinder, columnFunctionSize, float, 0)
-       ATTRIB(XonoticKeyBinder, columnKeysOrigin, float, 0)
-       ATTRIB(XonoticKeyBinder, columnKeysSize, float, 0)
+       ATTRIB(XonoticKeyBinder, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticKeyBinder, realUpperMargin, float, 0);
+       ATTRIB(XonoticKeyBinder, columnFunctionOrigin, float, 0);
+       ATTRIB(XonoticKeyBinder, columnFunctionSize, float, 0);
+       ATTRIB(XonoticKeyBinder, columnKeysOrigin, float, 0);
+       ATTRIB(XonoticKeyBinder, columnKeysSize, float, 0);
 
        METHOD(XonoticKeyBinder, loadKeyBinds, void(entity));
-       ATTRIB(XonoticKeyBinder, previouslySelected, int, -1)
-       ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0)
-       ATTRIB(XonoticKeyBinder, userbindEditButton, entity, NULL)
-       ATTRIB(XonoticKeyBinder, keyGrabButton, entity, NULL)
-       ATTRIB(XonoticKeyBinder, clearButton, entity, NULL)
-       ATTRIB(XonoticKeyBinder, userbindEditDialog, entity, NULL)
+       ATTRIB(XonoticKeyBinder, previouslySelected, int, -1);
+       ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0);
+       ATTRIB(XonoticKeyBinder, userbindEditButton, entity);
+       ATTRIB(XonoticKeyBinder, keyGrabButton, entity);
+       ATTRIB(XonoticKeyBinder, clearButton, entity);
+       ATTRIB(XonoticKeyBinder, userbindEditDialog, entity);
        METHOD(XonoticKeyBinder, editUserbind, void(entity, string, string, string));
 ENDCLASS(XonoticKeyBinder)
 entity makeXonoticKeyBinder();
index 8febd646781e4a1e8e94414c1048d88e7de721b9..daf249e450069f22f6a184c27bab04781dab4f9c 100644 (file)
@@ -3,31 +3,31 @@
 #include "listbox.qh"
 CLASS(XonoticLanguageList, XonoticListBox)
        METHOD(XonoticLanguageList, configureXonoticLanguageList, void(entity));
-       ATTRIB(XonoticLanguageList, rowsPerItem, float, 1)
+       ATTRIB(XonoticLanguageList, rowsPerItem, float, 1);
        METHOD(XonoticLanguageList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticLanguageList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticLanguageList, setSelected, void(entity, float));
        METHOD(XonoticLanguageList, loadCvars, void(entity));
        METHOD(XonoticLanguageList, saveCvars, void(entity));
 
-       ATTRIB(XonoticLanguageList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticLanguageList, realUpperMargin, float, 0)
-       ATTRIB(XonoticLanguageList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticLanguageList, columnNameSize, float, 0)
-       ATTRIB(XonoticLanguageList, columnPercentageOrigin, float, 0)
-       ATTRIB(XonoticLanguageList, columnPercentageSize, float, 0)
+       ATTRIB(XonoticLanguageList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticLanguageList, realUpperMargin, float, 0);
+       ATTRIB(XonoticLanguageList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticLanguageList, columnNameSize, float, 0);
+       ATTRIB(XonoticLanguageList, columnPercentageOrigin, float, 0);
+       ATTRIB(XonoticLanguageList, columnPercentageSize, float, 0);
 
        METHOD(XonoticLanguageList, doubleClickListBoxItem, void(entity, float, vector));
        METHOD(XonoticLanguageList, keyDown, float(entity, float, float, float));  // enter handling
 
        METHOD(XonoticLanguageList, destroy, void(entity));
 
-       ATTRIB(XonoticLanguageList, languagelist, float, -1)
+       ATTRIB(XonoticLanguageList, languagelist, float, -1);
        METHOD(XonoticLanguageList, getLanguages, void(entity));
        METHOD(XonoticLanguageList, setLanguage, void(entity));
        METHOD(XonoticLanguageList, languageParameter, string(entity, float, float));
 
-       ATTRIB(XonoticLanguageList, name, string, "languageselector")  // change this to make it noninteractive (for first run dialog)
+       ATTRIB(XonoticLanguageList, name, string, "languageselector");  // change this to make it noninteractive (for first run dialog);
 ENDCLASS(XonoticLanguageList)
 
 entity makeXonoticLanguageList();
index 3e789e772dd1b2372bee4fad53f0ccf7fa332758..14272cb19ba9f8e6db66f2a309daf000430adc10 100644 (file)
@@ -3,17 +3,17 @@
 #include "../item/listbox.qh"
 CLASS(XonoticListBox, ListBox)
        METHOD(XonoticListBox, configureXonoticListBox, void(entity));
-       ATTRIB(XonoticListBox, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticListBox, scrollbarWidth, float, SKINWIDTH_SCROLLBAR)
-       ATTRIB(XonoticListBox, src, string, SKINGFX_SCROLLBAR)
-       ATTRIB(XonoticListBox, tolerance, vector, SKINTOLERANCE_SLIDER)
-       ATTRIB(XonoticListBox, rowsPerItem, float, 1)
+       ATTRIB(XonoticListBox, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticListBox, scrollbarWidth, float, SKINWIDTH_SCROLLBAR);
+       ATTRIB(XonoticListBox, src, string, SKINGFX_SCROLLBAR);
+       ATTRIB(XonoticListBox, tolerance, vector, SKINTOLERANCE_SLIDER);
+       ATTRIB(XonoticListBox, rowsPerItem, float, 1);
        METHOD(XonoticListBox, resizeNotify, void(entity, vector, vector, vector, vector));
-       ATTRIB(XonoticListBox, color, vector, SKINCOLOR_SCROLLBAR_N)
-       ATTRIB(XonoticListBox, colorF, vector, SKINCOLOR_SCROLLBAR_F)
-       ATTRIB(XonoticListBox, color2, vector, SKINCOLOR_SCROLLBAR_S)
-       ATTRIB(XonoticListBox, colorC, vector, SKINCOLOR_SCROLLBAR_C)
-       ATTRIB(XonoticListBox, colorBG, vector, SKINCOLOR_LISTBOX_BACKGROUND)
-       ATTRIB(XonoticListBox, alphaBG, float, SKINALPHA_LISTBOX_BACKGROUND)
+       ATTRIB(XonoticListBox, color, vector, SKINCOLOR_SCROLLBAR_N);
+       ATTRIB(XonoticListBox, colorF, vector, SKINCOLOR_SCROLLBAR_F);
+       ATTRIB(XonoticListBox, color2, vector, SKINCOLOR_SCROLLBAR_S);
+       ATTRIB(XonoticListBox, colorC, vector, SKINCOLOR_SCROLLBAR_C);
+       ATTRIB(XonoticListBox, colorBG, vector, SKINCOLOR_LISTBOX_BACKGROUND);
+       ATTRIB(XonoticListBox, alphaBG, float, SKINALPHA_LISTBOX_BACKGROUND);
 ENDCLASS(XonoticListBox)
 entity makeXonoticListBox();
index fb25eebdef39bc2d4bc92cb5b1fd78d0f33c767e..0e071c2e71efc20321722e13b4246f46ec81cfda 100644 (file)
@@ -39,6 +39,7 @@
 #include "dialog_sandboxtools.qh"
 #include "dialog_monstertools.qh"
 #include "dialog_teamselect.qh"
+#include "dialog_uid2name.qh"
 #include "dialog_singleplayer.qh"
 #include "dialog_multiplayer.qh"
 #include "dialog_settings.qh"
@@ -224,6 +225,10 @@ void MainWindow_configureMainWindow(entity me)
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
 
+       i = NEW(XonoticUid2NameDialog);
+       i.configureDialog(i);
+       me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
+
        i = NEW(XonoticMonsterToolsDialog);
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS);
index daf5e0571cf4616af7fd4c9c3d1e476686612d8b..2397d41431605a6d59b36df2b996ded9d56bed65 100644 (file)
@@ -5,22 +5,22 @@
 CLASS(MainWindow, ModalController)
        METHOD(MainWindow, configureMainWindow, void(entity));
        METHOD(MainWindow, draw, void(entity));
-       ATTRIB(MainWindow, firstRunDialog, entity, NULL)
-       ATTRIB(MainWindow, advancedDialog, entity, NULL)
-       ATTRIB(MainWindow, mutatorsDialog, entity, NULL)
-       ATTRIB(MainWindow, mapInfoDialog, entity, NULL)
-       ATTRIB(MainWindow, userbindEditDialog, entity, NULL)
-       ATTRIB(MainWindow, winnerDialog, entity, NULL)
-       ATTRIB(MainWindow, serverInfoDialog, entity, NULL)
-       ATTRIB(MainWindow, cvarsDialog, entity, NULL)
-       ATTRIB(MainWindow, screenshotViewerDialog, entity, NULL)
-       ATTRIB(MainWindow, viewDialog, entity, NULL)
-       ATTRIB(MainWindow, hudconfirmDialog, entity, NULL)
-       ATTRIB(MainWindow, languageWarningDialog, entity, NULL)
-       ATTRIB(MainWindow, mainNexposee, entity, NULL)
-       ATTRIB(MainWindow, fadedAlpha, float, SKINALPHA_BEHIND)
-       ATTRIB(MainWindow, dialogToShow, entity, NULL)
-       ATTRIB(MainWindow, demostartconfirmDialog, entity, NULL)
-       ATTRIB(MainWindow, demotimeconfirmDialog, entity, NULL)
-       ATTRIB(MainWindow, resetDialog, entity, NULL)
+       ATTRIB(MainWindow, firstRunDialog, entity);
+       ATTRIB(MainWindow, advancedDialog, entity);
+       ATTRIB(MainWindow, mutatorsDialog, entity);
+       ATTRIB(MainWindow, mapInfoDialog, entity);
+       ATTRIB(MainWindow, userbindEditDialog, entity);
+       ATTRIB(MainWindow, winnerDialog, entity);
+       ATTRIB(MainWindow, serverInfoDialog, entity);
+       ATTRIB(MainWindow, cvarsDialog, entity);
+       ATTRIB(MainWindow, screenshotViewerDialog, entity);
+       ATTRIB(MainWindow, viewDialog, entity);
+       ATTRIB(MainWindow, hudconfirmDialog, entity);
+       ATTRIB(MainWindow, languageWarningDialog, entity);
+       ATTRIB(MainWindow, mainNexposee, entity);
+       ATTRIB(MainWindow, fadedAlpha, float, SKINALPHA_BEHIND);
+       ATTRIB(MainWindow, dialogToShow, entity);
+       ATTRIB(MainWindow, demostartconfirmDialog, entity);
+       ATTRIB(MainWindow, demotimeconfirmDialog, entity);
+       ATTRIB(MainWindow, resetDialog, entity);
 ENDCLASS(MainWindow)
index 4b41f5bd3be6eb514600614e290c834df62f3a49..8feee96ed7a05a7ac74ef8a97a8eae709a503c2e 100644 (file)
@@ -172,9 +172,8 @@ void XonoticMapList_refilter(entity me)
 {
        float i, j, n;
        string s;
-       float gt, f;
-       gt = MapInfo_CurrentGametype();
-       f = MapInfo_CurrentFeatures();
+       Gametype gt = MapInfo_CurrentGametype();
+       int f = MapInfo_CurrentFeatures();
        MapInfo_FilterGametype(gt, f, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
        if (me.stringFilter)
                MapInfo_FilterString(me.stringFilter);
@@ -256,7 +255,7 @@ void MapList_Add_All(entity btn, entity me)
 {
        float i;
        string s;
-       MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all
+       _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all
        s = "";
        for(i = 0; i < MapInfo_count; ++i)
                s = strcat(s, " ", MapInfo_BSPName_ByID(i));
index de9d7e28a314d5fb13072cf7dd8b2aa48a95fdbe..ae6f6185e54e79dd2aa30c708f1054c8adecb813 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticMapList, XonoticListBox)
        METHOD(XonoticMapList, configureXonoticMapList, void(entity));
-       ATTRIB(XonoticMapList, rowsPerItem, float, 4)
+       ATTRIB(XonoticMapList, rowsPerItem, float, 4);
        METHOD(XonoticMapList, draw, void(entity));
        METHOD(XonoticMapList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticMapList, clickListBoxItem, void(entity, float, vector));
@@ -13,39 +13,39 @@ CLASS(XonoticMapList, XonoticListBox)
        METHOD(XonoticMapList, refilterCallback, void(entity, entity));
        METHOD(XonoticMapList, keyDown, float(entity, float, float, float));
 
-       ATTRIB(XonoticMapList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticMapList, columnPreviewOrigin, float, 0)
-       ATTRIB(XonoticMapList, columnPreviewSize, float, 0)
-       ATTRIB(XonoticMapList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticMapList, columnNameSize, float, 0)
-       ATTRIB(XonoticMapList, checkMarkOrigin, vector, '0 0 0')
-       ATTRIB(XonoticMapList, checkMarkSize, vector, '0 0 0')
-       ATTRIB(XonoticMapList, realUpperMargin1, float, 0)
-       ATTRIB(XonoticMapList, realUpperMargin2, float, 0)
+       ATTRIB(XonoticMapList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticMapList, columnPreviewOrigin, float, 0);
+       ATTRIB(XonoticMapList, columnPreviewSize, float, 0);
+       ATTRIB(XonoticMapList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticMapList, columnNameSize, float, 0);
+       ATTRIB(XonoticMapList, checkMarkOrigin, vector, '0 0 0');
+       ATTRIB(XonoticMapList, checkMarkSize, vector, '0 0 0');
+       ATTRIB(XonoticMapList, realUpperMargin1, float, 0);
+       ATTRIB(XonoticMapList, realUpperMargin2, float, 0);
 
-       ATTRIB(XonoticMapList, lastGametype, float, 0)
-       ATTRIB(XonoticMapList, lastFeatures, float, 0)
+       ATTRIB(XonoticMapList, lastGametype, entity);
+       ATTRIB(XonoticMapList, lastFeatures, float, 0);
 
-       ATTRIB(XonoticMapList, origin, vector, '0 0 0')
-       ATTRIB(XonoticMapList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticMapList, origin, vector, '0 0 0');
+       ATTRIB(XonoticMapList, itemAbsSize, vector, '0 0 0');
 
-       ATTRIB(XonoticMapList, g_maplistCache, string, string_null)
+       ATTRIB(XonoticMapList, g_maplistCache, string);
        METHOD(XonoticMapList, g_maplistCacheToggle, void(entity, float));
        METHOD(XonoticMapList, g_maplistCacheQuery, float(entity, float));
 
-       ATTRIB(XonoticMapList, stringFilter, string, string_null)
-       ATTRIB(XonoticMapList, stringFilterBox, entity, NULL)
+       ATTRIB(XonoticMapList, stringFilter, string);
+       ATTRIB(XonoticMapList, stringFilterBox, entity);
 
-       ATTRIB(XonoticMapList, startButton, entity, NULL)
+       ATTRIB(XonoticMapList, startButton, entity);
 
        METHOD(XonoticMapList, loadCvars, void(entity));
 
-       ATTRIB(XonoticMapList, typeToSearchString, string, string_null)
-       ATTRIB(XonoticMapList, typeToSearchTime, float, 0)
+       ATTRIB(XonoticMapList, typeToSearchString, string);
+       ATTRIB(XonoticMapList, typeToSearchTime, float, 0);
 
        METHOD(XonoticMapList, destroy, void(entity));
 
-       ATTRIB(XonoticMapList, alphaBG, float, 0)
+       ATTRIB(XonoticMapList, alphaBG, float, 0);
 ENDCLASS(XonoticMapList)
 entity makeXonoticMapList();
 void MapList_StringFilterBox_Change(entity box, entity me);
index c530c7ca9cda274a197448aab1085ad1187d3e50..b8e19cf19ebaedf19a3fcb0f440f877ae3006012 100644 (file)
@@ -9,23 +9,23 @@ CLASS(XonoticPicker, Item)
        METHOD(XonoticPicker, mouseDrag, float(entity, vector));
        METHOD(XonoticPicker, keyDown, float(entity, float, float, float));
        METHOD(XonoticPicker, draw, void(entity));
-       ATTRIB(XonoticPicker, focusable, float, 1)
-       ATTRIB(XonoticPicker, disabled, float, 0)
-       ATTRIB(XonoticPicker, alpha, float, 1)
-       ATTRIB(XonoticPicker, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticPicker, focusable, float, 1);
+       ATTRIB(XonoticPicker, disabled, float, 0);
+       ATTRIB(XonoticPicker, alpha, float, 1);
+       ATTRIB(XonoticPicker, disabledAlpha, float, SKINALPHA_DISABLED);
 
-       ATTRIB(XonoticPicker, rows, float, 3)
-       ATTRIB(XonoticPicker, columns, float, 2)
+       ATTRIB(XonoticPicker, rows, float, 3);
+       ATTRIB(XonoticPicker, columns, float, 2);
 
        METHOD(XonoticPicker, moveFocus, void(entity, vector, vector));
        METHOD(XonoticPicker, cellSelect, void(entity, vector));
        METHOD(XonoticPicker, cellDraw, void(entity, vector, vector));
        METHOD(XonoticPicker, cellIsValid, bool(entity, vector));
-       ATTRIB(XonoticPicker, realCellSize, vector, '0 0 0')
-       ATTRIB(XonoticPicker, selectedCell, vector, '-1 -1 0')
-       ATTRIB(XonoticPicker, focusedCell, vector, '-1 -1 0')
-       ATTRIB(XonoticPicker, focusedCellAlpha, float, 0)
-       ATTRIB(XonoticPicker, focusedCellTime, float, 0)
-       ATTRIB(XonoticPicker, pressedCell, vector, '-1 -1 0')
+       ATTRIB(XonoticPicker, realCellSize, vector, '0 0 0');
+       ATTRIB(XonoticPicker, selectedCell, vector, '-1 -1 0');
+       ATTRIB(XonoticPicker, focusedCell, vector, '-1 -1 0');
+       ATTRIB(XonoticPicker, focusedCellAlpha, float, 0);
+       ATTRIB(XonoticPicker, focusedCellTime, float, 0);
+       ATTRIB(XonoticPicker, pressedCell, vector, '-1 -1 0');
 ENDCLASS(XonoticPicker)
 entity makeXonoticPicker();
index 8474b9140d8f1d86f9b579f5fc44fce2dc73d68e..a35ed966d9360d22dcf9b62505cccd97450ddc53 100644 (file)
@@ -2,21 +2,21 @@
 
 #include "listbox.qh"
 CLASS(XonoticPlayerList, XonoticListBox)
-       ATTRIB(XonoticPlayerList, rowsPerItem, float, 1)
+       ATTRIB(XonoticPlayerList, rowsPerItem, float, 1);
        METHOD(XonoticPlayerList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticPlayerList, drawListBoxItem, void(entity, int, vector, bool, bool));
-       ATTRIB(XonoticPlayerList, allowFocusSound, float, 0)
-       ATTRIB(XonoticPlayerList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticPlayerList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticPlayerList, columnNameSize, float, 0)
-       ATTRIB(XonoticPlayerList, columnScoreOrigin, float, 0)
-       ATTRIB(XonoticPlayerList, columnScoreSize, float, 0)
-       ATTRIB(XonoticPlayerList, realUpperMargin, float, 0)
-       ATTRIB(XonoticPlayerList, origin, vector, '0 0 0')
-       ATTRIB(XonoticPlayerList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticPlayerList, allowFocusSound, float, 0);
+       ATTRIB(XonoticPlayerList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticPlayerList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticPlayerList, columnNameSize, float, 0);
+       ATTRIB(XonoticPlayerList, columnScoreOrigin, float, 0);
+       ATTRIB(XonoticPlayerList, columnScoreSize, float, 0);
+       ATTRIB(XonoticPlayerList, realUpperMargin, float, 0);
+       ATTRIB(XonoticPlayerList, origin, vector, '0 0 0');
+       ATTRIB(XonoticPlayerList, itemAbsSize, vector, '0 0 0');
        METHOD(XonoticPlayerList, setPlayerList, void(entity, string));
        METHOD(XonoticPlayerList, getPlayerList, string(entity, float, float));
-       ATTRIB(XonoticPlayerList, playerList, float, -1)
-       ATTRIB(XonoticPlayerList, selectionDoesntMatter, bool, true)
+       ATTRIB(XonoticPlayerList, playerList, float, -1);
+       ATTRIB(XonoticPlayerList, selectionDoesntMatter, bool, true);
 ENDCLASS(XonoticPlayerList)
 entity makeXonoticPlayerList();
index 1a90fa737827bc8e2afaa6b074306d815439d046..c679d4449d7177598dc649228f7e4cc96b11be76 100644 (file)
@@ -38,6 +38,8 @@ void XonoticPlayerModelSelector_loadModels(entity me)
                fn = search_getfilename(glob, i);
                if(!get_model_parameters(fn, -1))
                        continue;
+               if(get_model_parameters_hidden)
+                       continue;
                bufstr_add(sortbuf, sprintf("%-128s%s", get_model_parameters_name, fn), 1);
        }
        search_end(glob);
@@ -49,6 +51,8 @@ void XonoticPlayerModelSelector_loadModels(entity me)
                fn = substring(bufstr_get(sortbuf, i), 128, -1);
                if(!get_model_parameters(fn, -1))
                        error("But it JUST worked!");
+               if(get_model_parameters_hidden)
+                       continue;
                bufstr_set(me.bufModels, BUFMODELS_COUNT*i+BUFMODELS_TITLE, get_model_parameters_name);
                bufstr_set(me.bufModels, BUFMODELS_COUNT*i+BUFMODELS_IMAGE, strcat("/", substring(get_model_datafilename(get_model_parameters_modelname, get_model_parameters_modelskin, "tga"), 0, -5)));
                bufstr_set(me.bufModels, BUFMODELS_COUNT*i+BUFMODELS_MODEL, get_model_parameters_modelname);
index 664f22c7c0e2c9d3970e94c9f7403ddfdab49ca0..4635c6ccdb65b438edffe6d28d71b2a492765445 100644 (file)
@@ -9,21 +9,21 @@ CLASS(XonoticPlayerModelSelector, XonoticImage)
        METHOD(XonoticPlayerModelSelector, draw, void(entity));
        METHOD(XonoticPlayerModelSelector, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticPlayerModelSelector, showNotify, void(entity));
-       ATTRIB(XonoticPlayerModelSelector, currentModel, string, string_null)
-       ATTRIB(XonoticPlayerModelSelector, currentSkin, float, 0)
-       ATTRIB(XonoticPlayerModelSelector, currentModelImage, string, string_null)
-       ATTRIB(XonoticPlayerModelSelector, currentModelTitle, string, string_null)
-       ATTRIB(XonoticPlayerModelSelector, currentModelDescription, string, string_null)
+       ATTRIB(XonoticPlayerModelSelector, currentModel, string);
+       ATTRIB(XonoticPlayerModelSelector, currentSkin, float, 0);
+       ATTRIB(XonoticPlayerModelSelector, currentModelImage, string);
+       ATTRIB(XonoticPlayerModelSelector, currentModelTitle, string);
+       ATTRIB(XonoticPlayerModelSelector, currentModelDescription, string);
        METHOD(XonoticPlayerModelSelector, go, void(entity, float));
        METHOD(XonoticPlayerModelSelector, destroy, void(entity));
-       ATTRIB(XonoticPlayerModelSelector, origin, vector, '0 0 0')
-       ATTRIB(XonoticPlayerModelSelector, size, vector, '0 0 0')
-       ATTRIB(XonoticPlayerModelSelector, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticPlayerModelSelector, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticPlayerModelSelector, titleFontSize, float, SKINFONTSIZE_TITLE)
-       ATTRIB(XonoticPlayerModelSelector, bufModels, float, -1)
-       ATTRIB(XonoticPlayerModelSelector, numModels, float, -1)
-       ATTRIB(XonoticPlayerModelSelector, idxModels, float, -1)
+       ATTRIB(XonoticPlayerModelSelector, origin, vector, '0 0 0');
+       ATTRIB(XonoticPlayerModelSelector, size, vector, '0 0 0');
+       ATTRIB(XonoticPlayerModelSelector, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticPlayerModelSelector, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticPlayerModelSelector, titleFontSize, float, SKINFONTSIZE_TITLE);
+       ATTRIB(XonoticPlayerModelSelector, bufModels, float, -1);
+       ATTRIB(XonoticPlayerModelSelector, numModels, float, -1);
+       ATTRIB(XonoticPlayerModelSelector, idxModels, float, -1);
 ENDCLASS(XonoticPlayerModelSelector)
 entity makeXonoticPlayerModelSelector();
 void PlayerModelSelector_Next_Click(entity btn, entity me);
index 6bee05bbc88f3961ba6bce22bbe50613b4177d09..bd9f71988fdc59e12dcf51cf43231a158795bff4 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticPlayList, XonoticListBox)
        METHOD(XonoticPlayList, configureXonoticPlayList, void(entity));
-       ATTRIB(XonoticPlayList, rowsPerItem, float, 1)
+       ATTRIB(XonoticPlayList, rowsPerItem, float, 1);
        METHOD(XonoticPlayList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticPlayList, draw, void(entity));
        METHOD(XonoticPlayList, drawListBoxItem, void(entity, int, vector, bool, bool));
@@ -17,16 +17,16 @@ CLASS(XonoticPlayList, XonoticListBox)
 
        METHOD(XonoticPlayList, addToPlayList, void(entity, string));
        METHOD(XonoticPlayList, removeSelectedFromPlayList, void(entity));
-       ATTRIB(XonoticPlayList, playingTrack, float, -1)
+       ATTRIB(XonoticPlayList, playingTrack, float, -1);
 
-       ATTRIB(XonoticPlayList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticPlayList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticPlayList, columnNameSize, float, 0)
-       ATTRIB(XonoticPlayList, columnNumberOrigin, float, 0)
-       ATTRIB(XonoticPlayList, columnNumberSize, float, 0)
-       ATTRIB(XonoticPlayList, realUpperMargin, float, 0)
-       ATTRIB(XonoticPlayList, origin, vector, '0 0 0')
-       ATTRIB(XonoticPlayList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticPlayList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticPlayList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticPlayList, columnNameSize, float, 0);
+       ATTRIB(XonoticPlayList, columnNumberOrigin, float, 0);
+       ATTRIB(XonoticPlayList, columnNumberSize, float, 0);
+       ATTRIB(XonoticPlayList, realUpperMargin, float, 0);
+       ATTRIB(XonoticPlayList, origin, vector, '0 0 0');
+       ATTRIB(XonoticPlayList, itemAbsSize, vector, '0 0 0');
 ENDCLASS(XonoticPlayList)
 
 entity makeXonoticPlayList();
index 36c228fcf41ae8202e86d20d953443ca90a729ce..cd671a338f296f73ee8a8eb124f03aa1dd50872c 100644 (file)
@@ -5,22 +5,22 @@ CLASS(XonoticRadioButton, RadioButton)
        METHOD(XonoticRadioButton, configureXonoticRadioButton, void(entity, float, string, string, string, string));
        METHOD(XonoticRadioButton, draw, void(entity));
        METHOD(XonoticRadioButton, setChecked, void(entity, float));
-       ATTRIB(XonoticRadioButton, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticRadioButton, image, string, SKINGFX_RADIOBUTTON)
-       ATTRIB(XonoticRadioButton, color, vector, SKINCOLOR_RADIOBUTTON_N)
-       ATTRIB(XonoticRadioButton, colorC, vector, SKINCOLOR_RADIOBUTTON_C)
-       ATTRIB(XonoticRadioButton, colorF, vector, SKINCOLOR_RADIOBUTTON_F)
-       ATTRIB(XonoticRadioButton, colorD, vector, SKINCOLOR_RADIOBUTTON_D)
+       ATTRIB(XonoticRadioButton, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticRadioButton, image, string, SKINGFX_RADIOBUTTON);
+       ATTRIB(XonoticRadioButton, color, vector, SKINCOLOR_RADIOBUTTON_N);
+       ATTRIB(XonoticRadioButton, colorC, vector, SKINCOLOR_RADIOBUTTON_C);
+       ATTRIB(XonoticRadioButton, colorF, vector, SKINCOLOR_RADIOBUTTON_F);
+       ATTRIB(XonoticRadioButton, colorD, vector, SKINCOLOR_RADIOBUTTON_D);
 
-       ATTRIB(XonoticRadioButton, cvarName, string, string_null)
-       ATTRIB(XonoticRadioButton, cvarValue, string, string_null)
-       ATTRIB(XonoticRadioButton, cvarOffValue, string, string_null)
-       ATTRIB(XonoticRadioButton, cvarValueIsAnotherCvar, float, 0)
+       ATTRIB(XonoticRadioButton, cvarName, string);
+       ATTRIB(XonoticRadioButton, cvarValue, string);
+       ATTRIB(XonoticRadioButton, cvarOffValue, string);
+       ATTRIB(XonoticRadioButton, cvarValueIsAnotherCvar, float, 0);
        METHOD(XonoticRadioButton, loadCvars, void(entity));
        METHOD(XonoticRadioButton, saveCvars, void(entity));
 
-       ATTRIB(XonoticRadioButton, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticRadioButton, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticRadioButton, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticRadioButton, disabledAlpha, float, SKINALPHA_DISABLED);
 ENDCLASS(XonoticRadioButton)
 entity makeXonoticRadioButton_T(float, string, string, string, string theTooltip);
 entity makeXonoticRadioButton(float, string, string, string);
index 0bfd3364cbd801c62c8e8fa3617d8dc4fdf54e35..a2efa672e9174c9f733113b7765e937237380f0b 100644 (file)
@@ -4,12 +4,12 @@
 CLASS(XonoticRootDialog, XonoticDialog)
        // still to be customized by user
        /*
-       ATTRIB(XonoticDialog, closable, float, 1)
-       ATTRIB(XonoticDialog, title, string, _("Form1")) // ;)
-       ATTRIB(XonoticDialog, color, vector, '1 0.5 1')
-       ATTRIB(XonoticDialog, intendedWidth, float, 0)
-       ATTRIB(XonoticDialog, rows, float, 3)
-       ATTRIB(XonoticDialog, columns, float, 2)
+       ATTRIB(XonoticDialog, closable, float, 1);
+       ATTRIB(XonoticDialog, title, string, _("Form1")); // ;);
+       ATTRIB(XonoticDialog, color, vector, '1 0.5 1');
+       ATTRIB(XonoticDialog, intendedWidth, float, 0);
+       ATTRIB(XonoticDialog, rows, float, 3);
+       ATTRIB(XonoticDialog, columns, float, 2);
        */
        METHOD(XonoticRootDialog, close, void(entity));
 ENDCLASS(XonoticRootDialog)
index 82ff4ba7b2b8dad148e2b4f20cf854271ce95838..42e168b4b58b2617d5978b7bce289bd8ac4a8131 100644 (file)
@@ -56,12 +56,11 @@ void XonoticScreenshotImage_draw(entity me)
                if (time < me.zoomTime + 2) // 1 seconds at full alpha, 1 second fading out
                {
                        string zoomString;
-                       float z;
-                       z = me.zoomFactor * 100;
-                       if (z - floor(z) == 0)
-                               zoomString = sprintf("%d%%", z);
+                       float myzoom = me.zoomFactor * 100;
+                       if (myzoom - floor(myzoom) == 0)
+                               zoomString = sprintf("%d%%", myzoom);
                        else
-                               zoomString = sprintf("%.2f%%", z);
+                               zoomString = sprintf("%.2f%%", myzoom);
                        theAlpha = (2 - (time - me.zoomTime));
                        draw_Text('0.05 0.95 0', zoomString, me.realFontSize, '1 1 1', theAlpha, false);
                }
index eb20c73a243a2d2691c8fee4bd5317a68e458188..8920bed287f50fceed2527f56646d36a8599f812 100644 (file)
@@ -5,15 +5,15 @@ CLASS(XonoticScreenshotImage, XonoticImage)
        METHOD(XonoticScreenshotImage, configureXonoticScreenshotImage, void(entity));
        METHOD(XonoticScreenshotImage, load, void(entity, string));
        METHOD(XonoticScreenshotImage, draw, void(entity));
-       ATTRIB(XonoticScreenshotImage, focusable, float, 1)  // mousePress and mouseDrag work only if focusable is set
+       ATTRIB(XonoticScreenshotImage, focusable, float, 1);  // mousePress and mouseDrag work only if focusable is set
        METHOD(XonoticScreenshotImage, mousePress, float(entity, vector));
        METHOD(XonoticScreenshotImage, mouseDrag, float(entity, vector));
        METHOD(XonoticScreenshotImage, mouseMove, float(entity, vector));
        METHOD(XonoticScreenshotImage, resizeNotify, void(entity, vector, vector, vector, vector));
-       ATTRIB(XonoticScreenshotImage, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticScreenshotImage, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticScreenshotImage, showTitle, float, 1)
-       ATTRIB(XonoticScreenshotImage, screenshotTime, float, 0)
-       ATTRIB(XonoticScreenshotImage, screenshotTitle, string, string_null)
+       ATTRIB(XonoticScreenshotImage, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticScreenshotImage, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticScreenshotImage, showTitle, float, 1);
+       ATTRIB(XonoticScreenshotImage, screenshotTime, float, 0);
+       ATTRIB(XonoticScreenshotImage, screenshotTitle, string);
 ENDCLASS(XonoticScreenshotImage)
 entity makeXonoticScreenshotImage();
index 1279c4ad9cf6de503d576127d700a5517c84637e..63071fc74081a92434d234599e80992e4189ed11 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticScreenshotList, XonoticListBox)
        METHOD(XonoticScreenshotList, configureXonoticScreenshotList, void(entity));
-       ATTRIB(XonoticScreenshotList, rowsPerItem, float, 1)
+       ATTRIB(XonoticScreenshotList, rowsPerItem, float, 1);
        METHOD(XonoticScreenshotList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticScreenshotList, setSelected, void(entity, float));
        METHOD(XonoticScreenshotList, draw, void(entity));
@@ -16,23 +16,23 @@ CLASS(XonoticScreenshotList, XonoticListBox)
        METHOD(XonoticScreenshotList, keyDown, float(entity, float, float, float));
        METHOD(XonoticScreenshotList, destroy, void(entity));
        METHOD(XonoticScreenshotList, showNotify, void(entity));
-       ATTRIB(XonoticScreenshotList, listScreenshot, float, -1)
-       ATTRIB(XonoticScreenshotList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticScreenshotList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticScreenshotList, columnNameSize, float, 0)
-       ATTRIB(XonoticScreenshotList, realUpperMargin, float, 0)
-       ATTRIB(XonoticScreenshotList, origin, vector, '0 0 0')
-       ATTRIB(XonoticScreenshotList, itemAbsSize, vector, '0 0 0')
-       ATTRIB(XonoticScreenshotList, filterString, string, string_null)
-       ATTRIB(XonoticScreenshotList, filterBox, entity, NULL)
-       ATTRIB(XonoticScreenshotList, filterTime, float, 0)
+       ATTRIB(XonoticScreenshotList, listScreenshot, float, -1);
+       ATTRIB(XonoticScreenshotList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticScreenshotList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticScreenshotList, columnNameSize, float, 0);
+       ATTRIB(XonoticScreenshotList, realUpperMargin, float, 0);
+       ATTRIB(XonoticScreenshotList, origin, vector, '0 0 0');
+       ATTRIB(XonoticScreenshotList, itemAbsSize, vector, '0 0 0');
+       ATTRIB(XonoticScreenshotList, filterString, string);
+       ATTRIB(XonoticScreenshotList, filterBox, entity);
+       ATTRIB(XonoticScreenshotList, filterTime, float, 0);
 
-       ATTRIB(XonoticScreenshotList, newScreenshotTime, float, 0)
-       ATTRIB(XonoticScreenshotList, newSlideShowScreenshotTime, float, 0)
+       ATTRIB(XonoticScreenshotList, newScreenshotTime, float, 0);
+       ATTRIB(XonoticScreenshotList, newSlideShowScreenshotTime, float, 0);
 
-       ATTRIB(XonoticScreenshotList, screenshotBrowserDialog, entity, NULL)
-       ATTRIB(XonoticScreenshotList, screenshotPreview, entity, NULL)
-       ATTRIB(XonoticScreenshotList, screenshotViewerDialog, entity, NULL)
+       ATTRIB(XonoticScreenshotList, screenshotBrowserDialog, entity);
+       ATTRIB(XonoticScreenshotList, screenshotPreview, entity);
+       ATTRIB(XonoticScreenshotList, screenshotViewerDialog, entity);
        METHOD(XonoticScreenshotList, goScreenshot, void(entity, float));
        METHOD(XonoticScreenshotList, startSlideShow, void(entity));
        METHOD(XonoticScreenshotList, stopSlideShow, void(entity));
index fc2ba573bbaa7448addcd648cde86d9a2ae421e2..bbb9dcac05488c54a2df6722a4ea591a477349e5 100644 (file)
@@ -212,7 +212,7 @@ int CheckCategoryForEntry(int entry)
                        case "cts":
                        case "xdf": { return CAT_DEFRAG; }
 
-                       default: { LOG_TRACEF("Found strange mod type: %s\n", modtype); return CAT_MODIFIED; }
+                       default: { LOG_TRACEF("Found strange mod type: %s", modtype); return CAT_MODIFIED; }
                }
        }
 
@@ -471,9 +471,8 @@ void XonoticServerList_draw(entity me)
                // entire list, otherwise there is no way to know which item is first in its category.
 
                // binary search method suggested by div
-               float x;
                float begin = 0;
-               for(x = 1; x <= category_ent_count; ++x) {
+               for(int j = 1; j <= category_ent_count; ++j) {
                        float first = begin;
                        float last = (itemcount - 1);
                        if (first > last) {
@@ -482,42 +481,42 @@ void XonoticServerList_draw(entity me)
                        }
                        float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first);
                        float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last);
-                       if (catf > x) {
-                               // The first one is already > x.
-                               // Therefore, category x does not exist.
+                       if (catf > j) {
+                               // The first one is already > j.
+                               // Therefore, category j does not exist.
                                // Higher numbered categories do exist though.
-                       } else if (catl < x) {
-                               // The last one is < x.
+                       } else if (catl < j) {
+                               // The last one is < j.
                                // Thus this category - and any following -
                                // don't exist.
                                break;
-                       } else if (catf == x) {
+                       } else if (catf == j) {
                                // Starts at first. This breaks the loop
                                // invariant in the binary search and thus has
                                // to be handled separately.
-                               if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != x)
+                               if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != j)
                                        error("Category mismatch I");
                                if(first > 0)
-                                       if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == x)
+                                       if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == j)
                                                error("Category mismatch II");
-                               category_name[category_draw_count] = x;
+                               category_name[category_draw_count] = j;
                                category_item[category_draw_count] = first;
                                ++category_draw_count;
                                begin = first + 1;
                        } else {
-                               // At this point, catf <= x < catl, thus
+                               // At this point, catf <= j < catl, thus
                                // catf < catl, thus first < last.
                                // INVARIANTS:
                                // last - first >= 1
                                // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first)
                                // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last)
-                               // catf < x
-                               // catl >= x
+                               // catf < j
+                               // catl >= j
                                while (last - first > 1) {
                                        float middle = floor((first + last) / 2);
                                        // By loop condition, middle != first && middle != last.
                                        float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
-                                       if (cat >= x) {
+                                       if (cat >= j) {
                                                last = middle;
                                                catl = cat;
                                        } else {
@@ -525,13 +524,13 @@ void XonoticServerList_draw(entity me)
                                                catf = cat;
                                        }
                                }
-                               if (catl == x) {
-                                       if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != x)
+                               if (catl == j) {
+                                       if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != j)
                                                error("Category mismatch III");
                                        if(last > 0)
-                                               if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == x)
+                                               if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == j)
                                                        error("Category mismatch IV");
-                                       category_name[category_draw_count] = x;
+                                       category_name[category_draw_count] = j;
                                        category_item[category_draw_count] = last;
                                        ++category_draw_count;
                                        begin = last + 1; // already scanned through these, skip 'em
@@ -630,10 +629,8 @@ void ServerList_PlayerSort_Click(entity btn, entity me)
 }
 void ServerList_TypeSort_Click(entity btn, entity me)
 {
-       string s, t;
-       float i, m;
-       s = me.filterString;
-       m = strstrofs(s, ":", 0);
+       string s = me.filterString;
+       int m = strstrofs(s, ":", 0);
        if(m >= 0)
        {
                s = substring(s, 0, m);
@@ -643,30 +640,23 @@ void ServerList_TypeSort_Click(entity btn, entity me)
        else
                s = "";
 
-       for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone
-       {
-               t = MapInfo_Type_ToString(i);
-               if(i > 1)
-                       if(t == "") // it repeats (default case)
-                       {
-                               // no type was found
-                               // choose the first one
-                               s = MapInfo_Type_ToString(1);
-                               break;
-                       }
-               if(s == t)
-               {
-                       // the type was found
-                       // choose the next one
-                       s = MapInfo_Type_ToString(i * 2);
-                       if(s == "")
-                               s = MapInfo_Type_ToString(1);
-                       break;
-               }
+       Gametype first = NULL; FOREACH(Gametypes, !first, first = it; break);
+       bool flag = false;
+       FOREACH(Gametypes, s == MapInfo_Type_ToString(it), {
+               // the type was found
+               // choose the next one
+               flag = true;
+               s = MapInfo_Type_ToString(Gametypes_from(it.m_id + 1));
+               if (s == "") s = MapInfo_Type_ToString(first);
+               break;
+       });
+       if (!flag) {
+               // no type was found
+               // choose the first one
+               s = MapInfo_Type_ToString(first);
        }
 
-       if(s != "")
-               s = strcat(s, ":");
+       if(s != "") s = strcat(s, ":");
        s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
 
        me.controlledTextbox.setText(me.controlledTextbox, s);
@@ -970,7 +960,7 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is
                isv6 = true;
                me.seenIPv6 += 1;
        }
-       else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0)
+       else if(IS_DIGIT(substring(s, 0, 1)))
        {
                isv4 = true;
                me.seenIPv4 += 1;
@@ -1070,7 +1060,7 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is
                t = strcat(t, _("encryption:"), " ", (q ? sprintf(_("AES level %d"), q) : ZCTX(_("ENC^none"))), ", ");
                t = strcat(t, sprintf(_("mod: %s"), ((modname == "xonotic") ? ZCTX(_("MOD^Default")) : original_modname)));
                if(pure_available)
-                       t = strcat(t, sprintf(_(" (%s)"), (pure) ? _("official settings") : _("modified settings")));
+                       t = strcat(t, sprintf(" (%s)", (pure) ? _("official settings") : _("modified settings")));
                t = strcat(t, ", ");
                t = strcat(t, ((sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS)) ? _("stats enabled") : _("stats disabled")));
                setZonedTooltip(me, t, string_null);
index ac726b222ad055e16b0bf6b2ffd5cb37d1aed751..e45abfda33b528cce9e9e72035dd4ab1807d47c5 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticServerList, XonoticListBox)
        METHOD(XonoticServerList, configureXonoticServerList, void(entity));
-       ATTRIB(XonoticServerList, rowsPerItem, float, 1)
+       ATTRIB(XonoticServerList, rowsPerItem, float, 1);
        METHOD(XonoticServerList, draw, void(entity));
        METHOD(XonoticServerList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticServerList, doubleClickListBoxItem, void(entity, float, vector));
@@ -11,56 +11,56 @@ CLASS(XonoticServerList, XonoticListBox)
        METHOD(XonoticServerList, keyDown, float(entity, float, float, float));
        METHOD(XonoticServerList, toggleFavorite, void(entity, string));
 
-       ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85)
+       ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85);
        METHOD(XonoticServerList, mouseMove, float(entity, vector));
-       ATTRIB(XonoticServerList, mouseOverIcons, bool, false)
+       ATTRIB(XonoticServerList, mouseOverIcons, bool, false);
        METHOD(XonoticServerList, focusedItemChangeNotify, void(entity));
 
-       ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticServerList, realUpperMargin, float, 0)
-       ATTRIB(XonoticServerList, columnIconsOrigin, float, 0)
-       ATTRIB(XonoticServerList, columnIconsSize, float, 0)
-       ATTRIB(XonoticServerList, columnPingOrigin, float, 0)
-       ATTRIB(XonoticServerList, columnPingSize, float, 0)
-       ATTRIB(XonoticServerList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticServerList, columnNameSize, float, 0)
-       ATTRIB(XonoticServerList, columnMapOrigin, float, 0)
-       ATTRIB(XonoticServerList, columnMapSize, float, 0)
-       ATTRIB(XonoticServerList, columnTypeOrigin, float, 0)
-       ATTRIB(XonoticServerList, columnTypeSize, float, 0)
-       ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0)
-       ATTRIB(XonoticServerList, columnPlayersSize, float, 0)
-       ATTRIB(XonoticServerList, lockedSelectedItem, bool, true)      // initially keep selected the first item of the list, avoiding an unwanted scrolling
-
-       ATTRIB(XonoticServerList, selectedServer, string, string_null) // to restore selected server when needed
+       ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticServerList, realUpperMargin, float, 0);
+       ATTRIB(XonoticServerList, columnIconsOrigin, float, 0);
+       ATTRIB(XonoticServerList, columnIconsSize, float, 0);
+       ATTRIB(XonoticServerList, columnPingOrigin, float, 0);
+       ATTRIB(XonoticServerList, columnPingSize, float, 0);
+       ATTRIB(XonoticServerList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticServerList, columnNameSize, float, 0);
+       ATTRIB(XonoticServerList, columnMapOrigin, float, 0);
+       ATTRIB(XonoticServerList, columnMapSize, float, 0);
+       ATTRIB(XonoticServerList, columnTypeOrigin, float, 0);
+       ATTRIB(XonoticServerList, columnTypeSize, float, 0);
+       ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0);
+       ATTRIB(XonoticServerList, columnPlayersSize, float, 0);
+       ATTRIB(XonoticServerList, lockedSelectedItem, bool, true);      // initially keep selected the first item of the list, avoiding an unwanted scrolling
+
+       ATTRIB(XonoticServerList, selectedServer, string); // to restore selected server when needed
        METHOD(XonoticServerList, setSelected, void(entity, float));
        METHOD(XonoticServerList, setSortOrder, void(entity, float, float));
-       ATTRIB(XonoticServerList, filterShowEmpty, float, 1)
-       ATTRIB(XonoticServerList, filterShowFull, float, 1)
-       ATTRIB(XonoticServerList, filterString, string, string_null)
-       ATTRIB(XonoticServerList, controlledTextbox, entity, NULL)
-       ATTRIB(XonoticServerList, ipAddressBox, entity, NULL)
-       ATTRIB(XonoticServerList, favoriteButton, entity, NULL)
-       ATTRIB(XonoticServerList, nextRefreshTime, float, 0)
+       ATTRIB(XonoticServerList, filterShowEmpty, float, 1);
+       ATTRIB(XonoticServerList, filterShowFull, float, 1);
+       ATTRIB(XonoticServerList, filterString, string);
+       ATTRIB(XonoticServerList, controlledTextbox, entity);
+       ATTRIB(XonoticServerList, ipAddressBox, entity);
+       ATTRIB(XonoticServerList, favoriteButton, entity);
+       ATTRIB(XonoticServerList, nextRefreshTime, float, 0);
        METHOD(XonoticServerList, refreshServerList, void(entity, float));  // refresh mode: REFRESHSERVERLIST_*
-       ATTRIB(XonoticServerList, needsRefresh, float, 1)
+       ATTRIB(XonoticServerList, needsRefresh, float, 1);
        METHOD(XonoticServerList, focusEnter, void(entity));
        METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)));
-       ATTRIB(XonoticServerList, sortButton1, entity, NULL)
-       ATTRIB(XonoticServerList, sortButton2, entity, NULL)
-       ATTRIB(XonoticServerList, sortButton3, entity, NULL)
-       ATTRIB(XonoticServerList, sortButton4, entity, NULL)
-       ATTRIB(XonoticServerList, sortButton5, entity, NULL)
-       ATTRIB(XonoticServerList, connectButton, entity, NULL)
-       ATTRIB(XonoticServerList, infoButton, entity, NULL)
-       ATTRIB(XonoticServerList, currentSortOrder, float, 0)
-       ATTRIB(XonoticServerList, currentSortField, float, -1)
-
-       ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1)
-
-       ATTRIB(XonoticServerList, seenIPv4, float, 0)
-       ATTRIB(XonoticServerList, seenIPv6, float, 0)
-       ATTRIB(XonoticServerList, categoriesHeight, float, 1.25)
+       ATTRIB(XonoticServerList, sortButton1, entity);
+       ATTRIB(XonoticServerList, sortButton2, entity);
+       ATTRIB(XonoticServerList, sortButton3, entity);
+       ATTRIB(XonoticServerList, sortButton4, entity);
+       ATTRIB(XonoticServerList, sortButton5, entity);
+       ATTRIB(XonoticServerList, connectButton, entity);
+       ATTRIB(XonoticServerList, infoButton, entity);
+       ATTRIB(XonoticServerList, currentSortOrder, float, 0);
+       ATTRIB(XonoticServerList, currentSortField, float, -1);
+
+       ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1);
+
+       ATTRIB(XonoticServerList, seenIPv4, float, 0);
+       ATTRIB(XonoticServerList, seenIPv6, float, 0);
+       ATTRIB(XonoticServerList, categoriesHeight, float, 1.25);
 
        METHOD(XonoticServerList, getTotalHeight, float(entity));
        METHOD(XonoticServerList, getItemAtPos, float(entity, float));
@@ -154,8 +154,8 @@ int category_draw_count;
        SLIST_CATEGORY(CAT_SERVERS,      "CAT_NORMAL",  "CAT_SERVERS",  CTX(_("SLCAT^Servers"))) \
        SLIST_CATEGORY(CAT_XPM,          "CAT_NORMAL",  "CAT_SERVERS",  CTX(_("SLCAT^Competitive Mode"))) \
        SLIST_CATEGORY(CAT_MODIFIED,     "",            "CAT_SERVERS",  CTX(_("SLCAT^Modified Servers"))) \
-       SLIST_CATEGORY(CAT_OVERKILL,     "",            "CAT_SERVERS",  CTX(_("SLCAT^Overkill Mode"))) \
-       SLIST_CATEGORY(CAT_INSTAGIB,     "",            "CAT_SERVERS",  CTX(_("SLCAT^InstaGib Mode"))) \
+       SLIST_CATEGORY(CAT_OVERKILL,     "",            "CAT_SERVERS",  CTX(_("SLCAT^Overkill"))) \
+       SLIST_CATEGORY(CAT_INSTAGIB,     "",            "CAT_SERVERS",  CTX(_("SLCAT^InstaGib"))) \
        SLIST_CATEGORY(CAT_DEFRAG,       "",            "CAT_SERVERS",  CTX(_("SLCAT^Defrag Mode")))
 
 #define SLIST_CATEGORY_AUTOCVAR(name) autocvar_menu_slist_categories_##name##_override
index 616fc233dcdd6a9bac92d9f0c66ae628a022ac56..9ea9aa54c6d1a80afa31d5f215de8b89ba8c9fb0 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticSkinList, XonoticListBox)
        METHOD(XonoticSkinList, configureXonoticSkinList, void(entity));
-       ATTRIB(XonoticSkinList, rowsPerItem, float, 4)
+       ATTRIB(XonoticSkinList, rowsPerItem, float, 4);
        METHOD(XonoticSkinList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticSkinList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticSkinList, getSkins, void(entity));
@@ -15,18 +15,18 @@ CLASS(XonoticSkinList, XonoticListBox)
        METHOD(XonoticSkinList, keyDown, float(entity, float, float, float));
        METHOD(XonoticSkinList, destroy, void(entity));
 
-       ATTRIB(XonoticSkinList, skinlist, float, -1)
-       ATTRIB(XonoticSkinList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticSkinList, columnPreviewOrigin, float, 0)
-       ATTRIB(XonoticSkinList, columnPreviewSize, float, 0)
-       ATTRIB(XonoticSkinList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticSkinList, columnNameSize, float, 0)
-       ATTRIB(XonoticSkinList, realUpperMargin1, float, 0)
-       ATTRIB(XonoticSkinList, realUpperMargin2, float, 0)
-       ATTRIB(XonoticSkinList, origin, vector, '0 0 0')
-       ATTRIB(XonoticSkinList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticSkinList, skinlist, float, -1);
+       ATTRIB(XonoticSkinList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticSkinList, columnPreviewOrigin, float, 0);
+       ATTRIB(XonoticSkinList, columnPreviewSize, float, 0);
+       ATTRIB(XonoticSkinList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticSkinList, columnNameSize, float, 0);
+       ATTRIB(XonoticSkinList, realUpperMargin1, float, 0);
+       ATTRIB(XonoticSkinList, realUpperMargin2, float, 0);
+       ATTRIB(XonoticSkinList, origin, vector, '0 0 0');
+       ATTRIB(XonoticSkinList, itemAbsSize, vector, '0 0 0');
 
-       ATTRIB(XonoticSkinList, name, string, "skinselector")
+       ATTRIB(XonoticSkinList, name, string, "skinselector");
 ENDCLASS(XonoticSkinList)
 
 entity makeXonoticSkinList();
index 17287c42841cc3b8f164ad6ab0090a750517fc8e..9204c5eb35b501481d2be533c53c361b18b10245 100644 (file)
@@ -5,24 +5,24 @@ CLASS(XonoticSlider, Slider)
        METHOD(XonoticSlider, configureXonoticSlider, void(entity, float, float, float, string, string));
        METHOD(XonoticSlider, setValue, void(entity, float));
        METHOD(XonoticSlider, setValue_noAnim, void(entity, float));
-       ATTRIB(XonoticSlider, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT)
-       ATTRIB(XonoticSlider, image, string, SKINGFX_SLIDER)
-       ATTRIB(XonoticSlider, tolerance, vector, SKINTOLERANCE_SLIDER)
-       ATTRIB(XonoticSlider, align, float, 0.5)
-       ATTRIB(XonoticSlider, color, vector, SKINCOLOR_SLIDER_N)
-       ATTRIB(XonoticSlider, colorC, vector, SKINCOLOR_SLIDER_C)
-       ATTRIB(XonoticSlider, colorF, vector, SKINCOLOR_SLIDER_F)
-       ATTRIB(XonoticSlider, colorD, vector, SKINCOLOR_SLIDER_D)
-       ATTRIB(XonoticSlider, color2, vector, SKINCOLOR_SLIDER_S)
+       ATTRIB(XonoticSlider, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT);
+       ATTRIB(XonoticSlider, image, string, SKINGFX_SLIDER);
+       ATTRIB(XonoticSlider, tolerance, vector, SKINTOLERANCE_SLIDER);
+       ATTRIB(XonoticSlider, align, float, 0.5);
+       ATTRIB(XonoticSlider, color, vector, SKINCOLOR_SLIDER_N);
+       ATTRIB(XonoticSlider, colorC, vector, SKINCOLOR_SLIDER_C);
+       ATTRIB(XonoticSlider, colorF, vector, SKINCOLOR_SLIDER_F);
+       ATTRIB(XonoticSlider, colorD, vector, SKINCOLOR_SLIDER_D);
+       ATTRIB(XonoticSlider, color2, vector, SKINCOLOR_SLIDER_S);
 
-       ATTRIB(XonoticSlider, cvarName, string, string_null)
+       ATTRIB(XonoticSlider, cvarName, string);
        METHOD(XonoticSlider, loadCvars, void(entity));
        METHOD(XonoticSlider, saveCvars, void(entity));
-       ATTRIB(XonoticSlider, sendCvars, float, 0)
+       ATTRIB(XonoticSlider, sendCvars, float, 0);
 
-       ATTRIB(XonoticSlider, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticSlider, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticSlider, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticSlider, disabledAlpha, float, SKINALPHA_DISABLED);
 ENDCLASS(XonoticSlider)
 entity makeXonoticSlider_T(float, float, float, string, string theTooltip);
 entity makeXonoticSlider(float, float, float, string);
index 2b78141383d28579d157c5d583addf039427f90d..dcc2c85b06a1746bec59d3144dd7222e08cae1a9 100644 (file)
@@ -7,9 +7,9 @@ float toDecibelOfSquare(float f, float mi)
        {
                // linear scale part
                float t = 1 / A + mi;
-               float y = exp(1 + A * mi);
-               if(f <= y)
-                       return mi + (t - mi) * (f / y);
+               float u = exp(1 + A * mi);
+               if(f <= u)
+                       return mi + (t - mi) * (f / u);
        }
        return log(f) / A;
 }
@@ -21,9 +21,9 @@ float fromDecibelOfSquare(float f, float mi)
        {
                // linear scale part
                float t = 1 / A + mi;
-               float y = exp(1 + A * mi);
+               float u = exp(1 + A * mi);
                if(f <= t)
-                       return y * ((f - mi) / (t - mi));
+                       return u * ((f - mi) / (t - mi));
        }
        return exp(A * f);
 }
index d25ba994befebe50917885258f9e72905c49a407..3661fd4936e13281d41d3d2a3237ecf30b849455 100644 (file)
@@ -5,6 +5,6 @@ CLASS(XonoticPicmipSlider, XonoticTextSlider)
        METHOD(XonoticPicmipSlider, configureXonoticPicmipSlider, void(entity));
        METHOD(XonoticPicmipSlider, draw, void(entity));
        METHOD(XonoticPicmipSlider, autofix, void(entity));
-       ATTRIB(XonoticPicmipSlider, have_s3tc, float, 0)
+       ATTRIB(XonoticPicmipSlider, have_s3tc, float, 0);
 ENDCLASS(XonoticPicmipSlider)
 entity makeXonoticPicmipSlider();  // note: you still need to call addValue and configureXonoticTextSliderValues!
index 762b216f9b6801d9b4668a801737c15a2f84f36b..9a55f884030e25732d23cad43de801e4e6c22c13 100644 (file)
@@ -39,8 +39,8 @@ float updateConwidths(float width, float height, float pixelheight)
 
        minfactor = min(1, 640 / c.x);            // can be > 1 only if c_x is <640
        maxfactor = max(1, r.x / c.x, r.y / c.y); // can be < 1 only if r_x < c_x and r_y < c_y
-       LOG_TRACE("min factor: ", ftos(minfactor), "\n");
-       LOG_TRACE("max factor: ", ftos(maxfactor), "\n");
+       LOG_TRACE("min factor: ", ftos(minfactor));
+       LOG_TRACE("max factor: ", ftos(maxfactor));
 
        if(sz < 0)
                f = 1 - (maxfactor - 1) * sz;
@@ -103,10 +103,10 @@ void XonoticResolutionSlider_addResolution(entity me, float w, float h, float pi
                                bestdenom = denom;
                        }
                }
-               me.insertValue(me, i, strzone(sprintf(_("%dx%d (%d:%d)"), w, h, bestnum, bestdenom)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight))));
+               me.insertValue(me, i, strzone(sprintf("%dx%d (%d:%d)", w, h, bestnum, bestdenom)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight))));
        }
        else
-               me.insertValue(me, i, strzone(sprintf(_("%dx%d"), w, h)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight))));
+               me.insertValue(me, i, strzone(sprintf("%dx%d", w, h)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight))));
 }
 float autocvar_menu_vid_allowdualscreenresolution;
 void XonoticResolutionSlider_configureXonoticResolutionSlider(entity me)
@@ -149,7 +149,7 @@ void XonoticResolutionSlider_loadResolutions(entity me, float fullscreen)
                r = getresolution(-1);
                if(r.x != 0 || r.y != 0)
                        me.addResolution(me, r.x, r.y, r.z);
-               LOG_TRACE("Added system resolutions.\n");
+               LOG_TRACE("Added system resolutions.");
        }
 
        if(me.nValues == 0)
@@ -184,9 +184,9 @@ void XonoticResolutionSlider_loadResolutions(entity me, float fullscreen)
                me.addResolution(me, 1280, 960, 1); // pc res
                me.addResolution(me, 1280, 1024, 1); // pc res
                me.addResolution(me, 1920, 1080, 1); // 1080p
-               LOG_TRACE("Added default resolutions.\n");
+               LOG_TRACE("Added default resolutions.");
        }
-       LOG_TRACE("Total number of resolutions detected: ", ftos(me.nValues), "\n");
+       LOG_TRACE("Total number of resolutions detected: ", ftos(me.nValues));
 
        me.vid_fullscreen = fullscreen;
 
index 40b7bbbc246764e24f4b20a7fcb3d07f53aaa994..3b56c2a8fdb27c9511f7dc3856ba2dd0ae18abfd 100644 (file)
@@ -8,9 +8,9 @@ CLASS(XonoticResolutionSlider, XonoticTextSlider)
        METHOD(XonoticResolutionSlider, loadCvars, void(entity));
        METHOD(XonoticResolutionSlider, saveCvars, void(entity));
        METHOD(XonoticResolutionSlider, draw, void(entity));
-       ATTRIB(XonoticResolutionSlider, vid_fullscreen, float, -1)
-       ATTRIB(XonoticResolutionSlider, maxAllowedWidth, float, 0)
-       ATTRIB(XonoticResolutionSlider, maxAllowedHeight, float, 0)
+       ATTRIB(XonoticResolutionSlider, vid_fullscreen, float, -1);
+       ATTRIB(XonoticResolutionSlider, maxAllowedWidth, float, 0);
+       ATTRIB(XonoticResolutionSlider, maxAllowedHeight, float, 0);
 ENDCLASS(XonoticResolutionSlider)
 entity makeXonoticResolutionSlider();
 float updateConwidths(float width, float height, float pixelheight);
index 1e4bdbca21664d56fea25791cf0c3bf5161d1d53..048a42184c86c6b9ffadc3f0014414ca2dafc65d 100644 (file)
@@ -9,7 +9,7 @@ entity makeXonoticScoreboardFadeTimeSlider()
 }
 void XonoticScoreboardFadeTimeSlider_configureXonoticScoreboardFadeTimeSlider(entity me)
 {
-       me.configureXonoticTextSlider(me, "scoreboard_fadeinspeed", string_null);
+       me.configureXonoticTextSlider(me, "hud_panel_scoreboard_fadeinspeed", string_null);
        me.addValue(me, ZCTX(_("PART^Slow")),     "5 2.5");
        me.addValue(me, ZCTX(_("PART^Normal")),   "10 5");
        me.addValue(me, ZCTX(_("PART^Fast")),     "15 7.5");
@@ -19,8 +19,8 @@ void XonoticScoreboardFadeTimeSlider_configureXonoticScoreboardFadeTimeSlider(en
 void XonoticScoreboardFadeTimeSlider_loadCvars(entity me)
 {
        me.setValueFromIdentifier_noAnim(me, sprintf("%s %s",
-               cvar_string("scoreboard_fadeinspeed"),
-               cvar_string("scoreboard_fadeoutspeed")
+               cvar_string("hud_panel_scoreboard_fadeinspeed"),
+               cvar_string("hud_panel_scoreboard_fadeoutspeed")
        ));
 }
 void XonoticScoreboardFadeTimeSlider_saveCvars(entity me)
@@ -28,7 +28,7 @@ void XonoticScoreboardFadeTimeSlider_saveCvars(entity me)
        if(me.value >= 0 || me.value < me.nValues)
        {
                tokenize_console(me.getIdentifier(me));
-               cvar_set("scoreboard_fadeinspeed", argv(0));
-               cvar_set("scoreboard_fadeoutspeed", argv(1));
+               cvar_set("hud_panel_scoreboard_fadeinspeed", argv(0));
+               cvar_set("hud_panel_scoreboard_fadeoutspeed", argv(1));
        }
 }
index 86dc4c773f8f1afa62b3011ecc20c8e788cf5bbc..1e30baa11d773f9cf00617b0786f8ed3a4f51728 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticSoundList, XonoticListBox)
        METHOD(XonoticSoundList, configureXonoticSoundList, void(entity));
-       ATTRIB(XonoticSoundList, rowsPerItem, float, 1)
+       ATTRIB(XonoticSoundList, rowsPerItem, float, 1);
        METHOD(XonoticSoundList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticSoundList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticSoundList, getSounds, void(entity));
@@ -13,18 +13,18 @@ CLASS(XonoticSoundList, XonoticListBox)
        METHOD(XonoticSoundList, destroy, void(entity));
        METHOD(XonoticSoundList, showNotify, void(entity));
 
-       ATTRIB(XonoticSoundList, listSound, int, -1)
-       ATTRIB(XonoticSoundList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticSoundList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticSoundList, columnNameSize, float, 0)
-       ATTRIB(XonoticSoundList, columnNumberOrigin, float, 0)
-       ATTRIB(XonoticSoundList, columnNumberSize, float, 0)
-       ATTRIB(XonoticSoundList, realUpperMargin, float, 0)
-       ATTRIB(XonoticSoundList, origin, vector, '0 0 0')
-       ATTRIB(XonoticSoundList, itemAbsSize, vector, '0 0 0')
+       ATTRIB(XonoticSoundList, listSound, int, -1);
+       ATTRIB(XonoticSoundList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticSoundList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticSoundList, columnNameSize, float, 0);
+       ATTRIB(XonoticSoundList, columnNumberOrigin, float, 0);
+       ATTRIB(XonoticSoundList, columnNumberSize, float, 0);
+       ATTRIB(XonoticSoundList, realUpperMargin, float, 0);
+       ATTRIB(XonoticSoundList, origin, vector, '0 0 0');
+       ATTRIB(XonoticSoundList, itemAbsSize, vector, '0 0 0');
 
-       ATTRIB(XonoticSoundList, filterString, string, string_null)
-       ATTRIB(XonoticSoundList, playlist, entity, NULL)
+       ATTRIB(XonoticSoundList, filterString, string);
+       ATTRIB(XonoticSoundList, playlist, entity);
 ENDCLASS(XonoticSoundList)
 
 entity makeXonoticSoundList();
index 7fdbf9c1645dde038675f8787e947d8dbba794aa..3e94738a9ab500d89a162003be58ef67106e49f0 100644 (file)
@@ -51,7 +51,7 @@ string XonoticStatsList_convertDate(string input)
 
 void XonoticStatsList_getStats(entity me)
 {
-       LOG_TRACE("XonoticStatsList_getStats() at time: ", ftos(time), "\n");
+       LOG_TRACE("XonoticStatsList_getStats() at time: ", ftos(time));
        // delete the old buffer if it exists
        if(me.listStats >= 0)
                buf_del(me.listStats);
index ebef28ac0140a34a9d17813389b7deda95653f47..01fb93645bf6aeb92991805cb4d642e1b2bed5b1 100644 (file)
@@ -3,7 +3,7 @@
 #include "listbox.qh"
 CLASS(XonoticStatsList, XonoticListBox)
        METHOD(XonoticStatsList, configureXonoticStatsList, void(entity));
-       ATTRIB(XonoticStatsList, rowsPerItem, float, 1.4)
+       ATTRIB(XonoticStatsList, rowsPerItem, float, 1.4);
        METHOD(XonoticStatsList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticStatsList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticStatsList, getStats, void(entity));
@@ -11,13 +11,13 @@ CLASS(XonoticStatsList, XonoticListBox)
        METHOD(XonoticStatsList, keyDown, float(entity, float, float, float));
        METHOD(XonoticStatsList, destroy, void(entity));
        METHOD(XonoticStatsList, showNotify, void(entity));
-       ATTRIB(XonoticStatsList, selectionDoesntMatter, bool, true)
+       ATTRIB(XonoticStatsList, selectionDoesntMatter, bool, true);
 
-       ATTRIB(XonoticStatsList, listStats, float, -1)
-       ATTRIB(XonoticStatsList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticStatsList, realUpperMargin, float, 0)
-       ATTRIB(XonoticStatsList, columnNameOrigin, float, 0)
-       ATTRIB(XonoticStatsList, columnNameSize, float, 0)
+       ATTRIB(XonoticStatsList, listStats, float, -1);
+       ATTRIB(XonoticStatsList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticStatsList, realUpperMargin, float, 0);
+       ATTRIB(XonoticStatsList, columnNameOrigin, float, 0);
+       ATTRIB(XonoticStatsList, columnNameSize, float, 0);
 ENDCLASS(XonoticStatsList)
 
 entity statslist;  // for reference elsewhere
index b296d2491846c5f27ca20b45847397d631699b6f..ff561d57e68d139dcb404e97270e4c6befc9e277 100644 (file)
@@ -4,24 +4,24 @@
 CLASS(XonoticTab, Tab)
        // still to be customized by user
        /*
-       ATTRIB(XonoticTab, intendedWidth, float, 0)
-       ATTRIB(XonoticTab, rows, float, 3)
-       ATTRIB(XonoticTab, columns, float, 2)
+       ATTRIB(XonoticTab, intendedWidth, float, 0);
+       ATTRIB(XonoticTab, rows, float, 3);
+       ATTRIB(XonoticTab, columns, float, 2);
        */
        METHOD(XonoticTab, showNotify, void(entity));
 
-       ATTRIB(XonoticTab, marginTop, float, 0)                                       // pixels
-       ATTRIB(XonoticTab, marginBottom, float, 0)                                    // pixels
-       ATTRIB(XonoticTab, marginLeft, float, 0)                                      // pixels
-       ATTRIB(XonoticTab, marginRight, float, 0)                                     // pixels
-       ATTRIB(XonoticTab, columnSpacing, float, SKINMARGIN_COLUMNS)                  // pixels
-       ATTRIB(XonoticTab, rowSpacing, float, SKINMARGIN_ROWS)                        // pixels
-       ATTRIB(XonoticTab, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL) // pixels
+       ATTRIB(XonoticTab, marginTop, float, 0);                                       // pixels
+       ATTRIB(XonoticTab, marginBottom, float, 0);                                    // pixels
+       ATTRIB(XonoticTab, marginLeft, float, 0);                                      // pixels
+       ATTRIB(XonoticTab, marginRight, float, 0);                                     // pixels
+       ATTRIB(XonoticTab, columnSpacing, float, SKINMARGIN_COLUMNS);                  // pixels
+       ATTRIB(XonoticTab, rowSpacing, float, SKINMARGIN_ROWS);                        // pixels
+       ATTRIB(XonoticTab, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL); // pixels
 
-       ATTRIB(XonoticTab, backgroundImage, string, string_null)
+       ATTRIB(XonoticTab, backgroundImage, string);
 
        // using "titleTooltip" instead of "tooltip" so that
        // the tooltip search function doesn't find it
        // .tooltip should be set only in the item displaying the tab title
-       ATTRIB(XonoticTab, titleTooltip, string, string_null)
+       ATTRIB(XonoticTab, titleTooltip, string);
 ENDCLASS(XonoticTab)
index eb4d48f8d96efa436bb35d05e570e2f1e3723ee6..46d140eb08d8023c6450ad3b4a03c7e7892ff5fb 100644 (file)
@@ -5,8 +5,8 @@ CLASS(XonoticTabController, ModalController)
        METHOD(XonoticTabController, configureXonoticTabController, void(entity, float));
        METHOD(XonoticTabController, makeTabButton_T, entity(entity, string, entity, string));
        METHOD(XonoticTabController, makeTabButton, entity(entity, string, entity));
-       ATTRIB(XonoticTabController, rows, float, 0)
-       ATTRIB(XonoticTabController, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticTabController, image, string, SKINGFX_BUTTON)
+       ATTRIB(XonoticTabController, rows, float, 0);
+       ATTRIB(XonoticTabController, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticTabController, image, string, SKINGFX_BUTTON);
 ENDCLASS(XonoticTabController)
 entity makeXonoticTabController(float theRows);
index fde05de8a34bd6b0897c0f093f8668547cc5f2b5..94f7a74228ef45c58f5b9055241b9271bf9abba1 100644 (file)
@@ -4,9 +4,9 @@
 CLASS(XonoticTextLabel, Label)
        METHOD(XonoticTextLabel, configureXonoticTextLabel, void(entity, float, string));
        METHOD(XonoticTextLabel, draw, void(entity));
-       ATTRIB(XonoticTextLabel, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticTextLabel, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticTextLabel, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticTextLabel, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticTextLabel, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticTextLabel, disabledAlpha, float, SKINALPHA_DISABLED);
 ENDCLASS(XonoticTextLabel)
 entity makeXonoticTextLabel(float theAlign, string theText);
 entity makeXonoticHeaderLabel(string theText);
index ae81f36e87ba44e5e8d52f024ce33fe154abdf50..58fe8e86bbe4a0becaac10ea463fe493f50b2d72 100644 (file)
@@ -6,24 +6,24 @@ CLASS(XonoticTextSlider, TextSlider)
        METHOD(XonoticTextSlider, setValue, void(entity, float));
        METHOD(XonoticTextSlider, setValue_noAnim, void(entity, float));
        METHOD(XonoticTextSlider, configureXonoticTextSliderValues, void(entity));
-       ATTRIB(XonoticTextSlider, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticTextSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT)
-       ATTRIB(XonoticTextSlider, image, string, SKINGFX_SLIDER)
-       ATTRIB(XonoticTextSlider, tolerance, vector, SKINTOLERANCE_SLIDER)
-       ATTRIB(XonoticTextSlider, align, float, 0.5)
-       ATTRIB(XonoticTextSlider, color, vector, SKINCOLOR_SLIDER_N)
-       ATTRIB(XonoticTextSlider, colorC, vector, SKINCOLOR_SLIDER_C)
-       ATTRIB(XonoticTextSlider, colorF, vector, SKINCOLOR_SLIDER_F)
-       ATTRIB(XonoticTextSlider, colorD, vector, SKINCOLOR_SLIDER_D)
-       ATTRIB(XonoticTextSlider, color2, vector, SKINCOLOR_SLIDER_S)
+       ATTRIB(XonoticTextSlider, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticTextSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT);
+       ATTRIB(XonoticTextSlider, image, string, SKINGFX_SLIDER);
+       ATTRIB(XonoticTextSlider, tolerance, vector, SKINTOLERANCE_SLIDER);
+       ATTRIB(XonoticTextSlider, align, float, 0.5);
+       ATTRIB(XonoticTextSlider, color, vector, SKINCOLOR_SLIDER_N);
+       ATTRIB(XonoticTextSlider, colorC, vector, SKINCOLOR_SLIDER_C);
+       ATTRIB(XonoticTextSlider, colorF, vector, SKINCOLOR_SLIDER_F);
+       ATTRIB(XonoticTextSlider, colorD, vector, SKINCOLOR_SLIDER_D);
+       ATTRIB(XonoticTextSlider, color2, vector, SKINCOLOR_SLIDER_S);
 
-       ATTRIB(XonoticTextSlider, cvarName, string, string_null)
+       ATTRIB(XonoticTextSlider, cvarName, string);
        METHOD(XonoticTextSlider, loadCvars, void(entity));
        METHOD(XonoticTextSlider, saveCvars, void(entity));
-       ATTRIB(XonoticTextSlider, sendCvars, float, 0)
+       ATTRIB(XonoticTextSlider, sendCvars, float, 0);
 
-       ATTRIB(XonoticTextSlider, alpha, float, SKINALPHA_TEXT)
-       ATTRIB(XonoticTextSlider, disabledAlpha, float, SKINALPHA_DISABLED)
+       ATTRIB(XonoticTextSlider, alpha, float, SKINALPHA_TEXT);
+       ATTRIB(XonoticTextSlider, disabledAlpha, float, SKINALPHA_DISABLED);
 ENDCLASS(XonoticTextSlider)
 entity makeXonoticTextSlider_T(string, string theTooltip);
 entity makeXonoticTextSlider(string);  // note: you still need to call addValue and configureXonoticTextSliderValues!
index 4d704606d31275d4281b03e2fdb1f66c864bd6d4..e746bd725746a2a93a6e84812e8810b3cf74deac 100644 (file)
@@ -7,7 +7,7 @@
 #include <common/constants.qh>
 #include <common/mapinfo.qh>
 #include <common/util.qh>
-#include <common/command/generic.qh>
+#include <common/command/_mod.qh>
 
 float GL_CheckExtension(string ext)
 {
@@ -321,22 +321,22 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data)
 
        if(_Nex_ExtResponseSystem_UpdateTo)
        {
-               LOG_TRACE("error: UpdateNotification_URI_Get_Callback has been called before\n");
+               LOG_TRACE("error: UpdateNotification_URI_Get_Callback has been called before");
                return;
        }
        if(status != 0)
        {
-               LOG_TRACEF("error receiving update notification: status is %d\n", status);
+               LOG_TRACEF("error receiving update notification: status is %d", status);
                return;
        }
        if(substring(data, 0, 1) == "<")
        {
-               LOG_TRACE("error: received HTML instead of an update notification\n");
+               LOG_TRACE("error: received HTML instead of an update notification");
                return;
        }
        if(strstrofs(data, "\r", 0) != -1)
        {
-               LOG_TRACE("error: received carriage returns from update notification server\n");
+               LOG_TRACE("error: received carriage returns from update notification server");
                return;
        }
 
@@ -429,6 +429,12 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data)
                }
        }
 
+       if(un_bannedservers != "")
+       {
+               _Nex_ExtResponseSystem_BannedServers = strzone(un_bannedservers);
+               _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 1;
+       }
+
        if(un_emergency_pk3s != "")
        {
                _Nex_ExtResponseSystem_Packs = strzone(un_emergency_pk3s);
@@ -509,7 +515,7 @@ float preMenuInit()
 
        MapInfo_Cache_Create();
        MapInfo_Enumerate();
-       if(!MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
+       if(!_MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
        {
                draw_reset_cropped();
 
@@ -679,53 +685,57 @@ float updateCompression()
        GAMETYPE(MAPINFO_TYPE_NEXBALL) \
        GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
        GAMETYPE(MAPINFO_TYPE_ASSAULT) \
-       if (cvar("developer")) GAMETYPE(MAPINFO_TYPE_RACE) \
-       GAMETYPE(MAPINFO_TYPE_CTS) \
        /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
        /**/
 
-int GameType_GetID(int cnt)
+// hidden gametypes come last so indexing always works correctly
+#define HIDDEN_GAMETYPES \
+       GAMETYPE(MAPINFO_TYPE_RACE) \
+       GAMETYPE(MAPINFO_TYPE_CTS) \
+       /**/
+
+Gametype GameType_GetID(int cnt)
 {
        int i = 0;
-
-       #define GAMETYPE(id) { if (i++ == cnt) return id; }
+       #define GAMETYPE(it) { if (i++ == cnt) return it; }
        GAMETYPES
+       HIDDEN_GAMETYPES
        #undef GAMETYPE
-
-       unused_float = i;
-
-       return 0;
+       return NULL;
 }
 
 int GameType_GetCount()
 {
        int i = 0;
-
        #define GAMETYPE(id) ++i;
        GAMETYPES
        #undef GAMETYPE
+       #define GAMETYPE(it) if (cvar("developer")) ++i;
+       HIDDEN_GAMETYPES
+       #undef GAMETYPE
+       return i;
+}
 
+int GameType_GetTotalCount()
+{
+       int i = 0;
+       #define GAMETYPE(id) ++i;
+       GAMETYPES
+       HIDDEN_GAMETYPES
+       #undef GAMETYPE
        return i;
 }
 
 string GameType_GetName(int cnt)
 {
-       int i = GameType_GetID(cnt);
-
-       if(i)
-               return MapInfo_Type_ToText(i);
-
-       return "";
+       Gametype i = GameType_GetID(cnt);
+       return i ? MapInfo_Type_ToText(i) : "";
 }
 
 string GameType_GetIcon(int cnt)
 {
-       int i = GameType_GetID(cnt);
-
-       if(i)
-               return strcat("gametype_", MapInfo_Type_ToString(i));
-
-       return "";
+       Gametype i = GameType_GetID(cnt);
+       return i ? strcat("gametype_", MapInfo_Type_ToString(i)) : "";
 }
 
 .void(entity) TR;
index d433352cf5a1734e70429d8ed33e0faf0d11fc9f..96fef2ad4006f553a3bd8a5b22dd594b684df5c3 100644 (file)
@@ -29,11 +29,12 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data);
 
 // game type list box stuff (does not NEED to contain all game types, other
 // types stay available via console)
-int GameType_GetID(int cnt);
+entity GameType_GetID(int cnt);
 string GameType_GetName(int cnt);
 string GameType_GetIcon(int cnt);
 //string GameType_GetTeams(float cnt);
 int GameType_GetCount();
+int GameType_GetTotalCount();
 
 void dialog_hudpanel_common_notoggle(entity me, string panelname);
 #define DIALOG_HUDPANEL_COMMON_NOTOGGLE() \
index 0b8e214c8097916f2b58fa74e837a956a5920837..c3c155bb2b83dde97c56c552fb16759431aa38ab 100644 (file)
@@ -4,9 +4,9 @@
 CLASS(XonoticWeaponarenaCheckBox, CheckBox)
        METHOD(XonoticWeaponarenaCheckBox, configureXonoticWeaponarenaCheckBox, void(entity, string, string));
        METHOD(XonoticWeaponarenaCheckBox, setChecked, void(entity, float));
-       ATTRIB(XonoticWeaponarenaCheckBox, fontSize, float, SKINFONTSIZE_NORMAL)
-       ATTRIB(XonoticWeaponarenaCheckBox, image, string, SKINGFX_CHECKBOX)
-       ATTRIB(XonoticWeaponarenaCheckBox, netname, string, string_null)
+       ATTRIB(XonoticWeaponarenaCheckBox, fontSize, float, SKINFONTSIZE_NORMAL);
+       ATTRIB(XonoticWeaponarenaCheckBox, image, string, SKINGFX_CHECKBOX);
+       ATTRIB(XonoticWeaponarenaCheckBox, netname, string);
 
        METHOD(XonoticWeaponarenaCheckBox, loadCvars, void(entity));
        METHOD(XonoticWeaponarenaCheckBox, saveCvars, void(entity));
index ed74631dead1b7454724cbe937cb6db0bdf6b72c..7b3d7375b7760018cf4ab267e3f8d2daab6bc137 100644 (file)
@@ -1,6 +1,6 @@
 #include "weaponslist.qh"
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 .bool disabled;
 
index 953a40cc0dd39a9f921fd7b2162a62015fc66130..4726d8553dc1a765fbc03672cc4137180a5135c7 100644 (file)
@@ -4,16 +4,16 @@
 CLASS(XonoticWeaponsList, XonoticListBox)
        METHOD(XonoticWeaponsList, configureXonoticWeaponsList, void(entity));
        METHOD(XonoticWeaponsList, toString, string(entity));
-       ATTRIB(XonoticWeaponsList, rowsPerItem, float, 1)
+       ATTRIB(XonoticWeaponsList, rowsPerItem, float, 1);
        METHOD(XonoticWeaponsList, draw, void(entity));
        METHOD(XonoticWeaponsList, drawListBoxItem, void(entity, int, vector, bool, bool));
        METHOD(XonoticWeaponsList, resizeNotify, void(entity, vector, vector, vector, vector));
        METHOD(XonoticWeaponsList, keyDown, float(entity, float, float, float));
-       ATTRIB(XonoticWeaponsList, realFontSize, vector, '0 0 0')
-       ATTRIB(XonoticWeaponsList, realUpperMargin, float, 0)
+       ATTRIB(XonoticWeaponsList, realFontSize, vector, '0 0 0');
+       ATTRIB(XonoticWeaponsList, realUpperMargin, float, 0);
        METHOD(XonoticWeaponsList, mouseDrag, float(entity, vector));
 
-       ATTRIB(XonoticWeaponsList, applyButton, entity, NULL)
+       ATTRIB(XonoticWeaponsList, applyButton, entity);
 ENDCLASS(XonoticWeaponsList)
 entity makeXonoticWeaponsList();
 void WeaponsList_MoveUp_Click(entity btn, entity me);
index 1ec7115ef27c503b55cd6c723a2a14e97a77afba..1735e1c11335f4e672c7908de65bd12ca6a231cb 100644 (file)
@@ -7,7 +7,7 @@ float test();
 spawnfunc(worldspawn)
 {
        float r;
-       LOG_TRACE("TESTCASE: START\n");
+       LOG_TRACE("TESTCASE: START");
        r = test();
        if(r == 1)
                error("TESTCASE: PASS");
diff --git a/qcsrc/server/_all.inc b/qcsrc/server/_all.inc
new file mode 100644 (file)
index 0000000..95bf715
--- /dev/null
@@ -0,0 +1,19 @@
+#include <server/_all.qh>
+#include "_mod.inc"
+
+#include "bot/_mod.inc"
+#include "command/_mod.inc"
+#include "compat/_mod.inc"
+#include "mutators/_mod.inc"
+#include "pathlib/_mod.inc"
+#include "weapons/_mod.inc"
+
+#include <common/_all.inc>
+#include <common/effects/qc/all.qc>
+
+#include <lib/csqcmodel/sv_model.qc>
+
+#include <lib/warpzone/anglestransform.qc>
+#include <lib/warpzone/common.qc>
+#include <lib/warpzone/server.qc>
+#include <lib/warpzone/util_server.qc>
index a32f8b5832cf01e9ecccc534be2d794d6672118f..1fabe4d0f965aa9abd6b564b180ad4e83637a96d 100644 (file)
@@ -43,7 +43,38 @@ const string STR_OBSERVER = "observer";
 
 #define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), body)
 
-// NOTE: FOR_EACH_MONSTER deprecated! Use the following instead: FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { code; });
+// using the "inside out" version of knuth-fisher-yates shuffle
+// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
+entity _FCR_clients[255];
+bool _FCR_entered = false;
+#define FOREACH_CLIENT_RANDOM(cond, body) \
+       MACRO_BEGIN { \
+               if (_FCR_entered) LOG_FATAL("FOREACH_CLIENT_RANDOM must not be nested"); \
+               _FCR_entered = true; \
+               int _cnt = 0; \
+               FOREACH_CLIENT(cond, LAMBDA( \
+                       int _j = floor(random() * (_cnt + 1)); \
+                       if (_j == _cnt) \
+                       { \
+                               _FCR_clients[_cnt] = it; \
+                       } \
+                       else \
+                       { \
+                               _FCR_clients[_cnt] = _FCR_clients[_j]; \
+                               _FCR_clients[_j] = it; \
+                       } \
+                       _cnt++; \
+               )); \
+               for (int _i = 0; _i < _cnt; ++_i) \
+               { \
+                       const noref int i = _i; \
+                       ITER_CONST noref entity it = _FCR_clients[i]; \
+                       if (cond) { LAMBDA(body) } \
+               } \
+               _FCR_entered = false; \
+       } MACRO_END
+
+// NOTE: FOR_EACH_MONSTER deprecated! Use the following instead: IL_EACH(g_monsters, true, { code; });
 
 #include <common/effects/all.qh>
 #include <common/models/all.qh>
index f22742f1dda7292b497fc88aeead8ae7d905b997..eb74529a64daf066777af19f3f5b27faae7b38e8 100644 (file)
@@ -3,20 +3,20 @@
 #include <server/antilag.qc>
 #include <server/campaign.qc>
 #include <server/cheats.qc>
-#include <server/cl_client.qc>
-#include <server/cl_impulse.qc>
-#include <server/cl_player.qc>
+#include <server/client.qc>
 #include <server/g_damage.qc>
 #include <server/g_hook.qc>
 #include <server/g_lights.qc>
 #include <server/g_models.qc>
 #include <server/g_subs.qc>
 #include <server/g_world.qc>
+#include <server/impulse.qc>
 #include <server/ipban.qc>
 #include <server/item_key.qc>
 #include <server/mapvoting.qc>
 #include <server/matrix.qc>
 #include <server/miscfunctions.qc>
+#include <server/player.qc>
 #include <server/playerdemo.qc>
 #include <server/portals.qc>
 #include <server/race.qc>
@@ -25,9 +25,8 @@
 #include <server/scores_rules.qc>
 #include <server/spawnpoints.qc>
 #include <server/steerlib.qc>
-#include <server/sv_main.qc>
+#ifdef SVQC
+    #include <server/sv_main.qc>
+#endif
 #include <server/teamplay.qc>
 #include <server/tests.qc>
-#include <server/t_halflife.qc>
-#include <server/t_quake.qc>
-#include <server/t_quake3.qc>
index 17e0a829d4531587f92f1777d1aa546a276d83e6..a0d160d57c31194100a4951913721c7449da2da3 100644 (file)
@@ -3,20 +3,20 @@
 #include <server/antilag.qh>
 #include <server/campaign.qh>
 #include <server/cheats.qh>
-#include <server/cl_client.qh>
-#include <server/cl_impulse.qh>
-#include <server/cl_player.qh>
+#include <server/client.qh>
 #include <server/g_damage.qh>
 #include <server/g_hook.qh>
 #include <server/g_lights.qh>
 #include <server/g_models.qh>
 #include <server/g_subs.qh>
 #include <server/g_world.qh>
+#include <server/impulse.qh>
 #include <server/ipban.qh>
 #include <server/item_key.qh>
 #include <server/mapvoting.qh>
 #include <server/matrix.qh>
 #include <server/miscfunctions.qh>
+#include <server/player.qh>
 #include <server/playerdemo.qh>
 #include <server/portals.qh>
 #include <server/race.qh>
@@ -25,9 +25,8 @@
 #include <server/scores_rules.qh>
 #include <server/spawnpoints.qh>
 #include <server/steerlib.qh>
-#include <server/sv_main.qh>
+#ifdef SVQC
+    #include <server/sv_main.qh>
+#endif
 #include <server/teamplay.qh>
 #include <server/tests.qh>
-#include <server/t_halflife.qh>
-#include <server/t_quake.qh>
-#include <server/t_quake3.qh>
index 4861fe2a79c25cb75ca100e3ddc6b1545870d863..4e794cb3fc361e7796c98fb66d80b2daae4f4525 100644 (file)
@@ -6,6 +6,7 @@
 #include "miscfunctions.qh"
 
 #include "command/common.qh"
+#include <common/playerstats.qh>
 #include <common/state.qh>
 
 .float anticheat_jointime;
@@ -158,67 +159,66 @@ void anticheat_prethink(entity this)
        CS(this).anticheat_div0_evade_offset = 0;
 }
 
-string anticheat_display(float f, float tmin, float mi, float ma)
+string anticheat_display(float f, float t, float tmin, float mi, float ma)
 {
        string s;
        s = ftos(f);
-       if(f <= mi)
-               return strcat(s, ":N");
-       if(f >= ma)
-               return strcat(s, ":Y");
+       if (t >= tmin) {
+               if(f <= mi)
+                       return strcat(s, ":N");
+               if(f >= ma)
+                       return strcat(s, ":Y");
+       }
        return strcat(s, ":-");
 }
 
-void anticheat_report(entity this)
-{
+#define ANTICHEATS(ANTICHEAT) \
+       ANTICHEAT("speedhack", MEAN_EVALUATE(CS(this), anticheat_speedhack), 240, 0, 9999); /* Actually this one seems broken. */ \
+       ANTICHEAT("speedhack_m1", MEAN_EVALUATE(CS(this), anticheat_speedhack_m1), 240, 1.01, 1.25); \
+       ANTICHEAT("speedhack_m2", MEAN_EVALUATE(CS(this), anticheat_speedhack_m2), 240, 1.01, 1.25); \
+       ANTICHEAT("speedhack_m3", MEAN_EVALUATE(CS(this), anticheat_speedhack_m3), 240, 1.01, 1.25); \
+       ANTICHEAT("speedhack_m4", MEAN_EVALUATE(CS(this), anticheat_speedhack_m4), 240, 1.01, 1.25); \
+       ANTICHEAT("speedhack_m5", MEAN_EVALUATE(CS(this), anticheat_speedhack_m5), 240, 1.01, 1.25); \
+       ANTICHEAT("div0_strafebot_old", MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_old), 120, 0.15, 0.4); \
+       ANTICHEAT("div0_strafebot_new", MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_new), 120, 0.25, 0.8); \
+       ANTICHEAT("div0_evade", MEAN_EVALUATE(CS(this), anticheat_div0_evade), 120, 0.2, 0.5); \
+       ANTICHEAT("idle_snapaim", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal) - MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_signal", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_noise", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_m2", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m2), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_m3", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m3), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_m4", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m4), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_m7", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m7), 120, 0, 9999); \
+       ANTICHEAT("idle_snapaim_m10", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m10), 120, 0, 9999)
+
+void anticheat_report_to_eventlog(entity this) {
        if(!autocvar_sv_eventlog)
                return;
-       // TODO(divVerent): Use xonstat to acquire good thresholds.
        GameLogEcho(strcat(":anticheat:_time:", ftos(this.playerid), ":", ftos(servertime - CS(this).anticheat_jointime)));
-       GameLogEcho(strcat(":anticheat:speedhack:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack), 240, 0, 9999))); // Actually this one seems broken.
-       GameLogEcho(strcat(":anticheat:speedhack_m1:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m1), 240, 1.01, 1.25)));
-       GameLogEcho(strcat(":anticheat:speedhack_m2:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m2), 240, 1.01, 1.25)));
-       GameLogEcho(strcat(":anticheat:speedhack_m3:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m3), 240, 1.01, 1.25)));
-       GameLogEcho(strcat(":anticheat:speedhack_m4:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m4), 240, 1.01, 1.25)));
-       GameLogEcho(strcat(":anticheat:speedhack_m5:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m5), 240, 1.01, 1.25)));
-       GameLogEcho(strcat(":anticheat:div0_strafebot_old:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_old), 120, 0.15, 0.4)));
-       GameLogEcho(strcat(":anticheat:div0_strafebot_new:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_new), 120, 0.25, 0.8)));
-       GameLogEcho(strcat(":anticheat:div0_evade:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_div0_evade), 120, 0.2, 0.5)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal) - MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_signal:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_noise:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_m2:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m2), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_m3:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m3), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_m4:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m4), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_m7:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m7), 120, 0, 9999)));
-       GameLogEcho(strcat(":anticheat:idle_snapaim_m10:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m10), 120, 0, 9999)));
+#define ANTICHEAT_REPORT_ONE(name, f, tmin, mi, ma) \
+       GameLogEcho(strcat(":anticheat:", name, ":", anticheat_display(f, servertime - CS(this).anticheat_jointime, tmin, mi, ma)))
+       ANTICHEATS(ANTICHEAT_REPORT_ONE);
+#undef ANTICHEAT_REPORT_ONE
 }
 
-float anticheat_getvalue(entity this, string id)
-{
-       switch(id) {
-               case "_time": return servertime - CS(this).anticheat_jointime;
-               case "speedhack": return MEAN_EVALUATE(CS(this), anticheat_speedhack);
-               case "speedhack_m1": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m1);
-               case "speedhack_m2": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m2);
-               case "speedhack_m3": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m3);
-               case "speedhack_m4": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m4);
-               case "speedhack_m5": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m5);
-               case "div0_strafebot_old": return MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_old);
-               case "div0_strafebot_new": return MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_new);
-               case "div0_evade": return MEAN_EVALUATE(CS(this), anticheat_div0_evade);
-               case "idle_snapaim": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal) - MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise);
-               case "idle_snapaim_signal": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal);
-               case "idle_snapaim_noise": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise);
-               case "idle_snapaim_m2": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m2);
-               case "idle_snapaim_m3": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m3);
-               case "idle_snapaim_m4": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m4);
-               case "idle_snapaim_m7": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m7);
-               case "idle_snapaim_m10": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m10);
-       }
-       return -1;
+void anticheat_report_to_playerstats(entity this) {
+       PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, "_time"), servertime - CS(this).anticheat_jointime);
+#define ANTICHEAT_REPORT_ONE(name, f, tmin, mi, ma) \
+       PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, name), f)
+       ANTICHEATS(ANTICHEAT_REPORT_ONE);
+#undef ANTICHEAT_REPORT_ONE
 }
 
+void anticheat_register_to_playerstats() {
+       PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_ANTICHEAT, "_time"));
+#define ANTICHEAT_REGISTER_ONE(name, unused_f, unused_tmin, unused_mi, unused_ma) \
+       PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_ANTICHEAT, name))
+       ANTICHEATS(ANTICHEAT_REGISTER_ONE);
+#undef ANTICHEAT_REGISTER_ONE
+}
+
+#undef ANTICHEATS
+
 void anticheat_startframe()
 {
        anticheat_div0_evade_evasion_delta += frametime * (0.5 + random());
index 2b0522b991fb25747ce48c06003cf4433c74e30d..dd870dd397ceee549e4786adc25478f3fbb6e92e 100644 (file)
@@ -1,14 +1,14 @@
 #pragma once
 
 void anticheat_init(entity this);
-void anticheat_report(entity this);
+void anticheat_report_to_eventlog(entity this);
+void anticheat_report_to_playerstats(entity this);
+void anticheat_register_to_playerstats();
 
 void anticheat_physics(entity this);
 void anticheat_spectatecopy(entity this, entity spectatee);
 void anticheat_prethink(entity this);
 
-float anticheat_getvalue(entity this, string name);
-
 void anticheat_startframe();
 void anticheat_endframe();
 
index 314858780bf7f6917035c71a10c89f66e7bd7ecb..c4f77dfad38446d8e11fe91466db06e6d1319ae7 100644 (file)
@@ -5,61 +5,9 @@ bool autocvar__campaign_testrun;
 int autocvar__campaign_index;
 string autocvar__campaign_name;
 bool autocvar__sv_init;
-float autocvar_bot_ai_aimskill_blendrate;
-float autocvar_bot_ai_aimskill_firetolerance_distdegrees;
-float autocvar_bot_ai_aimskill_firetolerance_maxdegrees;
-float autocvar_bot_ai_aimskill_firetolerance_mindegrees;
-float autocvar_bot_ai_aimskill_fixedrate;
-float autocvar_bot_ai_aimskill_mouse;
-float autocvar_bot_ai_aimskill_offset;
-float autocvar_bot_ai_aimskill_order_filter_1st;
-float autocvar_bot_ai_aimskill_order_filter_2nd;
-float autocvar_bot_ai_aimskill_order_filter_3th;
-float autocvar_bot_ai_aimskill_order_filter_4th;
-float autocvar_bot_ai_aimskill_order_filter_5th;
-float autocvar_bot_ai_aimskill_order_mix_1st;
-float autocvar_bot_ai_aimskill_order_mix_2nd;
-float autocvar_bot_ai_aimskill_order_mix_3th;
-float autocvar_bot_ai_aimskill_order_mix_4th;
-float autocvar_bot_ai_aimskill_order_mix_5th;
-float autocvar_bot_ai_aimskill_think;
-float autocvar_bot_ai_bunnyhop_firstjumpdelay;
-float autocvar_bot_ai_bunnyhop_skilloffset;
-float autocvar_bot_ai_bunnyhop_startdistance;
-float autocvar_bot_ai_bunnyhop_stopdistance;
-float autocvar_bot_ai_chooseweaponinterval;
-string autocvar_bot_ai_custom_weapon_priority_close;
-string autocvar_bot_ai_custom_weapon_priority_distances;
-string autocvar_bot_ai_custom_weapon_priority_far;
-string autocvar_bot_ai_custom_weapon_priority_mid;
-float autocvar_bot_ai_dangerdetectioninterval;
-float autocvar_bot_ai_dangerdetectionupdates;
-float autocvar_bot_ai_enemydetectioninterval;
-float autocvar_bot_ai_enemydetectionradius;
-float autocvar_bot_ai_friends_aware_pickup_radius;
-float autocvar_bot_ai_ignoregoal_timeout;
-float autocvar_bot_ai_keyboard_distance;
-float autocvar_bot_ai_keyboard_threshold;
-float autocvar_bot_ai_navigation_jetpack;
-float autocvar_bot_ai_navigation_jetpack_mindistance;
 float autocvar_bot_ai_strategyinterval;
-float autocvar_bot_ai_thinkinterval;
-bool autocvar_bot_ai_weapon_combo;
-float autocvar_bot_ai_weapon_combo_threshold;
-string autocvar_bot_config_file;
-bool autocvar_bot_god;
-bool autocvar_bot_ignore_bots;
-bool autocvar_bot_join_empty;
-bool autocvar_bot_navigation_ignoreplayers;
-bool autocvar_bot_nofire;
 #define autocvar_bot_number cvar("bot_number")
-#define autocvar_bot_prefix cvar_string("bot_prefix")
-#define autocvar_bot_suffix cvar_string("bot_suffix")
-bool autocvar_bot_usemodelnames;
 int autocvar_bot_vs_human;
-bool autocvar_bot_debug_tracewalk;
-bool autocvar_bot_debug_goalstack;
-bool autocvar_bot_wander_enable;
 int autocvar_captureleadlimit_override;
 #define autocvar_capturelimit_override cvar("capturelimit_override")
 float autocvar_ekg;
@@ -144,6 +92,7 @@ float autocvar_g_balance_teams_scorefactor;
 float autocvar_g_ballistics_density_corpse;
 float autocvar_g_ballistics_density_player;
 float autocvar_g_ballistics_mindistance;
+bool autocvar_g_ballistics_penetrate_clips;
 float autocvar_g_ban_default_bantime;
 float autocvar_g_ban_default_masksize;
 float autocvar_g_ban_sync_interval;
@@ -172,7 +121,6 @@ float autocvar_g_chat_flood_spl_tell;
 int autocvar_g_chat_nospectators;
 bool autocvar_g_chat_teamcolors;
 bool autocvar_g_chat_tellprivacy;
-bool autocvar_g_debug_bot_commands;
 bool autocvar_g_forced_respawn;
 string autocvar_g_forced_team_blue;
 string autocvar_g_forced_team_otherwise;
@@ -197,6 +145,7 @@ float autocvar_g_jetpack_antigravity;
 int autocvar_g_jetpack_fuel;
 float autocvar_g_jetpack_maxspeed_side;
 float autocvar_g_jetpack_maxspeed_up;
+float autocvar_g_jetpack_reverse_thrust;
 #define autocvar_g_maplist cvar_string("g_maplist")
 bool autocvar_g_maplist_check_waypoints;
 int autocvar_g_maplist_index;
@@ -295,7 +244,6 @@ float autocvar_g_turrets_targetscan_maxdelay;
 float autocvar_g_turrets_targetscan_mindelay;
 bool autocvar_g_use_ammunition;
 bool autocvar_g_waypointeditor;
-int autocvar_g_waypointeditor_auto;
 bool autocvar_g_waypoints_for_items;
 #define autocvar_g_weapon_stay cvar("g_weapon_stay")
 bool autocvar_g_weapon_throwable;
@@ -320,7 +268,6 @@ int autocvar_rescan_pending;
 bool autocvar_samelevel;
 string autocvar_sessionid;
 #define autocvar_skill cvar("skill")
-float autocvar_skill_auto;
 #define autocvar_slowmo cvar("slowmo")
 float autocvar_snd_soundradius;
 int autocvar_spawn_debug;
@@ -356,7 +303,7 @@ float autocvar_sv_gameplayfix_q2airaccelerate;
 int autocvar_sv_gentle;
 #define autocvar_sv_gravity cvar("sv_gravity")
 string autocvar_sv_intermission_cdtrack;
-float autocvar_sv_itemstime;
+int autocvar_sv_itemstime;
 string autocvar_sv_jumpspeedcap_max;
 float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
 string autocvar_sv_jumpspeedcap_min;
@@ -428,7 +375,6 @@ float autocvar_timelimit_overtime;
 int autocvar_timelimit_overtimes;
 float autocvar_timelimit_suddendeath;
 #define autocvar_utf8_enable cvar("utf8_enable")
-bool autocvar_waypoint_benchmark;
 float autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
 bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag;
 float autocvar_g_trueaim_minrange;
@@ -460,7 +406,8 @@ float autocvar_g_monsters_respawn_delay;
 bool autocvar_g_monsters_respawn;
 float autocvar_g_monsters_armor_blockpercent;
 float autocvar_g_monsters_healthbars;
-float autocvar_g_monsters_lineofsight;
+bool autocvar_g_monsters_lineofsight = true;
+//bool autocvar_g_monsters_ignoretraces = true;
 #define autocvar_g_bloodloss cvar("g_bloodloss")
 bool autocvar_g_nades;
 bool autocvar_g_nades_override_dropweapon = true;
@@ -552,3 +499,26 @@ float autocvar_g_frozen_revive_falldamage;
 int autocvar_g_frozen_revive_falldamage_health;
 bool autocvar_g_frozen_damage_trigger;
 float autocvar_g_frozen_force;
+float autocvar_sv_airaccel_qw;
+float autocvar_sv_airstrafeaccel_qw;
+float autocvar_sv_airspeedlimit_nonqw;
+float autocvar_sv_airaccel_qw_stretchfactor;
+float autocvar_sv_maxairstrafespeed;
+float autocvar_sv_airstrafeaccelerate;
+float autocvar_sv_warsowbunny_turnaccel;
+float autocvar_sv_airaccel_sideways_friction;
+float autocvar_sv_aircontrol;
+float autocvar_sv_aircontrol_power;
+float autocvar_sv_aircontrol_backwards;
+float autocvar_sv_aircontrol_penalty;
+float autocvar_sv_warsowbunny_airforwardaccel;
+float autocvar_sv_warsowbunny_topspeed;
+float autocvar_sv_warsowbunny_accel;
+float autocvar_sv_warsowbunny_backtosideratio;
+float autocvar_sv_friction;
+float autocvar_sv_accelerate;
+float autocvar_sv_stopspeed;
+float autocvar_sv_airaccelerate;
+float autocvar_sv_airstopaccelerate;
+float autocvar_sv_track_canjump;
+bool autocvar_sv_showspectators;
index 787939110d9951f662439e926cd936a38b57dfb8..7c0c2882e990915ba7501a21511f2e75469eb62d 100644 (file)
@@ -1,6 +1,5 @@
 // generated file; do not modify
-#include <server/bot/aim.qc>
-#include <server/bot/bot.qc>
-#include <server/bot/navigation.qc>
-#include <server/bot/scripting.qc>
-#include <server/bot/waypoints.qc>
+#include <server/bot/api.qc>
+
+#include <server/bot/default/_mod.inc>
+#include <server/bot/null/_mod.inc>
index 802d39187818db60fd811789a83c585b1bcaae23..3678fc69f192bbf66634aa6c66b181dfa6a183e8 100644 (file)
@@ -1,6 +1,5 @@
 // generated file; do not modify
-#include <server/bot/aim.qh>
-#include <server/bot/bot.qh>
-#include <server/bot/navigation.qh>
-#include <server/bot/scripting.qh>
-#include <server/bot/waypoints.qh>
+#include <server/bot/api.qh>
+
+#include <server/bot/default/_mod.qh>
+#include <server/bot/null/_mod.qh>
diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc
deleted file mode 100644 (file)
index b2fca55..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-#include "aim.qh"
-
-#include "bot.qh"
-
-#include <common/physics/player.qh>
-#include <common/state.qh>
-
-#include "../weapons/weaponsystem.qh"
-
-#include "../mutators/all.qh"
-
-// traces multiple trajectories to find one that will impact the target
-// 'end' vector is the place it aims for,
-// returns true only if it hit targ (don't target non-solid entities)
-
-float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore)
-{
-       float c, savesolid, shottime;
-       vector dir, end, v, o;
-       if (shotspeed < 1)
-               return false; // could cause division by zero if calculated
-       if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER
-               return false; // could never hit it
-       if (!tracetossent)
-               tracetossent = new(tracetossent);
-       tracetossent.owner = ignore;
-       setsize(tracetossent, m1, m2);
-       savesolid = targ.solid;
-       targ.solid = SOLID_NOT;
-       o = (targ.absmin + targ.absmax) * 0.5;
-       shottime = ((vlen(o - org) / shotspeed) + shotdelay);
-       v = targ.velocity * shottime + o;
-       tracebox(o, targ.mins, targ.maxs, v, false, targ);
-       v = trace_endpos;
-       end = v + (targ.mins + targ.maxs) * 0.5;
-       if ((vlen(end - org) / shotspeed + 0.2) > maxtime)
-       {
-               // out of range
-               targ.solid = savesolid;
-               return false;
-       }
-
-       if (!tracetossfaketarget)
-               tracetossfaketarget = new(tracetossfaketarget);
-       tracetossfaketarget.solid = savesolid;
-       tracetossfaketarget.movetype = targ.movetype;
-       _setmodel(tracetossfaketarget, targ.model); // no low precision
-       tracetossfaketarget.model = targ.model;
-       tracetossfaketarget.modelindex = targ.modelindex;
-       setsize(tracetossfaketarget, targ.mins, targ.maxs);
-       setorigin(tracetossfaketarget, v);
-
-       c = 0;
-       dir = normalize(end - org);
-       while (c < 10) // 10 traces
-       {
-               setorigin(tracetossent, org); // reset
-               tracetossent.velocity = findtrajectory_velocity = normalize(dir) * shotspeed + shotspeedupward * '0 0 1';
-               tracetoss(tracetossent, ignore); // love builtin functions...
-               if (trace_ent == tracetossfaketarget) // done
-               {
-                       targ.solid = savesolid;
-
-                       // make it disappear
-                       tracetossfaketarget.solid = SOLID_NOT;
-                       tracetossfaketarget.movetype = MOVETYPE_NONE;
-                       tracetossfaketarget.model = "";
-                       tracetossfaketarget.modelindex = 0;
-                       // relink to remove it from physics considerations
-                       setorigin(tracetossfaketarget, v);
-
-                       return true;
-               }
-               dir.z = dir.z + 0.1; // aim up a little more
-               c = c + 1;
-       }
-       targ.solid = savesolid;
-
-       // make it disappear
-       tracetossfaketarget.solid = SOLID_NOT;
-       tracetossfaketarget.movetype = MOVETYPE_NONE;
-       tracetossfaketarget.model = "";
-       tracetossfaketarget.modelindex = 0;
-       // relink to remove it from physics considerations
-       setorigin(tracetossfaketarget, v);
-
-       // leave a valid one even if it won't reach
-       findtrajectory_velocity = normalize(end - org) * shotspeed + shotspeedupward * '0 0 1';
-       return false;
-}
-
-void lag_update(entity this)
-{
-       if (this.lag1_time) if (time > this.lag1_time) {this.lag_func(this, this.lag1_time, this.lag1_float1, this.lag1_float2, this.lag1_entity1, this.lag1_vec1, this.lag1_vec2, this.lag1_vec3, this.lag1_vec4);this.lag1_time = 0;}
-       if (this.lag2_time) if (time > this.lag2_time) {this.lag_func(this, this.lag2_time, this.lag2_float1, this.lag2_float2, this.lag2_entity1, this.lag2_vec1, this.lag2_vec2, this.lag2_vec3, this.lag2_vec4);this.lag2_time = 0;}
-       if (this.lag3_time) if (time > this.lag3_time) {this.lag_func(this, this.lag3_time, this.lag3_float1, this.lag3_float2, this.lag3_entity1, this.lag3_vec1, this.lag3_vec2, this.lag3_vec3, this.lag3_vec4);this.lag3_time = 0;}
-       if (this.lag4_time) if (time > this.lag4_time) {this.lag_func(this, this.lag4_time, this.lag4_float1, this.lag4_float2, this.lag4_entity1, this.lag4_vec1, this.lag4_vec2, this.lag4_vec3, this.lag4_vec4);this.lag4_time = 0;}
-       if (this.lag5_time) if (time > this.lag5_time) {this.lag_func(this, this.lag5_time, this.lag5_float1, this.lag5_float2, this.lag5_entity1, this.lag5_vec1, this.lag5_vec2, this.lag5_vec3, this.lag5_vec4);this.lag5_time = 0;}
-}
-
-float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
-{
-       if (this.lag1_time == 0) {this.lag1_time = t;this.lag1_float1 = f1;this.lag1_float2 = f2;this.lag1_entity1 = e1;this.lag1_vec1 = v1;this.lag1_vec2 = v2;this.lag1_vec3 = v3;this.lag1_vec4 = v4;return true;}
-       if (this.lag2_time == 0) {this.lag2_time = t;this.lag2_float1 = f1;this.lag2_float2 = f2;this.lag2_entity1 = e1;this.lag2_vec1 = v1;this.lag2_vec2 = v2;this.lag2_vec3 = v3;this.lag2_vec4 = v4;return true;}
-       if (this.lag3_time == 0) {this.lag3_time = t;this.lag3_float1 = f1;this.lag3_float2 = f2;this.lag3_entity1 = e1;this.lag3_vec1 = v1;this.lag3_vec2 = v2;this.lag3_vec3 = v3;this.lag3_vec4 = v4;return true;}
-       if (this.lag4_time == 0) {this.lag4_time = t;this.lag4_float1 = f1;this.lag4_float2 = f2;this.lag4_entity1 = e1;this.lag4_vec1 = v1;this.lag4_vec2 = v2;this.lag4_vec3 = v3;this.lag4_vec4 = v4;return true;}
-       if (this.lag5_time == 0) {this.lag5_time = t;this.lag5_float1 = f1;this.lag5_float2 = f2;this.lag5_entity1 = e1;this.lag5_vec1 = v1;this.lag5_vec2 = v2;this.lag5_vec3 = v3;this.lag5_vec4 = v4;return true;}
-       // no room for it (what is the best thing to do here??)
-       return false;
-}
-
-bool bot_shouldattack(entity this, entity targ)
-{
-       if (targ.team == this.team)
-       {
-               if (targ == this)
-                       return false;
-               if (teamplay)
-               if (targ.team != 0)
-                       return false;
-       }
-
-       if(STAT(FROZEN, targ))
-               return false;
-
-       if(teamplay)
-       {
-               if(targ.team==0)
-                       return false;
-       }
-       else if(bot_ignore_bots)
-               if(IS_BOT_CLIENT(targ))
-                       return false;
-
-       if (!targ.takedamage)
-               return false;
-       if (IS_DEAD(targ))
-               return false;
-       if (PHYS_INPUT_BUTTON_CHAT(targ))
-               return false;
-       if(targ.flags & FL_NOTARGET)
-               return false;
-
-       if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ))
-               return false;
-
-       return true;
-}
-
-void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
-{
-       if(this.flags & FL_INWATER)
-       {
-               this.bot_aimtarg = NULL;
-               return;
-       }
-       this.bot_aimtarg = e1;
-       this.bot_aimlatency = this.ping; // FIXME?  Shouldn't this be in the lag item?
-       //this.bot_aimorigin = v1;
-       //this.bot_aimvelocity = v2;
-       this.bot_aimtargorigin = v3;
-       this.bot_aimtargvelocity = v4;
-       if(skill <= 0)
-               this.bot_canfire = (random() < 0.8);
-       else if(skill <= 1)
-               this.bot_canfire = (random() < 0.9);
-       else if(skill <= 2)
-               this.bot_canfire = (random() < 0.95);
-       else
-               this.bot_canfire = 1;
-}
-
-float bot_aimdir(entity this, vector v, float maxfiredeviation)
-{
-       float dist, delta_t, blend;
-       vector desiredang, diffang;
-
-       //dprint("aim ", this.netname, ": old:", vtos(this.v_angle));
-       // make sure v_angle is sane first
-       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
-       this.v_angle_z = 0;
-
-       // get the desired angles to aim at
-       //dprint(" at:", vtos(v));
-       v = normalize(v);
-       //te_lightning2(NULL, this.origin + this.view_ofs, this.origin + this.view_ofs + v * 200);
-       if (time >= this.bot_badaimtime)
-       {
-               this.bot_badaimtime = max(this.bot_badaimtime + 0.3, time);
-               this.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+this.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
-       }
-       desiredang = vectoangles(v) + this.bot_badaimoffset;
-       //dprint(" desired:", vtos(desiredang));
-       if (desiredang.x >= 180)
-               desiredang.x = desiredang.x - 360;
-       desiredang.x = bound(-90, 0 - desiredang.x, 90);
-       desiredang.z = this.v_angle.z;
-       //dprint(" / ", vtos(desiredang));
-
-       //// pain throws off aim
-       //if (this.bot_painintensity)
-       //{
-       //      // shake from pain
-       //      desiredang = desiredang + randomvec() * this.bot_painintensity * 0.2;
-       //}
-
-       // calculate turn angles
-       diffang = (desiredang - this.bot_olddesiredang);
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       this.bot_olddesiredang = desiredang;
-       //dprint(" diff:", vtos(diffang));
-
-       delta_t = time-this.bot_prevaimtime;
-       this.bot_prevaimtime = time;
-       // Here we will try to anticipate the comming aiming direction
-       this.bot_1st_order_aimfilter= this.bot_1st_order_aimfilter
-               + (diffang * (1 / delta_t)    - this.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1);
-       this.bot_2nd_order_aimfilter= this.bot_2nd_order_aimfilter
-               + (this.bot_1st_order_aimfilter - this.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1);
-       this.bot_3th_order_aimfilter= this.bot_3th_order_aimfilter
-               + (this.bot_2nd_order_aimfilter - this.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1);
-       this.bot_4th_order_aimfilter= this.bot_4th_order_aimfilter
-               + (this.bot_3th_order_aimfilter - this.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1);
-       this.bot_5th_order_aimfilter= this.bot_5th_order_aimfilter
-               + (this.bot_4th_order_aimfilter - this.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1);
-
-       //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing !
-       blend = bound(0,skill+this.bot_aimskill,10)*0.1;
-       desiredang = desiredang + blend *
-       (
-                 this.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
-               + this.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd
-               + this.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th
-               + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th
-               + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th
-       );
-
-       // calculate turn angles
-       diffang = desiredang - this.bot_mouseaim;
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       //dprint(" diff:", vtos(diffang));
-
-       if (time >= this.bot_aimthinktime)
-       {
-               this.bot_aimthinktime = max(this.bot_aimthinktime + 0.5 - 0.05*(skill+this.bot_thinkskill), time);
-               this.bot_mouseaim = this.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+this.bot_thinkskill),10));
-       }
-
-       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
-
-       diffang = this.bot_mouseaim - desiredang;
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       desiredang = desiredang + diffang * bound(0,autocvar_bot_ai_aimskill_think,1);
-
-       // calculate turn angles
-       diffang = desiredang - this.v_angle;
-       // wrap yaw turn
-       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
-       if (diffang.y >= 180)
-               diffang.y = diffang.y - 360;
-       //dprint(" diff:", vtos(diffang));
-
-       // jitter tracking
-       dist = vlen(diffang);
-       //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3)));
-
-       // turn
-       float r, fixedrate, blendrate;
-       fixedrate = autocvar_bot_ai_aimskill_fixedrate / bound(1,dist,1000);
-       blendrate = autocvar_bot_ai_aimskill_blendrate;
-       r = max(fixedrate, blendrate);
-       //this.v_angle = this.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
-       this.v_angle = this.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+this.bot_mouseskill,3)*0.005-random()), 1);
-       this.v_angle = this.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1);
-       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
-       //this.v_angle = this.v_angle + diffang * (1/ blendrate);
-       this.v_angle_z = 0;
-       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
-       //dprint(" turn:", vtos(this.v_angle));
-
-       makevectors(this.v_angle);
-       shotorg = this.origin + this.view_ofs;
-       shotdir = v_forward;
-
-       //dprint(" dir:", vtos(v_forward));
-       //te_lightning2(NULL, shotorg, shotorg + shotdir * 100);
-
-       // calculate turn angles again
-       //diffang = desiredang - this.v_angle;
-       //diffang_y = diffang_y - floor(diffang_y / 360) * 360;
-       //if (diffang_y >= 180)
-       //      diffang_y = diffang_y - 360;
-
-       //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n");
-
-       // decide whether to fire this time
-       // note the maxfiredeviation is in degrees so this has to convert to radians first
-       //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
-       if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
-       if(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1))
-               this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5);
-       //traceline(shotorg,shotorg+shotdir*1000,false,NULL);
-       //dprint(ftos(maxfiredeviation),"\n");
-       //dprint(" diff:", vtos(diffang), "\n");
-
-       return this.bot_canfire && (time < this.bot_firetimer);
-}
-
-vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay)
-{
-       // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast...
-       return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed);
-}
-
-bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity)
-{
-       float f, r, hf, distanceratio;
-       vector v;
-       /*
-       eprint(this);
-       dprint("bot_aim(", ftos(shotspeed));
-       dprint(", ", ftos(shotspeedupward));
-       dprint(", ", ftos(maxshottime));
-       dprint(", ", ftos(applygravity));
-       dprint(");\n");
-       */
-
-       hf = this.dphitcontentsmask;
-       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-
-       shotspeed *= W_WeaponSpeedFactor(this);
-       shotspeedupward *= W_WeaponSpeedFactor(this);
-       if (!shotspeed)
-       {
-               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " shotspeed is zero!\n");
-               shotspeed = 1000000;
-       }
-       if (!maxshottime)
-       {
-               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " maxshottime is zero!\n");
-               maxshottime = 1;
-       }
-       makevectors(this.v_angle);
-       shotorg = this.origin + this.view_ofs;
-       shotdir = v_forward;
-       v = bot_shotlead(this.bot_aimtargorigin, this.bot_aimtargvelocity, shotspeed, this.bot_aimlatency);
-       distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees;
-       distanceratio = bound(0,distanceratio,1);
-       r =  (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees)
-               * (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees;
-       if (applygravity && this.bot_aimtarg)
-       {
-               if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', this.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, this))
-               {
-                       this.dphitcontentsmask = hf;
-                       return false;
-               }
-
-               f = bot_aimdir(this, findtrajectory_velocity - shotspeedupward * '0 0 1', r);
-       }
-       else
-       {
-               f = bot_aimdir(this, v - shotorg, r);
-               //dprint("AIM: ");dprint(vtos(this.bot_aimtargorigin));dprint(" + ");dprint(vtos(this.bot_aimtargvelocity));dprint(" * ");dprint(ftos(this.bot_aimlatency + vlen(this.bot_aimtargorigin - shotorg) / shotspeed));dprint(" = ");dprint(vtos(v));dprint(" : aimdir = ");dprint(vtos(normalize(v - shotorg)));dprint(" : ");dprint(vtos(shotdir));dprint("\n");
-               //traceline(shotorg, shotorg + shotdir * 10000, false, this);
-               //if (trace_ent.takedamage)
-               //if (trace_fraction < 1)
-               //if (!bot_shouldattack(this, trace_ent))
-               //      return false;
-               traceline(shotorg, this.bot_aimtargorigin, false, this);
-               if (trace_fraction < 1)
-               if (trace_ent != this.enemy)
-               if (!bot_shouldattack(this, trace_ent))
-               {
-                       this.dphitcontentsmask = hf;
-                       return false;
-               }
-       }
-
-       //if (r > maxshottime * shotspeed)
-       //      return false;
-       this.dphitcontentsmask = hf;
-       return true;
-}
diff --git a/qcsrc/server/bot/aim.qh b/qcsrc/server/bot/aim.qh
deleted file mode 100644 (file)
index dfe10e2..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-entity tracetossent;
-entity tracetossfaketarget;
-vector findtrajectory_velocity;
-
-
-
-vector shotorg;
-vector shotdir;
-
-// lag simulation
-// upto 5 queued messages
-.float lag1_time;
-.float lag1_float1;
-.float lag1_float2;
-.entity lag1_entity1;
-.vector lag1_vec1;
-.vector lag1_vec2;
-.vector lag1_vec3;
-.vector lag1_vec4;
-
-.float lag2_time;
-.float lag2_float1;
-.float lag2_float2;
-.entity lag2_entity1;
-.vector lag2_vec1;
-.vector lag2_vec2;
-.vector lag2_vec3;
-.vector lag2_vec4;
-
-.float lag3_time;
-.float lag3_float1;
-.float lag3_float2;
-.entity lag3_entity1;
-.vector lag3_vec1;
-.vector lag3_vec2;
-.vector lag3_vec3;
-.vector lag3_vec4;
-
-.float lag4_time;
-.float lag4_float1;
-.float lag4_float2;
-.entity lag4_entity1;
-.vector lag4_vec1;
-.vector lag4_vec2;
-.vector lag4_vec3;
-.vector lag4_vec4;
-
-.float lag5_time;
-.float lag5_float1;
-.float lag5_float2;
-.entity lag5_entity1;
-.vector lag5_vec1;
-.vector lag5_vec2;
-.vector lag5_vec3;
-.vector lag5_vec4;
-
-.float bot_badaimtime;
-.float bot_aimthinktime;
-.float bot_prevaimtime;
-.float bot_firetimer;
-.float bot_aimlatency;
-
-.vector bot_mouseaim;
-.vector bot_badaimoffset;
-.vector bot_1st_order_aimfilter;
-.vector bot_2nd_order_aimfilter;
-.vector bot_3th_order_aimfilter;
-.vector bot_4th_order_aimfilter;
-.vector bot_5th_order_aimfilter;
-.vector bot_olddesiredang;
-
-//.vector bot_aimorigin;
-//.vector bot_aimvelocity;
-.vector bot_aimtargorigin;
-.vector bot_aimtargvelocity;
-
-.entity bot_aimtarg;
-
-/*
- * Functions
- */
-
-float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
-void lag_update(entity this);
-void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
-
-float bot_shouldattack(entity this, entity targ);
-float bot_aimdir(entity this, vector v, float maxfiredeviation);
-bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity);
-float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore);
-
-vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay);
-
-.void(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func;
diff --git a/qcsrc/server/bot/api.qc b/qcsrc/server/bot/api.qc
new file mode 100644 (file)
index 0000000..274b034
--- /dev/null
@@ -0,0 +1 @@
+#include "api.qh"
diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh
new file mode 100644 (file)
index 0000000..dbe3197
--- /dev/null
@@ -0,0 +1,90 @@
+#pragma once
+
+#include <common/weapons/_all.qh>
+
+const int WAYPOINTFLAG_GENERATED = BIT(23);
+const int WAYPOINTFLAG_ITEM = BIT(22);
+const int WAYPOINTFLAG_TELEPORT = BIT(21);
+const int WAYPOINTFLAG_NORELINK = BIT(20);
+const int WAYPOINTFLAG_PERSONAL = BIT(19);
+const int WAYPOINTFLAG_PROTECTED = BIT(18);  // Useless WP detection never kills these.
+const int WAYPOINTFLAG_USEFUL = BIT(17);  // Useless WP detection temporary flag.
+const int WAYPOINTFLAG_DEAD_END = BIT(16);  // Useless WP detection temporary flag.
+
+entity kh_worldkeylist;
+.entity kh_worldkeynext;
+
+float bot_custom_weapon;
+float bot_weapons_close[Weapons_MAX];
+float bot_weapons_far[Weapons_MAX];
+float bot_weapons_mid[Weapons_MAX];
+float skill;
+
+.float bot_attack;
+.float bot_dodgerating;
+.float bot_dodge;
+.float bot_forced_team;
+.float bot_moveskill; // moving technique
+.float bot_pickup;
+.float(entity player, entity item) bot_pickupevalfunc;
+.float bot_strategytime;
+.string cleanname;
+.float havocbot_role_timeout;
+.float isbot; // true if this client is actually a bot
+.float lastteleporttime;
+.float navigation_hasgoals;
+.float nearestwaypointtimeout;
+.entity nearestwaypoint;
+.float speed;
+.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15;
+.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31;
+.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost;
+.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost;
+.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost;
+.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost;
+.float wpconsidered;
+.float wpcost;
+.int wpflags;
+
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity);
+void bot_clientconnect(entity this);
+void bot_clientdisconnect(entity this);
+void bot_cmdhelp(string scmd);
+void bot_endgame();
+bool bot_fixcount();
+void bot_list_commands();
+void bot_queuecommand(entity bot, string cmdstring);
+void bot_clear(entity this);
+void bot_relinkplayerlist();
+void bot_resetqueues();
+void bot_serverframe();
+bool bot_shouldattack(entity this, entity e);
+void bot_think(entity this);
+
+entity find_bot_by_name(string name);
+entity find_bot_by_number(float number);
+
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius);
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius);
+
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp);
+void navigation_goalrating_end(entity this);
+void navigation_goalrating_start(entity this);
+void navigation_markroutes(entity this, entity fixed_source_waypoint);
+void navigation_markroutes_inverted(entity fixed_source_waypoint);
+void navigation_routerating(entity this, entity e, float f, float rangebias);
+
+bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
+
+void waypoint_remove(entity e);
+void waypoint_saveall();
+void waypoint_schedulerelinkall();
+void waypoint_schedulerelink(entity wp);
+void waypoint_spawnforitem(entity e);
+void waypoint_spawnforitem_force(entity e, vector org);
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
+void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
+entity waypoint_spawn(vector m1, vector m2, float f);
+
+.entity goalcurrent;
+void navigation_clearroute(entity this);
diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc
deleted file mode 100644 (file)
index f9afb39..0000000
+++ /dev/null
@@ -1,712 +0,0 @@
-#include "bot.qh"
-
-#include "aim.qh"
-#include "navigation.qh"
-#include "scripting.qh"
-#include "waypoints.qh"
-
-#include "havocbot/havocbot.qh"
-#include "havocbot/scripting.qh"
-
-#include "../teamplay.qh"
-
-#include "../antilag.qh"
-#include "../autocvars.qh"
-#include "../campaign.qh"
-#include "../cl_client.qh"
-#include "../constants.qh"
-#include "../defs.qh"
-#include "../race.qh"
-#include <common/t_items.qh>
-
-#include "../mutators/all.qh"
-
-#include "../weapons/accuracy.qh"
-
-#include <common/physics/player.qh>
-#include <common/constants.qh>
-#include <common/mapinfo.qh>
-#include <common/teams.qh>
-#include <common/util.qh>
-
-#include <common/weapons/all.qh>
-
-#include <lib/csqcmodel/sv_model.qh>
-
-#include <lib/warpzone/common.qh>
-#include <lib/warpzone/util_server.qh>
-
-entity bot_spawn()
-{
-       entity bot = spawnclient();
-       if (bot)
-       {
-               currentbots = currentbots + 1;
-               bot_setnameandstuff(bot);
-               WITHSELF(bot, ClientConnect());
-               WITHSELF(bot, PutClientInServer());
-       }
-       return bot;
-}
-
-void bot_think(entity this)
-{
-       if (this.bot_nextthink > time)
-               return;
-
-       this.flags &= ~FL_GODMODE;
-       if(autocvar_bot_god)
-               this.flags |= FL_GODMODE;
-
-       this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
-       //if (this.bot_painintensity > 0)
-       //      this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
-
-       //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health;
-       //this.bot_painintensity = bound(0, this.bot_painintensity, 100);
-
-       if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
-       {
-               this.bot_nextthink = time + 0.5;
-               return;
-       }
-
-       if (this.fixangle)
-       {
-               this.v_angle = this.angles;
-               this.v_angle_z = 0;
-               this.fixangle = false;
-       }
-
-       this.dmg_take = 0;
-       this.dmg_save = 0;
-       this.dmg_inflictor = NULL;
-
-       // calculate an aiming latency based on the skill setting
-       // (simulated network latency + naturally delayed reflexes)
-       //this.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
-       // minimum ping 20+10 random
-       this.ping = bound(0,0.07 - bound(0, (skill + this.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
-       // skill 10 = ping 0.2 (adrenaline)
-       // skill 0 = ping 0.7 (slightly drunk)
-
-       // clear buttons
-       PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
-       PHYS_INPUT_BUTTON_ATCK2(this) = false;
-       PHYS_INPUT_BUTTON_ZOOM(this) = false;
-       PHYS_INPUT_BUTTON_CROUCH(this) = false;
-       PHYS_INPUT_BUTTON_HOOK(this) = false;
-       PHYS_INPUT_BUTTON_INFO(this) = false;
-       PHYS_INPUT_BUTTON_DRAG(this) = false;
-       PHYS_INPUT_BUTTON_CHAT(this) = false;
-       PHYS_INPUT_BUTTON_USE(this) = false;
-
-       if (time < game_starttime)
-       {
-               // block the bot during the countdown to game start
-               this.movement = '0 0 0';
-               this.bot_nextthink = game_starttime;
-               return;
-       }
-
-       // if dead, just wait until we can respawn
-       if (IS_DEAD(this))
-       {
-               if (this.deadflag == DEAD_DEAD)
-               {
-                       PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
-                       this.bot_strategytime = 0;
-               }
-       }
-       else if(this.aistatus & AI_STATUS_STUCK)
-               navigation_unstuck(this);
-
-       // now call the current bot AI (havocbot for example)
-       this.bot_ai(this);
-}
-
-void bot_setnameandstuff(entity this)
-{
-       string readfile, s;
-       float file, tokens, prio;
-
-       string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
-       string name, prefix, suffix;
-
-       if(autocvar_g_campaign)
-       {
-               prefix = "";
-               suffix = "";
-       }
-       else
-       {
-               prefix = autocvar_bot_prefix;
-               suffix = autocvar_bot_suffix;
-       }
-
-       file = fopen(autocvar_bot_config_file, FILE_READ);
-
-       if(file < 0)
-       {
-               LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n"));
-               readfile = "";
-       }
-       else
-       {
-               RandomSelection_Init();
-               while((readfile = fgets(file)))
-               {
-                       if(substring(readfile, 0, 2) == "//")
-                               continue;
-                       if(substring(readfile, 0, 1) == "#")
-                               continue;
-                       tokens = tokenizebyseparator(readfile, "\t");
-                       if(tokens == 0)
-                               continue;
-                       s = argv(0);
-                       prio = 1;
-                       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
-                               if(s == it.cleanname)
-                               {
-                                       prio = 0;
-                                       break;
-                               }
-                       ));
-                       RandomSelection_Add(NULL, 0, readfile, 1, prio);
-               }
-               readfile = RandomSelection_chosen_string;
-               fclose(file);
-       }
-
-       tokens = tokenizebyseparator(readfile, "\t");
-       if(argv(0) != "") bot_name = argv(0);
-       else bot_name = "Bot";
-
-       if(argv(1) != "") bot_model = argv(1);
-       else bot_model = "";
-
-       if(argv(2) != "") bot_skin = argv(2);
-       else bot_skin = "0";
-
-       if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3);
-       else bot_shirt = ftos(floor(random() * 15));
-
-       if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
-       else bot_pants = ftos(floor(random() * 15));
-
-       this.bot_forced_team = stof(argv(5));
-
-       prio = 6;
-
-       #define READSKILL(f,w,r) if(argv(prio) != "") this.f = stof(argv(prio)) * (w); else this.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio
-       //print(bot_name, ": ping=", argv(9), "\n");
-
-       READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
-       READSKILL(bot_moveskill, 2, 0); // move skill
-       READSKILL(bot_dodgeskill, 2, 0); // dodge skill
-
-       READSKILL(bot_pingskill, 0.5, 0); // ping skill
-
-       READSKILL(bot_weaponskill, 2, 0); // weapon skill
-       READSKILL(bot_aggresskill, 1, 0); // aggre skill
-       READSKILL(bot_rangepreference, 1, 0); // read skill
-
-       READSKILL(bot_aimskill, 2, 0); // aim skill
-       READSKILL(bot_offsetskill, 2, 0.5); // offset skill
-       READSKILL(bot_mouseskill, 1, 0.5); // mouse skill
-
-       READSKILL(bot_thinkskill, 1, 0.5); // think skill
-       READSKILL(bot_aiskill, 2, 0); // "ai" skill
-
-       this.bot_config_loaded = true;
-
-       // this is really only a default, JoinBestTeam is called later
-       setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
-       this.bot_preferredcolors = this.clientcolors;
-
-       // pick the name
-       if (autocvar_bot_usemodelnames)
-               name = bot_model;
-       else
-               name = bot_name;
-
-       // number bots with identical names
-       int j = 0;
-       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
-               if(it.cleanname == name)
-                       ++j;
-       ));
-       if (j)
-               this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
-       else
-               this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
-
-       this.cleanname = strzone(name);
-
-       // pick the model and skin
-       if(substring(bot_model, -4, 1) != ".")
-               bot_model = strcat(bot_model, ".iqm");
-       this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model));
-       this.playerskin = this.playerskin_freeme = strzone(bot_skin);
-
-       this.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the NULL
-       this.cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
-}
-
-void bot_custom_weapon_priority_setup()
-{
-       float tokens, i, w;
-
-       bot_custom_weapon = false;
-
-       if(     autocvar_bot_ai_custom_weapon_priority_far == "" ||
-               autocvar_bot_ai_custom_weapon_priority_mid == "" ||
-               autocvar_bot_ai_custom_weapon_priority_close == "" ||
-               autocvar_bot_ai_custom_weapon_priority_distances == ""
-       )
-               return;
-
-       // Parse distances
-       tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
-
-       if (tokens!=2)
-               return;
-
-       bot_distance_far = stof(argv(0));
-       bot_distance_close = stof(argv(1));
-
-       if(bot_distance_far < bot_distance_close){
-               bot_distance_far = stof(argv(1));
-               bot_distance_close = stof(argv(0));
-       }
-
-       // Initialize list of weapons
-       bot_weapons_far[0] = -1;
-       bot_weapons_mid[0] = -1;
-       bot_weapons_close[0] = -1;
-
-       // Parse far distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," ");
-
-       int c = 0;
-       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
-               w = stof(argv(i));
-               if ( w >= WEP_FIRST && w <= WEP_LAST) {
-                       bot_weapons_far[c] = w;
-                       ++c;
-               }
-       }
-       if(c < Weapons_COUNT)
-               bot_weapons_far[c] = -1;
-
-       // Parse mid distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," ");
-
-       c = 0;
-       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
-               w = stof(argv(i));
-               if ( w >= WEP_FIRST && w <= WEP_LAST) {
-                       bot_weapons_mid[c] = w;
-                       ++c;
-               }
-       }
-       if(c < Weapons_COUNT)
-               bot_weapons_mid[c] = -1;
-
-       // Parse close distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," ");
-
-       c = 0;
-       for(i=0; i < tokens && i < Weapons_COUNT; ++i){
-               w = stof(argv(i));
-               if ( w >= WEP_FIRST && w <= WEP_LAST) {
-                       bot_weapons_close[c] = w;
-                       ++c;
-               }
-       }
-       if(c < Weapons_COUNT)
-               bot_weapons_close[c] = -1;
-
-       bot_custom_weapon = true;
-}
-
-void bot_endgame()
-{
-       entity e;
-       //dprint("bot_endgame\n");
-       e = bot_list;
-       while (e)
-       {
-               setcolor(e, e.bot_preferredcolors);
-               e = e.nextbot;
-       }
-       // if dynamic waypoints are ever implemented, save them here
-}
-
-void bot_relinkplayerlist()
-{
-       entity e;
-       entity prevbot;
-       player_count = 0;
-       currentbots = 0;
-       player_list = e = findchainflags(flags, FL_CLIENT);
-       bot_list = NULL;
-       prevbot = NULL;
-       while (e)
-       {
-               player_count = player_count + 1;
-               e.nextplayer = e.chain;
-               if (IS_BOT_CLIENT(e))
-               {
-                       if (prevbot)
-                               prevbot.nextbot = e;
-                       else
-                       {
-                               bot_list = e;
-                               bot_list.nextbot = NULL;
-                       }
-                       prevbot = e;
-                       currentbots = currentbots + 1;
-               }
-               e = e.chain;
-       }
-       LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
-       bot_strategytoken = bot_list;
-       bot_strategytoken_taken = true;
-}
-
-void bot_clientdisconnect(entity this)
-{
-       if (!IS_BOT_CLIENT(this))
-               return;
-       bot_clearqueue(this);
-       if(this.cleanname)
-               strunzone(this.cleanname);
-       if(this.netname_freeme)
-               strunzone(this.netname_freeme);
-       if(this.playermodel_freeme)
-               strunzone(this.playermodel_freeme);
-       if(this.playerskin_freeme)
-               strunzone(this.playerskin_freeme);
-       this.cleanname = string_null;
-       this.netname_freeme = string_null;
-       this.playermodel_freeme = string_null;
-       this.playerskin_freeme = string_null;
-       if(this.bot_cmd_current)
-               remove(this.bot_cmd_current);
-       if(bot_waypoint_queue_owner==this)
-               bot_waypoint_queue_owner = NULL;
-}
-
-void bot_clientconnect(entity this)
-{
-       if (!IS_BOT_CLIENT(this)) return;
-       this.bot_preferredcolors = this.clientcolors;
-       this.bot_nextthink = time - random();
-       this.lag_func = bot_lagfunc;
-       this.isbot = true;
-       this.createdtime = this.bot_nextthink;
-
-       if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches
-               bot_setnameandstuff(this);
-
-       if(this.bot_forced_team==1)
-               this.team = NUM_TEAM_1;
-       else if(this.bot_forced_team==2)
-               this.team = NUM_TEAM_2;
-       else if(this.bot_forced_team==3)
-               this.team = NUM_TEAM_3;
-       else if(this.bot_forced_team==4)
-               this.team = NUM_TEAM_4;
-       else
-               JoinBestTeam(this, false, true);
-
-       havocbot_setupbot(this);
-}
-
-void bot_removefromlargestteam()
-{
-       float besttime, bestcount, thiscount;
-       entity best, head;
-       CheckAllowedTeams(NULL);
-       GetTeamCounts(NULL);
-       head = findchainfloat(isbot, true);
-       if (!head)
-               return;
-       best = head;
-       besttime = head.createdtime;
-       bestcount = 0;
-       while (head)
-       {
-               if(head.team == NUM_TEAM_1)
-                       thiscount = c1;
-               else if(head.team == NUM_TEAM_2)
-                       thiscount = c2;
-               else if(head.team == NUM_TEAM_3)
-                       thiscount = c3;
-               else if(head.team == NUM_TEAM_4)
-                       thiscount = c4;
-               else
-                       thiscount = 0;
-               if (thiscount > bestcount)
-               {
-                       bestcount = thiscount;
-                       besttime = head.createdtime;
-                       best = head;
-               }
-               else if (thiscount == bestcount && besttime < head.createdtime)
-               {
-                       besttime = head.createdtime;
-                       best = head;
-               }
-               head = head.chain;
-       }
-       currentbots = currentbots - 1;
-       dropclient(best);
-}
-
-void bot_removenewest()
-{
-       float besttime;
-       entity best, head;
-
-       if(teamplay)
-       {
-               bot_removefromlargestteam();
-               return;
-       }
-
-       head = findchainfloat(isbot, true);
-       if (!head)
-               return;
-       best = head;
-       besttime = head.createdtime;
-       while (head)
-       {
-               if (besttime < head.createdtime)
-               {
-                       besttime = head.createdtime;
-                       best = head;
-               }
-               head = head.chain;
-       }
-       currentbots = currentbots - 1;
-       dropclient(best);
-}
-
-void autoskill(float factor)
-{
-       float bestbot;
-       float bestplayer;
-
-       bestbot = -1;
-       bestplayer = -1;
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
-               if(IS_REAL_CLIENT(it))
-                       bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck);
-               else
-                       bestbot = max(bestbot, it.totalfrags - it.totalfrags_lastcheck);
-       ));
-
-       LOG_TRACE("autoskill: best player got ", ftos(bestplayer), ", ");
-       LOG_TRACE("best bot got ", ftos(bestbot), "; ");
-       if(bestbot < 0 || bestplayer < 0)
-       {
-               LOG_TRACE("not doing anything\n");
-               // don't return, let it reset all counters below
-       }
-       else if(bestbot <= bestplayer * factor - 2)
-       {
-               if(autocvar_skill < 17)
-               {
-                       LOG_TRACE("2 frags difference, increasing skill\n");
-                       cvar_set("skill", ftos(autocvar_skill + 1));
-                       bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
-               }
-       }
-       else if(bestbot >= bestplayer * factor + 2)
-       {
-               if(autocvar_skill > 0)
-               {
-                       LOG_TRACE("2 frags difference, decreasing skill\n");
-                       cvar_set("skill", ftos(autocvar_skill - 1));
-                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
-               }
-       }
-       else
-       {
-               LOG_TRACE("not doing anything\n");
-               return;
-               // don't reset counters, wait for them to accumulate
-       }
-
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags));
-}
-
-void bot_calculate_stepheightvec()
-{
-       stepheightvec = autocvar_sv_stepheight * '0 0 1';
-       jumpstepheightvec = stepheightvec +
-               ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85';
-               // 0.75 factor is for safety to make the jumps easy
-}
-
-float bot_fixcount()
-{
-       int activerealplayers = 0;
-       int realplayers = 0;
-       if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) {
-               activerealplayers = M_ARGV(0, int);
-               realplayers = M_ARGV(1, int);
-       } else {
-               FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
-                       if(IS_PLAYER(it))
-                               ++activerealplayers;
-                       ++realplayers;
-               ));
-       }
-
-       int bots;
-       // add/remove bots if needed to make sure there are at least
-       // minplayers+bot_number, or remove all bots if no one is playing
-       // But don't remove bots immediately on level change, as the real players
-       // usually haven't rejoined yet
-       bots_would_leave = false;
-       if (teamplay && autocvar_bot_vs_human && (c3==-1 && c4==-1))
-               bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
-       else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
-       {
-               float realminplayers, minplayers;
-               realminplayers = autocvar_minplayers;
-               minplayers = max(0, floor(realminplayers));
-
-               float realminbots, minbots;
-               realminbots = autocvar_bot_number;
-               minbots = max(0, floor(realminbots));
-
-               bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers);
-               if(bots > minbots)
-                       bots_would_leave = true;
-       }
-       else
-       {
-               // if there are no players, remove bots
-               bots = 0;
-       }
-
-       // only add one bot per frame to avoid utter chaos
-       if(time > botframe_nextthink)
-       {
-               //dprint(ftos(bots), " ? ", ftos(currentbots), "\n");
-               while (currentbots < bots)
-               {
-                       if (bot_spawn() == NULL)
-                       {
-                               bprint("Can not add bot, server full.\n");
-                               return false;
-                       }
-               }
-               while (currentbots > bots)
-                       bot_removenewest();
-       }
-
-       return true;
-}
-
-void bot_serverframe()
-{
-       if (intermission_running)
-               return;
-
-       if (time < 2)
-               return;
-
-       bot_calculate_stepheightvec();
-       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
-
-       if(time > autoskill_nextthink)
-       {
-               float a;
-               a = autocvar_skill_auto;
-               if(a)
-                       autoskill(a);
-               autoskill_nextthink = time + 5;
-       }
-
-       if(time > botframe_nextthink)
-       {
-               if(!bot_fixcount())
-                       botframe_nextthink = time + 10;
-       }
-
-       bot_ignore_bots = autocvar_bot_ignore_bots;
-
-       if(botframe_spawnedwaypoints)
-       {
-               if(autocvar_waypoint_benchmark)
-                       localcmd("quit\n");
-       }
-
-       if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto)
-       if (botframe_spawnedwaypoints)
-       {
-               if(botframe_cachedwaypointlinks)
-               {
-                       if(!botframe_loadedforcedlinks)
-                               waypoint_load_links_hardwired();
-               }
-               else
-               {
-                       // TODO: Make this check cleaner
-                       entity wp = findchain(classname, "waypoint");
-                       if(time - wp.nextthink > 10)
-                               waypoint_save_links();
-               }
-       }
-       else
-       {
-               botframe_spawnedwaypoints = true;
-               waypoint_loadall();
-               if(!waypoint_load_links())
-                       waypoint_schedulerelinkall();
-       }
-
-       if (bot_list)
-       {
-               // cycle the goal token from one bot to the next each frame
-               // (this prevents them from all doing spawnfunc_waypoint searches on the same
-               //  frame, which causes choppy framerates)
-               if (bot_strategytoken_taken)
-               {
-                       bot_strategytoken_taken = false;
-                       if (bot_strategytoken)
-                               bot_strategytoken = bot_strategytoken.nextbot;
-                       if (!bot_strategytoken)
-                               bot_strategytoken = bot_list;
-               }
-
-               if (botframe_nextdangertime < time)
-               {
-                       float interval;
-                       interval = autocvar_bot_ai_dangerdetectioninterval;
-                       if (botframe_nextdangertime < time - interval * 1.5)
-                               botframe_nextdangertime = time;
-                       botframe_nextdangertime = botframe_nextdangertime + interval;
-                       botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates);
-               }
-       }
-
-       if (autocvar_g_waypointeditor)
-               botframe_showwaypointlinks();
-
-       if (autocvar_g_waypointeditor_auto)
-               botframe_autowaypoints();
-
-       if(time > bot_cvar_nextthink)
-       {
-               if(currentbots>0)
-                       bot_custom_weapon_priority_setup();
-               bot_cvar_nextthink = time + 5;
-       }
-}
diff --git a/qcsrc/server/bot/bot.qh b/qcsrc/server/bot/bot.qh
deleted file mode 100644 (file)
index a28c94b..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-const int AI_STATUS_ROAMING                                            = BIT(0);       // Bot is just crawling the map. No enemies at sight
-const int AI_STATUS_ATTACKING                                  = BIT(1);       // There are enemies at sight
-const int AI_STATUS_RUNNING                                            = BIT(2);       // Bot is bunny hopping
-const int AI_STATUS_DANGER_AHEAD                               = BIT(3);       // There is lava/slime/trigger_hurt ahead
-const int AI_STATUS_OUT_JUMPPAD                                        = BIT(4);       // Trying to get out of a "vertical" jump pad
-const int AI_STATUS_OUT_WATER                                  = BIT(5);       // Trying to get out of water
-const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = BIT(6);       // Waiting for the personal waypoint to be linked
-const int AI_STATUS_WAYPOINT_PERSONAL_GOING            = BIT(7);       // Going to a personal waypoint
-const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = BIT(8);       // Personal waypoint reached
-const int AI_STATUS_JETPACK_FLYING                             = BIT(9);
-const int AI_STATUS_JETPACK_LANDING                            = BIT(10);
-const int AI_STATUS_STUCK                                              = BIT(11);  // Cannot reach any goal
-
-.float isbot; // true if this client is actually a bot
-.int aistatus;
-
-// Skill system
-float skill;
-float autoskill_nextthink;
-
-// havocbot_keyboardskill // keyboard movement
-.float bot_moveskill; // moving technique
-.float bot_dodgeskill; // dodging
-
-.float bot_pingskill; // ping offset
-
-.float bot_weaponskill; // weapon usage skill (combos, e.g.)
-.float bot_aggresskill; // aggressivity, controls "think before fire" behaviour
-.float bot_rangepreference; // weapon choice offset for range (>0 = prefer long range earlier "sniper", <0 = prefer short range "spammer")
-
-.float bot_aimskill; // aim accuracy
-.float bot_offsetskill; // aim breakage
-.float bot_mouseskill; // mouse "speed"
-
-.float bot_thinkskill; // target choice
-.float bot_aiskill; // strategy choice
-
-.float totalfrags_lastcheck;
-
-// Custom weapon priorities
-float bot_custom_weapon;
-float bot_distance_far;
-float bot_distance_close;
-
-float bot_weapons_far[Weapons_MAX];
-float bot_weapons_mid[Weapons_MAX];
-float bot_weapons_close[Weapons_MAX];
-
-entity bot_list;
-entity player_list;
-.entity nextbot;
-.entity nextplayer;
-.string cleanname;
-.string netname_freeme;
-.string playermodel_freeme;
-.string playerskin_freeme;
-
-.float bot_nextthink;
-
-.float createdtime;
-.float bot_preferredcolors;
-.float bot_attack;
-.float bot_dodge;
-.float bot_dodgerating;
-
-.float bot_pickup;
-.float bot_pickupbasevalue;
-.float bot_canfire;
-.float bot_strategytime;
-
-.float bot_forced_team;
-.float bot_config_loaded;
-
-float bot_strategytoken_taken;
-entity bot_strategytoken;
-
-float botframe_spawnedwaypoints;
-float botframe_nextthink;
-float botframe_nextdangertime;
-float bot_cvar_nextthink;
-float bot_ignore_bots; // let bots not attack other bots (only works in non-teamplay)
-
-/*
- * Functions
- */
-
-entity bot_spawn();
-float bot_fixcount();
-
-void bot_think(entity this);
-void bot_setnameandstuff(entity this);
-void bot_custom_weapon_priority_setup();
-void bot_endgame();
-void bot_relinkplayerlist();
-void bot_clientdisconnect(entity this);
-void bot_clientconnect(entity this);
-void bot_removefromlargestteam();
-void bot_removenewest();
-void autoskill(float factor);
-void bot_serverframe();
-
-.void(entity this) bot_ai;
-.float(entity player, entity item) bot_pickupevalfunc;
-
-/*
- * Imports
- */
-
-void(entity this) havocbot_setupbot;
-
-void bot_calculate_stepheightvec();
diff --git a/qcsrc/server/bot/default/_mod.inc b/qcsrc/server/bot/default/_mod.inc
new file mode 100644 (file)
index 0000000..03fb7e0
--- /dev/null
@@ -0,0 +1,9 @@
+// generated file; do not modify
+#include <server/bot/default/aim.qc>
+#include <server/bot/default/bot.qc>
+#include <server/bot/default/cvars.qc>
+#include <server/bot/default/navigation.qc>
+#include <server/bot/default/scripting.qc>
+#include <server/bot/default/waypoints.qc>
+
+#include <server/bot/default/havocbot/_mod.inc>
diff --git a/qcsrc/server/bot/default/_mod.qh b/qcsrc/server/bot/default/_mod.qh
new file mode 100644 (file)
index 0000000..0489627
--- /dev/null
@@ -0,0 +1,9 @@
+// generated file; do not modify
+#include <server/bot/default/aim.qh>
+#include <server/bot/default/bot.qh>
+#include <server/bot/default/cvars.qh>
+#include <server/bot/default/navigation.qh>
+#include <server/bot/default/scripting.qh>
+#include <server/bot/default/waypoints.qh>
+
+#include <server/bot/default/havocbot/_mod.qh>
diff --git a/qcsrc/server/bot/default/aim.qc b/qcsrc/server/bot/default/aim.qc
new file mode 100644 (file)
index 0000000..1624676
--- /dev/null
@@ -0,0 +1,395 @@
+#include "aim.qh"
+
+#include "cvars.qh"
+
+#include "bot.qh"
+
+#include <common/physics/player.qh>
+#include <common/state.qh>
+
+#include "../../weapons/weaponsystem.qh"
+
+#include "../../mutators/_mod.qh"
+
+// traces multiple trajectories to find one that will impact the target
+// 'end' vector is the place it aims for,
+// returns true only if it hit targ (don't target non-solid entities)
+
+float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore)
+{
+       float c, savesolid, shottime;
+       vector dir, end, v, o;
+       if (shotspeed < 1)
+               return false; // could cause division by zero if calculated
+       if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER
+               return false; // could never hit it
+       if (!tracetossent)
+               tracetossent = new(tracetossent);
+       tracetossent.owner = ignore;
+       setsize(tracetossent, m1, m2);
+       savesolid = targ.solid;
+       targ.solid = SOLID_NOT;
+       o = (targ.absmin + targ.absmax) * 0.5;
+       shottime = ((vlen(o - org) / shotspeed) + shotdelay);
+       v = targ.velocity * shottime + o;
+       tracebox(o, targ.mins, targ.maxs, v, false, targ);
+       v = trace_endpos;
+       end = v + (targ.mins + targ.maxs) * 0.5;
+       if ((vlen(end - org) / shotspeed + 0.2) > maxtime)
+       {
+               // out of range
+               targ.solid = savesolid;
+               return false;
+       }
+
+       if (!tracetossfaketarget)
+               tracetossfaketarget = new(tracetossfaketarget);
+       tracetossfaketarget.solid = savesolid;
+       set_movetype(tracetossfaketarget, targ.move_movetype);
+       _setmodel(tracetossfaketarget, targ.model); // no low precision
+       tracetossfaketarget.model = targ.model;
+       tracetossfaketarget.modelindex = targ.modelindex;
+       setsize(tracetossfaketarget, targ.mins, targ.maxs);
+       setorigin(tracetossfaketarget, v);
+
+       c = 0;
+       dir = normalize(end - org);
+       while (c < 10) // 10 traces
+       {
+               setorigin(tracetossent, org); // reset
+               tracetossent.velocity = findtrajectory_velocity = normalize(dir) * shotspeed + shotspeedupward * '0 0 1';
+               tracetoss(tracetossent, ignore); // love builtin functions...
+               if (trace_ent == tracetossfaketarget) // done
+               {
+                       targ.solid = savesolid;
+
+                       // make it disappear
+                       tracetossfaketarget.solid = SOLID_NOT;
+                       set_movetype(tracetossfaketarget, MOVETYPE_NONE);
+                       tracetossfaketarget.model = "";
+                       tracetossfaketarget.modelindex = 0;
+                       // relink to remove it from physics considerations
+                       setorigin(tracetossfaketarget, v);
+
+                       return true;
+               }
+               dir.z = dir.z + 0.1; // aim up a little more
+               c = c + 1;
+       }
+       targ.solid = savesolid;
+
+       // make it disappear
+       tracetossfaketarget.solid = SOLID_NOT;
+       set_movetype(tracetossfaketarget, MOVETYPE_NONE);
+       tracetossfaketarget.model = "";
+       tracetossfaketarget.modelindex = 0;
+       // relink to remove it from physics considerations
+       setorigin(tracetossfaketarget, v);
+
+       // leave a valid one even if it won't reach
+       findtrajectory_velocity = normalize(end - org) * shotspeed + shotspeedupward * '0 0 1';
+       return false;
+}
+
+void lag_update(entity this)
+{
+       if (this.lag1_time) if (time > this.lag1_time) {this.lag_func(this, this.lag1_time, this.lag1_float1, this.lag1_float2, this.lag1_entity1, this.lag1_vec1, this.lag1_vec2, this.lag1_vec3, this.lag1_vec4);this.lag1_time = 0;}
+       if (this.lag2_time) if (time > this.lag2_time) {this.lag_func(this, this.lag2_time, this.lag2_float1, this.lag2_float2, this.lag2_entity1, this.lag2_vec1, this.lag2_vec2, this.lag2_vec3, this.lag2_vec4);this.lag2_time = 0;}
+       if (this.lag3_time) if (time > this.lag3_time) {this.lag_func(this, this.lag3_time, this.lag3_float1, this.lag3_float2, this.lag3_entity1, this.lag3_vec1, this.lag3_vec2, this.lag3_vec3, this.lag3_vec4);this.lag3_time = 0;}
+       if (this.lag4_time) if (time > this.lag4_time) {this.lag_func(this, this.lag4_time, this.lag4_float1, this.lag4_float2, this.lag4_entity1, this.lag4_vec1, this.lag4_vec2, this.lag4_vec3, this.lag4_vec4);this.lag4_time = 0;}
+       if (this.lag5_time) if (time > this.lag5_time) {this.lag_func(this, this.lag5_time, this.lag5_float1, this.lag5_float2, this.lag5_entity1, this.lag5_vec1, this.lag5_vec2, this.lag5_vec3, this.lag5_vec4);this.lag5_time = 0;}
+}
+
+float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
+{
+       if (this.lag1_time == 0) {this.lag1_time = t;this.lag1_float1 = f1;this.lag1_float2 = f2;this.lag1_entity1 = e1;this.lag1_vec1 = v1;this.lag1_vec2 = v2;this.lag1_vec3 = v3;this.lag1_vec4 = v4;return true;}
+       if (this.lag2_time == 0) {this.lag2_time = t;this.lag2_float1 = f1;this.lag2_float2 = f2;this.lag2_entity1 = e1;this.lag2_vec1 = v1;this.lag2_vec2 = v2;this.lag2_vec3 = v3;this.lag2_vec4 = v4;return true;}
+       if (this.lag3_time == 0) {this.lag3_time = t;this.lag3_float1 = f1;this.lag3_float2 = f2;this.lag3_entity1 = e1;this.lag3_vec1 = v1;this.lag3_vec2 = v2;this.lag3_vec3 = v3;this.lag3_vec4 = v4;return true;}
+       if (this.lag4_time == 0) {this.lag4_time = t;this.lag4_float1 = f1;this.lag4_float2 = f2;this.lag4_entity1 = e1;this.lag4_vec1 = v1;this.lag4_vec2 = v2;this.lag4_vec3 = v3;this.lag4_vec4 = v4;return true;}
+       if (this.lag5_time == 0) {this.lag5_time = t;this.lag5_float1 = f1;this.lag5_float2 = f2;this.lag5_entity1 = e1;this.lag5_vec1 = v1;this.lag5_vec2 = v2;this.lag5_vec3 = v3;this.lag5_vec4 = v4;return true;}
+       // no room for it (what is the best thing to do here??)
+       return false;
+}
+
+bool bot_shouldattack(entity this, entity targ)
+{
+       if (targ.team == this.team)
+       {
+               if (targ == this)
+                       return false;
+               if (teamplay)
+               if (targ.team != 0)
+                       return false;
+       }
+
+       if(STAT(FROZEN, targ))
+               return false;
+
+       if(teamplay)
+       {
+               if(targ.team==0)
+                       return false;
+       }
+       else if(bot_ignore_bots)
+               if(IS_BOT_CLIENT(targ))
+                       return false;
+
+       if (!targ.takedamage)
+               return false;
+       if (IS_DEAD(targ))
+               return false;
+       if (PHYS_INPUT_BUTTON_CHAT(targ))
+               return false;
+       if(targ.flags & FL_NOTARGET)
+               return false;
+
+       if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ))
+               return false;
+
+       return true;
+}
+
+void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
+{
+       if(this.flags & FL_INWATER)
+       {
+               this.bot_aimtarg = NULL;
+               return;
+       }
+       this.bot_aimtarg = e1;
+       this.bot_aimlatency = this.ping; // FIXME?  Shouldn't this be in the lag item?
+       //this.bot_aimorigin = v1;
+       //this.bot_aimvelocity = v2;
+       this.bot_aimtargorigin = v3;
+       this.bot_aimtargvelocity = v4;
+       if(skill <= 0)
+               this.bot_canfire = (random() < 0.8);
+       else if(skill <= 1)
+               this.bot_canfire = (random() < 0.9);
+       else if(skill <= 2)
+               this.bot_canfire = (random() < 0.95);
+       else
+               this.bot_canfire = 1;
+}
+
+float bot_aimdir(entity this, vector v, float maxfiredeviation)
+{
+       float dist, delta_t, blend;
+       vector desiredang, diffang;
+
+       //dprint("aim ", this.netname, ": old:", vtos(this.v_angle));
+       // make sure v_angle is sane first
+       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
+       this.v_angle_z = 0;
+
+       // get the desired angles to aim at
+       //dprint(" at:", vtos(v));
+       v = normalize(v);
+       //te_lightning2(NULL, this.origin + this.view_ofs, this.origin + this.view_ofs + v * 200);
+       if (time >= this.bot_badaimtime)
+       {
+               this.bot_badaimtime = max(this.bot_badaimtime + 0.3, time);
+               this.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+this.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset;
+       }
+       desiredang = vectoangles(v) + this.bot_badaimoffset;
+       //dprint(" desired:", vtos(desiredang));
+       if (desiredang.x >= 180)
+               desiredang.x = desiredang.x - 360;
+       desiredang.x = bound(-90, 0 - desiredang.x, 90);
+       desiredang.z = this.v_angle.z;
+       //dprint(" / ", vtos(desiredang));
+
+       //// pain throws off aim
+       //if (this.bot_painintensity)
+       //{
+       //      // shake from pain
+       //      desiredang = desiredang + randomvec() * this.bot_painintensity * 0.2;
+       //}
+
+       // calculate turn angles
+       diffang = (desiredang - this.bot_olddesiredang);
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       this.bot_olddesiredang = desiredang;
+       //dprint(" diff:", vtos(diffang));
+
+       delta_t = time-this.bot_prevaimtime;
+       this.bot_prevaimtime = time;
+       // Here we will try to anticipate the comming aiming direction
+       this.bot_1st_order_aimfilter= this.bot_1st_order_aimfilter
+               + (diffang * (1 / delta_t)    - this.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1);
+       this.bot_2nd_order_aimfilter= this.bot_2nd_order_aimfilter
+               + (this.bot_1st_order_aimfilter - this.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1);
+       this.bot_3th_order_aimfilter= this.bot_3th_order_aimfilter
+               + (this.bot_2nd_order_aimfilter - this.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1);
+       this.bot_4th_order_aimfilter= this.bot_4th_order_aimfilter
+               + (this.bot_3th_order_aimfilter - this.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1);
+       this.bot_5th_order_aimfilter= this.bot_5th_order_aimfilter
+               + (this.bot_4th_order_aimfilter - this.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1);
+
+       //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing !
+       blend = bound(0,skill+this.bot_aimskill,10)*0.1;
+       desiredang = desiredang + blend *
+       (
+                 this.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st
+               + this.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd
+               + this.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th
+               + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th
+               + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th
+       );
+
+       // calculate turn angles
+       diffang = desiredang - this.bot_mouseaim;
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       //dprint(" diff:", vtos(diffang));
+
+       if (time >= this.bot_aimthinktime)
+       {
+               this.bot_aimthinktime = max(this.bot_aimthinktime + 0.5 - 0.05*(skill+this.bot_thinkskill), time);
+               this.bot_mouseaim = this.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+this.bot_thinkskill),10));
+       }
+
+       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+
+       diffang = this.bot_mouseaim - desiredang;
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       desiredang = desiredang + diffang * bound(0,autocvar_bot_ai_aimskill_think,1);
+
+       // calculate turn angles
+       diffang = desiredang - this.v_angle;
+       // wrap yaw turn
+       diffang.y = diffang.y - floor(diffang.y / 360) * 360;
+       if (diffang.y >= 180)
+               diffang.y = diffang.y - 360;
+       //dprint(" diff:", vtos(diffang));
+
+       // jitter tracking
+       dist = vlen(diffang);
+       //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3)));
+
+       // turn
+       float r, fixedrate, blendrate;
+       fixedrate = autocvar_bot_ai_aimskill_fixedrate / bound(1,dist,1000);
+       blendrate = autocvar_bot_ai_aimskill_blendrate;
+       r = max(fixedrate, blendrate);
+       //this.v_angle = this.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1);
+       this.v_angle = this.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+this.bot_mouseskill,3)*0.005-random()), 1);
+       this.v_angle = this.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1);
+       //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1);
+       //this.v_angle = this.v_angle + diffang * (1/ blendrate);
+       this.v_angle_z = 0;
+       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
+       //dprint(" turn:", vtos(this.v_angle));
+
+       makevectors(this.v_angle);
+       shotorg = this.origin + this.view_ofs;
+       shotdir = v_forward;
+
+       //dprint(" dir:", vtos(v_forward));
+       //te_lightning2(NULL, shotorg, shotorg + shotdir * 100);
+
+       // calculate turn angles again
+       //diffang = desiredang - this.v_angle;
+       //diffang_y = diffang_y - floor(diffang_y / 360) * 360;
+       //if (diffang_y >= 180)
+       //      diffang_y = diffang_y - 360;
+
+       //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n");
+
+       // decide whether to fire this time
+       // note the maxfiredeviation is in degrees so this has to convert to radians first
+       //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
+       if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180)))
+       if(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1))
+               this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5);
+       //traceline(shotorg,shotorg+shotdir*1000,false,NULL);
+       //dprint(ftos(maxfiredeviation),"\n");
+       //dprint(" diff:", vtos(diffang), "\n");
+
+       return this.bot_canfire && (time < this.bot_firetimer);
+}
+
+vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay)
+{
+       // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast...
+       return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed);
+}
+
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity)
+{
+       float f, r, hf, distanceratio;
+       vector v;
+       /*
+       eprint(this);
+       dprint("bot_aim(", ftos(shotspeed));
+       dprint(", ", ftos(shotspeedupward));
+       dprint(", ", ftos(maxshottime));
+       dprint(", ", ftos(applygravity));
+       dprint(");\n");
+       */
+
+       hf = this.dphitcontentsmask;
+       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+
+       shotspeed *= W_WeaponSpeedFactor(this);
+       shotspeedupward *= W_WeaponSpeedFactor(this);
+       if (!shotspeed)
+       {
+               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " shotspeed is zero!");
+               shotspeed = 1000000;
+       }
+       if (!maxshottime)
+       {
+               LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " maxshottime is zero!");
+               maxshottime = 1;
+       }
+       makevectors(this.v_angle);
+       shotorg = this.origin + this.view_ofs;
+       shotdir = v_forward;
+       v = bot_shotlead(this.bot_aimtargorigin, this.bot_aimtargvelocity, shotspeed, this.bot_aimlatency);
+       distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees;
+       distanceratio = bound(0,distanceratio,1);
+       r =  (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees)
+               * (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees;
+       if (applygravity && this.bot_aimtarg)
+       {
+               if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', this.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, this))
+               {
+                       this.dphitcontentsmask = hf;
+                       return false;
+               }
+
+               f = bot_aimdir(this, findtrajectory_velocity - shotspeedupward * '0 0 1', r);
+       }
+       else
+       {
+               f = bot_aimdir(this, v - shotorg, r);
+               //dprint("AIM: ");dprint(vtos(this.bot_aimtargorigin));dprint(" + ");dprint(vtos(this.bot_aimtargvelocity));dprint(" * ");dprint(ftos(this.bot_aimlatency + vlen(this.bot_aimtargorigin - shotorg) / shotspeed));dprint(" = ");dprint(vtos(v));dprint(" : aimdir = ");dprint(vtos(normalize(v - shotorg)));dprint(" : ");dprint(vtos(shotdir));dprint("\n");
+               //traceline(shotorg, shotorg + shotdir * 10000, false, this);
+               //if (trace_ent.takedamage)
+               //if (trace_fraction < 1)
+               //if (!bot_shouldattack(this, trace_ent))
+               //      return false;
+               traceline(shotorg, this.bot_aimtargorigin, false, this);
+               if (trace_fraction < 1)
+               if (trace_ent != this.enemy)
+               if (!bot_shouldattack(this, trace_ent))
+               {
+                       this.dphitcontentsmask = hf;
+                       return false;
+               }
+       }
+
+       //if (r > maxshottime * shotspeed)
+       //      return false;
+       this.dphitcontentsmask = hf;
+       return true;
+}
diff --git a/qcsrc/server/bot/default/aim.qh b/qcsrc/server/bot/default/aim.qh
new file mode 100644 (file)
index 0000000..dfe10e2
--- /dev/null
@@ -0,0 +1,99 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+entity tracetossent;
+entity tracetossfaketarget;
+vector findtrajectory_velocity;
+
+
+
+vector shotorg;
+vector shotdir;
+
+// lag simulation
+// upto 5 queued messages
+.float lag1_time;
+.float lag1_float1;
+.float lag1_float2;
+.entity lag1_entity1;
+.vector lag1_vec1;
+.vector lag1_vec2;
+.vector lag1_vec3;
+.vector lag1_vec4;
+
+.float lag2_time;
+.float lag2_float1;
+.float lag2_float2;
+.entity lag2_entity1;
+.vector lag2_vec1;
+.vector lag2_vec2;
+.vector lag2_vec3;
+.vector lag2_vec4;
+
+.float lag3_time;
+.float lag3_float1;
+.float lag3_float2;
+.entity lag3_entity1;
+.vector lag3_vec1;
+.vector lag3_vec2;
+.vector lag3_vec3;
+.vector lag3_vec4;
+
+.float lag4_time;
+.float lag4_float1;
+.float lag4_float2;
+.entity lag4_entity1;
+.vector lag4_vec1;
+.vector lag4_vec2;
+.vector lag4_vec3;
+.vector lag4_vec4;
+
+.float lag5_time;
+.float lag5_float1;
+.float lag5_float2;
+.entity lag5_entity1;
+.vector lag5_vec1;
+.vector lag5_vec2;
+.vector lag5_vec3;
+.vector lag5_vec4;
+
+.float bot_badaimtime;
+.float bot_aimthinktime;
+.float bot_prevaimtime;
+.float bot_firetimer;
+.float bot_aimlatency;
+
+.vector bot_mouseaim;
+.vector bot_badaimoffset;
+.vector bot_1st_order_aimfilter;
+.vector bot_2nd_order_aimfilter;
+.vector bot_3th_order_aimfilter;
+.vector bot_4th_order_aimfilter;
+.vector bot_5th_order_aimfilter;
+.vector bot_olddesiredang;
+
+//.vector bot_aimorigin;
+//.vector bot_aimvelocity;
+.vector bot_aimtargorigin;
+.vector bot_aimtargvelocity;
+
+.entity bot_aimtarg;
+
+/*
+ * Functions
+ */
+
+float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
+void lag_update(entity this);
+void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4);
+
+float bot_shouldattack(entity this, entity targ);
+float bot_aimdir(entity this, vector v, float maxfiredeviation);
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity);
+float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore);
+
+vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay);
+
+.void(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func;
diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc
new file mode 100644 (file)
index 0000000..fb6f1e0
--- /dev/null
@@ -0,0 +1,766 @@
+#include "bot.qh"
+
+#include "cvars.qh"
+
+#include "aim.qh"
+#include "navigation.qh"
+#include "scripting.qh"
+#include "waypoints.qh"
+
+#include "havocbot/havocbot.qh"
+#include "havocbot/scripting.qh"
+
+#include "../../teamplay.qh"
+
+#include "../../antilag.qh"
+#include "../../autocvars.qh"
+#include "../../campaign.qh"
+#include "../../client.qh"
+#include "../../constants.qh"
+#include "../../defs.qh"
+#include "../../race.qh"
+#include <common/t_items.qh>
+
+#include "../../mutators/_mod.qh"
+
+#include "../../weapons/accuracy.qh"
+
+#include <common/physics/player.qh>
+#include <common/constants.qh>
+#include <common/net_linked.qh>
+#include <common/mapinfo.qh>
+#include <common/teams.qh>
+#include <common/util.qh>
+
+#include <server/scores_rules.qh>
+
+#include <common/weapons/_all.qh>
+
+#include <lib/csqcmodel/sv_model.qh>
+
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+
+entity bot_spawn()
+{
+       entity bot = spawnclient();
+       if (bot)
+       {
+               currentbots = currentbots + 1;
+               bot_setnameandstuff(bot);
+               ClientConnect(bot);
+               PutClientInServer(bot);
+       }
+       return bot;
+}
+
+void bot_think(entity this)
+{
+       if (this.bot_nextthink > time)
+               return;
+
+       this.flags &= ~FL_GODMODE;
+       if(autocvar_bot_god)
+               this.flags |= FL_GODMODE;
+
+       this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
+       if(this.bot_nextthink < time)
+               this.bot_nextthink = time + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
+       //if (this.bot_painintensity > 0)
+       //      this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
+
+       //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health;
+       //this.bot_painintensity = bound(0, this.bot_painintensity, 100);
+
+       if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
+       {
+               this.movement = '0 0 0';
+               this.bot_nextthink = time + 0.5;
+               return;
+       }
+
+       if (this.fixangle)
+       {
+               this.v_angle = this.angles;
+               this.v_angle_z = 0;
+               this.fixangle = false;
+       }
+
+       this.dmg_take = 0;
+       this.dmg_save = 0;
+       this.dmg_inflictor = NULL;
+
+       // calculate an aiming latency based on the skill setting
+       // (simulated network latency + naturally delayed reflexes)
+       //this.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
+       // minimum ping 20+10 random
+       this.ping = bound(0,0.07 - bound(0, (skill + this.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
+       // skill 10 = ping 0.2 (adrenaline)
+       // skill 0 = ping 0.7 (slightly drunk)
+
+       // clear buttons
+       PHYS_INPUT_BUTTON_ATCK(this) = false;
+       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       PHYS_INPUT_BUTTON_ZOOM(this) = false;
+       PHYS_INPUT_BUTTON_CROUCH(this) = false;
+       PHYS_INPUT_BUTTON_HOOK(this) = false;
+       PHYS_INPUT_BUTTON_INFO(this) = false;
+       PHYS_INPUT_BUTTON_DRAG(this) = false;
+       PHYS_INPUT_BUTTON_CHAT(this) = false;
+       PHYS_INPUT_BUTTON_USE(this) = false;
+
+       if (time < game_starttime)
+       {
+               // block the bot during the countdown to game start
+               this.movement = '0 0 0';
+               this.bot_nextthink = game_starttime;
+               return;
+       }
+
+       // if dead, just wait until we can respawn
+       if (IS_DEAD(this))
+       {
+               this.movement = '0 0 0';
+               if (this.deadflag == DEAD_DEAD)
+               {
+                       PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
+                       this.bot_strategytime = 0;
+               }
+       }
+       else if(this.aistatus & AI_STATUS_STUCK)
+               navigation_unstuck(this);
+
+       // now call the current bot AI (havocbot for example)
+       this.bot_ai(this);
+}
+
+void bot_setnameandstuff(entity this)
+{
+       string readfile, s;
+       float file, tokens, prio;
+
+       string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
+       string name, prefix, suffix;
+
+       if(autocvar_g_campaign)
+       {
+               prefix = "";
+               suffix = "";
+       }
+       else
+       {
+               prefix = autocvar_bot_prefix;
+               suffix = autocvar_bot_suffix;
+       }
+
+       file = fopen(autocvar_bot_config_file, FILE_READ);
+
+       if(file < 0)
+       {
+               LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n"));
+               readfile = "";
+       }
+       else
+       {
+               RandomSelection_Init();
+               while((readfile = fgets(file)))
+               {
+                       if(substring(readfile, 0, 2) == "//")
+                               continue;
+                       if(substring(readfile, 0, 1) == "#")
+                               continue;
+                       tokens = tokenizebyseparator(readfile, "\t");
+                       if(tokens == 0)
+                               continue;
+                       s = argv(0);
+                       prio = 1;
+                       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
+                               if(s == it.cleanname)
+                               {
+                                       prio = 0;
+                                       break;
+                               }
+                       ));
+                       RandomSelection_AddString(readfile, 1, prio);
+               }
+               readfile = RandomSelection_chosen_string;
+               fclose(file);
+       }
+
+       tokens = tokenizebyseparator(readfile, "\t");
+       if(argv(0) != "") bot_name = argv(0);
+       else bot_name = "Bot";
+
+       if(argv(1) != "") bot_model = argv(1);
+       else bot_model = "";
+
+       if(argv(2) != "") bot_skin = argv(2);
+       else bot_skin = "0";
+
+       if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3);
+       else bot_shirt = ftos(floor(random() * 15));
+
+       if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
+       else bot_pants = ftos(floor(random() * 15));
+
+       this.bot_forced_team = stof(argv(5));
+
+       prio = 6;
+
+       #define READSKILL(f,w,r) if(argv(prio) != "") this.f = stof(argv(prio)) * (w); else this.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio
+       //print(bot_name, ": ping=", argv(9), "\n");
+
+       READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
+       READSKILL(bot_moveskill, 2, 0); // move skill
+       READSKILL(bot_dodgeskill, 2, 0); // dodge skill
+
+       READSKILL(bot_pingskill, 0.5, 0); // ping skill
+
+       READSKILL(bot_weaponskill, 2, 0); // weapon skill
+       READSKILL(bot_aggresskill, 1, 0); // aggre skill
+       READSKILL(bot_rangepreference, 1, 0); // read skill
+
+       READSKILL(bot_aimskill, 2, 0); // aim skill
+       READSKILL(bot_offsetskill, 2, 0.5); // offset skill
+       READSKILL(bot_mouseskill, 1, 0.5); // mouse skill
+
+       READSKILL(bot_thinkskill, 1, 0.5); // think skill
+       READSKILL(bot_aiskill, 2, 0); // "ai" skill
+
+       this.bot_config_loaded = true;
+
+       // this is really only a default, JoinBestTeam is called later
+       setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
+       this.bot_preferredcolors = this.clientcolors;
+
+       // pick the name
+       if (autocvar_bot_usemodelnames)
+               name = bot_model;
+       else
+               name = bot_name;
+
+       // number bots with identical names
+       int j = 0;
+       FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA(
+               if(it.cleanname == name)
+                       ++j;
+       ));
+       if (j)
+               this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
+       else
+               this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
+
+       this.cleanname = strzone(name);
+
+       // pick the model and skin
+       if(substring(bot_model, -4, 1) != ".")
+               bot_model = strcat(bot_model, ".iqm");
+       this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model));
+       this.playerskin = this.playerskin_freeme = strzone(bot_skin);
+
+       this.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the NULL
+       this.cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
+}
+
+void bot_custom_weapon_priority_setup()
+{
+       float tokens, i, w;
+
+       bot_custom_weapon = false;
+
+       if(     autocvar_bot_ai_custom_weapon_priority_far == "" ||
+               autocvar_bot_ai_custom_weapon_priority_mid == "" ||
+               autocvar_bot_ai_custom_weapon_priority_close == "" ||
+               autocvar_bot_ai_custom_weapon_priority_distances == ""
+       )
+               return;
+
+       // Parse distances
+       tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
+
+       if (tokens!=2)
+               return;
+
+       bot_distance_far = stof(argv(0));
+       bot_distance_close = stof(argv(1));
+
+       if(bot_distance_far < bot_distance_close){
+               bot_distance_far = stof(argv(1));
+               bot_distance_close = stof(argv(0));
+       }
+
+       // Initialize list of weapons
+       bot_weapons_far[0] = -1;
+       bot_weapons_mid[0] = -1;
+       bot_weapons_close[0] = -1;
+
+       // Parse far distance weapon priorities
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," ");
+
+       int c = 0;
+       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
+               w = stof(argv(i));
+               if ( w >= WEP_FIRST && w <= WEP_LAST) {
+                       bot_weapons_far[c] = w;
+                       ++c;
+               }
+       }
+       if(c < Weapons_COUNT)
+               bot_weapons_far[c] = -1;
+
+       // Parse mid distance weapon priorities
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," ");
+
+       c = 0;
+       for(i=0; i < tokens && c < Weapons_COUNT; ++i){
+               w = stof(argv(i));
+               if ( w >= WEP_FIRST && w <= WEP_LAST) {
+                       bot_weapons_mid[c] = w;
+                       ++c;
+               }
+       }
+       if(c < Weapons_COUNT)
+               bot_weapons_mid[c] = -1;
+
+       // Parse close distance weapon priorities
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," ");
+
+       c = 0;
+       for(i=0; i < tokens && i < Weapons_COUNT; ++i){
+               w = stof(argv(i));
+               if ( w >= WEP_FIRST && w <= WEP_LAST) {
+                       bot_weapons_close[c] = w;
+                       ++c;
+               }
+       }
+       if(c < Weapons_COUNT)
+               bot_weapons_close[c] = -1;
+
+       bot_custom_weapon = true;
+}
+
+void bot_endgame()
+{
+       bot_relinkplayerlist();
+       entity e = bot_list;
+       while (e)
+       {
+               setcolor(e, e.bot_preferredcolors);
+               e = e.nextbot;
+       }
+       // if dynamic waypoints are ever implemented, save them here
+}
+
+void bot_relinkplayerlist()
+{
+       player_count = 0;
+       currentbots = 0;
+       bot_list = NULL;
+
+       entity prevbot = NULL;
+       FOREACH_CLIENT(true,
+       {
+               ++player_count;
+
+               if(IS_BOT_CLIENT(it))
+               {
+                       if(prevbot)
+                               prevbot.nextbot = it;
+                       else
+                       {
+                               bot_list = it;
+                               bot_list.nextbot = NULL;
+                       }
+                       prevbot = it;
+                       ++currentbots;
+               }
+       });
+       LOG_TRACE("relink: ", ftos(currentbots), " bots seen.");
+       bot_strategytoken = bot_list;
+       bot_strategytoken_taken = true;
+}
+
+void bot_clientdisconnect(entity this)
+{
+       if (!IS_BOT_CLIENT(this))
+               return;
+       bot_clearqueue(this);
+       if(this.cleanname)
+               strunzone(this.cleanname);
+       if(this.netname_freeme)
+               strunzone(this.netname_freeme);
+       if(this.playermodel_freeme)
+               strunzone(this.playermodel_freeme);
+       if(this.playerskin_freeme)
+               strunzone(this.playerskin_freeme);
+       this.cleanname = string_null;
+       this.netname_freeme = string_null;
+       this.playermodel_freeme = string_null;
+       this.playerskin_freeme = string_null;
+       if(this.bot_cmd_current)
+               delete(this.bot_cmd_current);
+       if(bot_waypoint_queue_owner == this)
+               bot_waypoint_queue_owner = NULL;
+}
+
+void bot_clientconnect(entity this)
+{
+       if (!IS_BOT_CLIENT(this)) return;
+       this.bot_preferredcolors = this.clientcolors;
+       this.bot_nextthink = time - random();
+       this.lag_func = bot_lagfunc;
+       this.isbot = true;
+       this.createdtime = this.bot_nextthink;
+
+       if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches
+               bot_setnameandstuff(this);
+
+       if(this.bot_forced_team==1)
+               this.team = NUM_TEAM_1;
+       else if(this.bot_forced_team==2)
+               this.team = NUM_TEAM_2;
+       else if(this.bot_forced_team==3)
+               this.team = NUM_TEAM_3;
+       else if(this.bot_forced_team==4)
+               this.team = NUM_TEAM_4;
+       else
+               JoinBestTeam(this, false, true);
+
+       havocbot_setupbot(this);
+}
+
+void bot_removefromlargestteam()
+{
+       CheckAllowedTeams(NULL);
+       GetTeamCounts(NULL);
+
+       entity best = NULL;
+       float besttime = 0;
+       int bestcount = 0;
+
+       int bcount = 0;
+       FOREACH_CLIENT(it.isbot,
+       {
+               ++bcount;
+
+               if(!best)
+               {
+                       best = it;
+                       besttime = it.createdtime;
+               }
+
+               int thiscount = 0;
+
+               switch(it.team)
+               {
+                       case NUM_TEAM_1: thiscount = c1; break;
+                       case NUM_TEAM_2: thiscount = c2; break;
+                       case NUM_TEAM_3: thiscount = c3; break;
+                       case NUM_TEAM_4: thiscount = c4; break;
+               }
+
+               if(thiscount > bestcount)
+               {
+                       bestcount = thiscount;
+                       besttime = it.createdtime;
+                       best = it;
+               }
+               else if(thiscount == bestcount && besttime < it.createdtime)
+               {
+                       besttime = it.createdtime;
+                       best = it;
+               }
+       });
+       if(!bcount)
+               return; // no bots to remove
+       currentbots = currentbots - 1;
+       dropclient(best);
+}
+
+void bot_removenewest()
+{
+       if(teamplay)
+       {
+               bot_removefromlargestteam();
+               return;
+       }
+
+       float besttime = 0;
+       entity best = NULL;
+       int bcount = 0;
+
+       FOREACH_CLIENT(it.isbot,
+       {
+               ++bcount;
+
+               if(!best)
+               {
+                       best = it;
+                       besttime = it.createdtime;
+               }
+
+               if(besttime < it.createdtime)
+               {
+                       besttime = it.createdtime;
+                       best = it;
+               }
+       });
+
+       if(!bcount)
+               return; // no bots to remove
+
+       currentbots = currentbots - 1;
+       dropclient(best);
+}
+
+void autoskill(float factor)
+{
+       float bestbot;
+       float bestplayer;
+
+       bestbot = -1;
+       bestplayer = -1;
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+               if(IS_REAL_CLIENT(it))
+                       bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck);
+               else
+                       bestbot = max(bestbot, it.totalfrags - it.totalfrags_lastcheck);
+       ));
+
+       LOG_TRACE("autoskill: best player got ", ftos(bestplayer), ", ");
+       LOG_TRACE("best bot got ", ftos(bestbot), "; ");
+       if(bestbot < 0 || bestplayer < 0)
+       {
+               LOG_TRACE("not doing anything");
+               // don't return, let it reset all counters below
+       }
+       else if(bestbot <= bestplayer * factor - 2)
+       {
+               if(autocvar_skill < 17)
+               {
+                       LOG_TRACE("2 frags difference, increasing skill");
+                       cvar_set("skill", ftos(autocvar_skill + 1));
+                       bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
+               }
+       }
+       else if(bestbot >= bestplayer * factor + 2)
+       {
+               if(autocvar_skill > 0)
+               {
+                       LOG_TRACE("2 frags difference, decreasing skill");
+                       cvar_set("skill", ftos(autocvar_skill - 1));
+                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
+               }
+       }
+       else
+       {
+               LOG_TRACE("not doing anything");
+               return;
+               // don't reset counters, wait for them to accumulate
+       }
+
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags));
+}
+
+void bot_calculate_stepheightvec()
+{
+       stepheightvec = autocvar_sv_stepheight * '0 0 1';
+       jumpstepheightvec = stepheightvec +
+               ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85';
+               // 0.75 factor is for safety to make the jumps easy
+}
+
+float bot_fixcount()
+{
+       int activerealplayers = 0;
+       int realplayers = 0;
+       if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) {
+               activerealplayers = M_ARGV(0, int);
+               realplayers = M_ARGV(1, int);
+       } else {
+               FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+                       if(IS_PLAYER(it))
+                               ++activerealplayers;
+                       ++realplayers;
+               ));
+       }
+
+       int bots;
+       // add/remove bots if needed to make sure there are at least
+       // minplayers+bot_number, or remove all bots if no one is playing
+       // But don't remove bots immediately on level change, as the real players
+       // usually haven't rejoined yet
+       bots_would_leave = false;
+       if (teamplay && autocvar_bot_vs_human && AvailableTeams() == 2)
+               bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
+       else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
+       {
+               float realminplayers, minplayers;
+               realminplayers = autocvar_minplayers;
+               minplayers = max(0, floor(realminplayers));
+
+               float realminbots, minbots;
+               realminbots = autocvar_bot_number;
+               minbots = max(0, floor(realminbots));
+
+               bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers);
+               if(bots > minbots)
+                       bots_would_leave = true;
+       }
+       else
+       {
+               // if there are no players, remove bots
+               bots = 0;
+       }
+
+       // only add one bot per frame to avoid utter chaos
+       if(time > botframe_nextthink)
+       {
+               //dprint(ftos(bots), " ? ", ftos(currentbots), "\n");
+               while (currentbots < bots)
+               {
+                       if (bot_spawn() == NULL)
+                       {
+                               bprint("Can not add bot, server full.\n");
+                               return false;
+                       }
+               }
+               while (currentbots > bots)
+                       bot_removenewest();
+       }
+
+       return true;
+}
+
+void bot_remove_from_bot_list(entity this)
+{
+       entity e = bot_list;
+       entity prev_bot = NULL;
+       while (e)
+       {
+               if(e == this)
+               {
+                       if(!prev_bot)
+                               bot_list = this.nextbot;
+                       else
+                               prev_bot.nextbot = this.nextbot;
+                       if(bot_strategytoken == this)
+                       {
+                               bot_strategytoken = this.nextbot;
+                               bot_strategytoken_taken = true;
+                       }
+                       this.nextbot = NULL;
+                       break;
+               }
+               prev_bot = e;
+               e = e.nextbot;
+       }
+}
+
+void bot_clear(entity this)
+{
+       bot_remove_from_bot_list(this);
+       if(bot_waypoint_queue_owner == this)
+               bot_waypoint_queue_owner = NULL;
+       this.aistatus &= ~AI_STATUS_STUCK; // otherwise bot_waypoint_queue_owner will be set again to this by navigation_unstuck
+}
+
+void bot_serverframe()
+{
+       if (gameover)
+               return;
+
+       if (time < 2)
+               return;
+
+       bot_calculate_stepheightvec();
+       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
+
+       if(time > autoskill_nextthink)
+       {
+               float a;
+               a = autocvar_skill_auto;
+               if(a)
+                       autoskill(a);
+               autoskill_nextthink = time + 5;
+       }
+
+       if(time > botframe_nextthink)
+       {
+               if(!bot_fixcount())
+                       botframe_nextthink = time + 10;
+       }
+
+       bot_ignore_bots = autocvar_bot_ignore_bots;
+
+       if(botframe_spawnedwaypoints)
+       {
+               if(autocvar_waypoint_benchmark)
+                       localcmd("quit\n");
+       }
+
+       if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto)
+       if (botframe_spawnedwaypoints)
+       {
+               if(botframe_cachedwaypointlinks)
+               {
+                       if(!botframe_loadedforcedlinks)
+                               waypoint_load_links_hardwired();
+               }
+               else
+               {
+                       // TODO: Make this check cleaner
+                       IL_EACH(g_waypoints, time - it.nextthink > 10,
+                       {
+                               waypoint_save_links();
+                               break;
+                       });
+               }
+       }
+       else
+       {
+               botframe_spawnedwaypoints = true;
+               waypoint_loadall();
+               if(!waypoint_load_links())
+                       waypoint_schedulerelinkall();
+       }
+
+       if (bot_list)
+       {
+               // cycle the goal token from one bot to the next each frame
+               // (this prevents them from all doing spawnfunc_waypoint searches on the same
+               //  frame, which causes choppy framerates)
+               if (bot_strategytoken_taken)
+               {
+                       bot_strategytoken_taken = false;
+                       if (bot_strategytoken)
+                               bot_strategytoken = bot_strategytoken.nextbot;
+                       if (!bot_strategytoken)
+                               bot_strategytoken = bot_list;
+               }
+
+               if (botframe_nextdangertime < time)
+               {
+                       float interval;
+                       interval = autocvar_bot_ai_dangerdetectioninterval;
+                       if (botframe_nextdangertime < time - interval * 1.5)
+                               botframe_nextdangertime = time;
+                       botframe_nextdangertime = botframe_nextdangertime + interval;
+                       botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates);
+               }
+       }
+
+       if (autocvar_g_waypointeditor)
+               botframe_showwaypointlinks();
+
+       if (autocvar_g_waypointeditor_auto)
+               botframe_autowaypoints();
+
+       if(time > bot_cvar_nextthink)
+       {
+               if(currentbots>0)
+                       bot_custom_weapon_priority_setup();
+               bot_cvar_nextthink = time + 5;
+       }
+}
diff --git a/qcsrc/server/bot/default/bot.qh b/qcsrc/server/bot/default/bot.qh
new file mode 100644 (file)
index 0000000..f615c04
--- /dev/null
@@ -0,0 +1,109 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+const int AI_STATUS_ROAMING                    = BIT(0); // Bot is just crawling the map. No enemies at sight
+const int AI_STATUS_ATTACKING                  = BIT(1); // There are enemies at sight
+const int AI_STATUS_RUNNING                    = BIT(2); // Bot is bunny hopping
+const int AI_STATUS_DANGER_AHEAD               = BIT(3); // There is lava/slime/trigger_hurt ahead
+const int AI_STATUS_OUT_JUMPPAD                = BIT(4); // Trying to get out of a "vertical" jump pad
+const int AI_STATUS_OUT_WATER                  = BIT(5); // Trying to get out of water
+const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = BIT(6); // Waiting for the personal waypoint to be linked
+const int AI_STATUS_WAYPOINT_PERSONAL_GOING    = BIT(7); // Going to a personal waypoint
+const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = BIT(8); // Personal waypoint reached
+const int AI_STATUS_JETPACK_FLYING             = BIT(9);
+const int AI_STATUS_JETPACK_LANDING            = BIT(10);
+const int AI_STATUS_STUCK                      = BIT(11); // Cannot reach any goal
+
+.float isbot; // true if this client is actually a bot
+.int aistatus;
+
+// Skill system
+float autoskill_nextthink;
+
+// havocbot_keyboardskill // keyboard movement
+.float bot_moveskill; // moving technique
+.float bot_dodgeskill; // dodging
+
+.float bot_pingskill; // ping offset
+
+.float bot_weaponskill; // weapon usage skill (combos, e.g.)
+.float bot_aggresskill; // aggressivity, controls "think before fire" behaviour
+.float bot_rangepreference; // weapon choice offset for range (>0 = prefer long range earlier "sniper", <0 = prefer short range "spammer")
+
+.float bot_aimskill; // aim accuracy
+.float bot_offsetskill; // aim breakage
+.float bot_mouseskill; // mouse "speed"
+
+.float bot_thinkskill; // target choice
+.float bot_aiskill; // strategy choice
+
+.float totalfrags_lastcheck;
+
+// Custom weapon priorities
+float bot_distance_far;
+float bot_distance_close;
+
+entity bot_list;
+.entity nextbot;
+.string cleanname;
+.string netname_freeme;
+.string playermodel_freeme;
+.string playerskin_freeme;
+
+.float bot_nextthink;
+
+.float createdtime;
+.float bot_preferredcolors;
+.float bot_attack;
+.float bot_dodge;
+.float bot_dodgerating;
+
+.float bot_pickup;
+.float bot_pickupbasevalue;
+.float bot_canfire;
+.float bot_strategytime;
+
+.float bot_forced_team;
+.float bot_config_loaded;
+
+float bot_strategytoken_taken;
+entity bot_strategytoken;
+
+float botframe_spawnedwaypoints;
+float botframe_nextthink;
+float botframe_nextdangertime;
+float bot_cvar_nextthink;
+float bot_ignore_bots; // let bots not attack other bots (only works in non-teamplay)
+
+/*
+ * Functions
+ */
+
+entity bot_spawn();
+float bot_fixcount();
+
+void bot_think(entity this);
+void bot_setnameandstuff(entity this);
+void bot_custom_weapon_priority_setup();
+void bot_endgame();
+void bot_relinkplayerlist();
+void bot_clear(entity this);
+void bot_clientdisconnect(entity this);
+void bot_clientconnect(entity this);
+void bot_removefromlargestteam();
+void bot_removenewest();
+void autoskill(float factor);
+void bot_serverframe();
+
+.void(entity this) bot_ai;
+.float(entity player, entity item) bot_pickupevalfunc;
+
+/*
+ * Imports
+ */
+
+void(entity this) havocbot_setupbot;
+
+void bot_calculate_stepheightvec();
diff --git a/qcsrc/server/bot/default/cvars.qc b/qcsrc/server/bot/default/cvars.qc
new file mode 100644 (file)
index 0000000..779e7f0
--- /dev/null
@@ -0,0 +1 @@
+#include "cvars.qh"
diff --git a/qcsrc/server/bot/default/cvars.qh b/qcsrc/server/bot/default/cvars.qh
new file mode 100644 (file)
index 0000000..d612c7a
--- /dev/null
@@ -0,0 +1,58 @@
+#pragma once
+
+float autocvar_bot_ai_aimskill_blendrate;
+float autocvar_bot_ai_aimskill_firetolerance_distdegrees;
+float autocvar_bot_ai_aimskill_firetolerance_maxdegrees;
+float autocvar_bot_ai_aimskill_firetolerance_mindegrees;
+float autocvar_bot_ai_aimskill_fixedrate;
+float autocvar_bot_ai_aimskill_mouse;
+float autocvar_bot_ai_aimskill_offset;
+float autocvar_bot_ai_aimskill_order_filter_1st;
+float autocvar_bot_ai_aimskill_order_filter_2nd;
+float autocvar_bot_ai_aimskill_order_filter_3th;
+float autocvar_bot_ai_aimskill_order_filter_4th;
+float autocvar_bot_ai_aimskill_order_filter_5th;
+float autocvar_bot_ai_aimskill_order_mix_1st;
+float autocvar_bot_ai_aimskill_order_mix_2nd;
+float autocvar_bot_ai_aimskill_order_mix_3th;
+float autocvar_bot_ai_aimskill_order_mix_4th;
+float autocvar_bot_ai_aimskill_order_mix_5th;
+float autocvar_bot_ai_aimskill_think;
+float autocvar_bot_ai_bunnyhop_firstjumpdelay;
+float autocvar_bot_ai_bunnyhop_skilloffset;
+float autocvar_bot_ai_bunnyhop_startdistance;
+float autocvar_bot_ai_bunnyhop_stopdistance;
+float autocvar_bot_ai_chooseweaponinterval;
+string autocvar_bot_ai_custom_weapon_priority_close;
+string autocvar_bot_ai_custom_weapon_priority_distances;
+string autocvar_bot_ai_custom_weapon_priority_far;
+string autocvar_bot_ai_custom_weapon_priority_mid;
+float autocvar_bot_ai_dangerdetectioninterval;
+float autocvar_bot_ai_dangerdetectionupdates;
+float autocvar_bot_ai_enemydetectioninterval;
+float autocvar_bot_ai_enemydetectionradius;
+float autocvar_bot_ai_friends_aware_pickup_radius;
+float autocvar_bot_ai_ignoregoal_timeout;
+float autocvar_bot_ai_keyboard_distance;
+float autocvar_bot_ai_keyboard_threshold;
+float autocvar_bot_ai_navigation_jetpack;
+float autocvar_bot_ai_navigation_jetpack_mindistance;
+float autocvar_bot_ai_thinkinterval;
+bool autocvar_bot_ai_weapon_combo;
+float autocvar_bot_ai_weapon_combo_threshold;
+string autocvar_bot_config_file;
+bool autocvar_bot_god;
+bool autocvar_bot_ignore_bots;
+bool autocvar_bot_join_empty;
+bool autocvar_bot_navigation_ignoreplayers;
+bool autocvar_bot_nofire;
+#define autocvar_bot_prefix cvar_string("bot_prefix")
+#define autocvar_bot_suffix cvar_string("bot_suffix")
+bool autocvar_bot_usemodelnames;
+bool autocvar_bot_debug_tracewalk;
+bool autocvar_bot_debug_goalstack;
+bool autocvar_bot_wander_enable;
+bool autocvar_g_debug_bot_commands;
+int autocvar_g_waypointeditor_auto;
+float autocvar_skill_auto;
+bool autocvar_waypoint_benchmark;
diff --git a/qcsrc/server/bot/default/havocbot/_mod.inc b/qcsrc/server/bot/default/havocbot/_mod.inc
new file mode 100644 (file)
index 0000000..3ecfda1
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <server/bot/default/havocbot/havocbot.qc>
+#include <server/bot/default/havocbot/roles.qc>
diff --git a/qcsrc/server/bot/default/havocbot/_mod.qh b/qcsrc/server/bot/default/havocbot/_mod.qh
new file mode 100644 (file)
index 0000000..01fcd46
--- /dev/null
@@ -0,0 +1,3 @@
+// generated file; do not modify
+#include <server/bot/default/havocbot/havocbot.qh>
+#include <server/bot/default/havocbot/roles.qh>
diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc
new file mode 100644 (file)
index 0000000..8e6b391
--- /dev/null
@@ -0,0 +1,1317 @@
+#include "havocbot.qh"
+
+#include "../cvars.qh"
+
+#include "../aim.qh"
+#include "../bot.qh"
+#include "../navigation.qh"
+#include "../scripting.qh"
+#include "../waypoints.qh"
+
+#include <common/constants.qh>
+#include <common/net_linked.qh>
+#include <common/physics/player.qh>
+#include <common/state.qh>
+#include <common/items/_mod.qh>
+
+#include <common/triggers/teleporters.qh>
+#include <common/triggers/trigger/jumppads.qh>
+
+#include <lib/warpzone/common.qh>
+
+.float speed;
+
+void havocbot_ai(entity this)
+{
+       if(this.draggedby)
+               return;
+
+       if(bot_execute_commands(this))
+               return;
+
+       if (bot_strategytoken == this)
+       if (!bot_strategytoken_taken)
+       {
+               if(this.havocbot_blockhead)
+               {
+                       this.havocbot_blockhead = false;
+               }
+               else
+               {
+                       if (!this.jumppadcount)
+                               this.havocbot_role(this); // little too far down the rabbit hole
+               }
+
+               // TODO: tracewalk() should take care of this job (better path finding under water)
+               // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
+               if(!(IS_DEAD(this)))
+               if(!this.goalcurrent)
+               if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
+               {
+                       // Look for the closest waypoint out of water
+                       entity newgoal = NULL;
+                       IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 10000),
+                       {
+                               if(it.origin.z < this.origin.z)
+                                       continue;
+
+                               if(it.origin.z - this.origin.z - this.view_ofs.z > 100)
+                                       continue;
+
+                               if (pointcontents(it.origin + it.maxs + '0 0 1') != CONTENT_EMPTY)
+                                       continue;
+
+                               traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
+
+                               if(trace_fraction < 1)
+                                       continue;
+
+                               if(!newgoal || vlen2(it.origin - this.origin) < vlen2(newgoal.origin - this.origin))
+                                       newgoal = it;
+                       });
+
+                       if(newgoal)
+                       {
+                       //      te_wizspike(newgoal.origin);
+                               navigation_pushroute(this, newgoal);
+                       }
+               }
+
+               // token has been used this frame
+               bot_strategytoken_taken = true;
+       }
+
+       if(IS_DEAD(this))
+               return;
+
+       havocbot_chooseenemy(this);
+       if (this.bot_chooseweapontime < time )
+       {
+               this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
+               havocbot_chooseweapon(this);
+       }
+       havocbot_aim(this);
+       lag_update(this);
+       if (this.bot_aimtarg)
+       {
+               this.aistatus |= AI_STATUS_ATTACKING;
+               this.aistatus &= ~AI_STATUS_ROAMING;
+
+               if(this.weapons)
+               {
+                       Weapon w = PS(this).m_weapon;
+                       w.wr_aim(w, this);
+                       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
+                       {
+                               PHYS_INPUT_BUTTON_ATCK(this) = false;
+                               PHYS_INPUT_BUTTON_ATCK2(this) = false;
+                       }
+                       else
+                       {
+                               if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))
+                                       this.lastfiredweapon = PS(this).m_weapon.m_id;
+                       }
+               }
+               else
+               {
+                       if(IS_PLAYER(this.bot_aimtarg))
+                               bot_aimdir(this, this.bot_aimtarg.origin + this.bot_aimtarg.view_ofs - this.origin - this.view_ofs , -1);
+               }
+       }
+       else if (this.goalcurrent)
+       {
+               this.aistatus |= AI_STATUS_ROAMING;
+               this.aistatus &= ~AI_STATUS_ATTACKING;
+
+               vector now,v,next;//,heading;
+               float aimdistance,skillblend,distanceblend,blend;
+               next = now = ( (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5) - (this.origin + this.view_ofs);
+               aimdistance = vlen(now);
+               //heading = this.velocity;
+               //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n");
+               if(
+                       this.goalstack01 != this && this.goalstack01 && !wasfreed(this.goalstack01) && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
+                       !(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+               )
+                       next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs);
+
+               skillblend=bound(0,(skill+this.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
+               distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1);
+               blend = skillblend * (1-distanceblend);
+               //v = (now * (distanceblend) + next * (1-distanceblend)) * (skillblend) + now * (1-skillblend);
+               //v = now * (distanceblend) * (skillblend) + next * (1-distanceblend) * (skillblend) + now * (1-skillblend);
+               //v = now * ((1-skillblend) + (distanceblend) * (skillblend)) + next * (1-distanceblend) * (skillblend);
+               v = now + blend * (next - now);
+               //dprint(etos(this), " ");
+               //dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n");
+               //v = now * (distanceblend) + next * (1-distanceblend);
+               if (this.waterlevel < WATERLEVEL_SWIMMING)
+                       v.z = 0;
+               //dprint("walk at:", vtos(v), "\n");
+               //te_lightning2(NULL, this.origin, this.goalcurrent.origin);
+               bot_aimdir(this, v, -1);
+       }
+       havocbot_movetogoal(this);
+
+       // if the bot is not attacking, consider reloading weapons
+       if (!(this.aistatus & AI_STATUS_ATTACKING))
+       {
+               // we are currently holding a weapon that's not fully loaded, reload it
+               if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
+               if(this.clip_load < this.clip_size)
+                       this.impulse = 20; // "press" the reload button, not sure if this is done right
+
+               // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
+               // the code above executes next frame, starting the reloading then
+               if(skill >= 5) // bots can only look for unloaded weapons past this skill
+               if(this.clip_load >= 0) // only if we're not reloading a weapon already
+               {
+                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                               if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo))
+                                       PS(this).m_switchweapon = it;
+                       ));
+               }
+       }
+}
+
+void havocbot_keyboard_movement(entity this, vector destorg)
+{
+       vector keyboard;
+       float blend, maxspeed;
+       float sk;
+
+       sk = skill + this.bot_moveskill;
+
+       maxspeed = autocvar_sv_maxspeed;
+
+       if (time < this.havocbot_keyboardtime)
+               return;
+
+       this.havocbot_keyboardtime =
+               max(
+                       this.havocbot_keyboardtime
+                               + 0.05/max(1, sk+this.havocbot_keyboardskill)
+                               + random()*0.025/max(0.00025, skill+this.havocbot_keyboardskill)
+               , time);
+       keyboard = this.movement * (1.0 / maxspeed);
+
+       float trigger, trigger1;
+       blend = bound(0,sk*0.1,1);
+       trigger = autocvar_bot_ai_keyboard_threshold;
+       trigger1 = 0 - trigger;
+
+       // categorize forward movement
+       // at skill < 1.5 only forward
+       // at skill < 2.5 only individual directions
+       // at skill < 4.5 only individual directions, and forward diagonals
+       // at skill >= 4.5, all cases allowed
+       if (keyboard.x > trigger)
+       {
+               keyboard.x = 1;
+               if (sk < 2.5)
+                       keyboard.y = 0;
+       }
+       else if (keyboard.x < trigger1 && sk > 1.5)
+       {
+               keyboard.x = -1;
+               if (sk < 4.5)
+                       keyboard.y = 0;
+       }
+       else
+       {
+               keyboard.x = 0;
+               if (sk < 1.5)
+                       keyboard.y = 0;
+       }
+       if (sk < 4.5)
+               keyboard.z = 0;
+
+       if (keyboard.y > trigger)
+               keyboard.y = 1;
+       else if (keyboard.y < trigger1)
+               keyboard.y = -1;
+       else
+               keyboard.y = 0;
+
+       if (keyboard.z > trigger)
+               keyboard.z = 1;
+       else if (keyboard.z < trigger1)
+               keyboard.z = -1;
+       else
+               keyboard.z = 0;
+
+       this.havocbot_keyboard = keyboard * maxspeed;
+       if (this.havocbot_ducktime>time) PHYS_INPUT_BUTTON_CROUCH(this) = true;
+
+       keyboard = this.havocbot_keyboard;
+       blend = bound(0,vlen(destorg-this.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree
+       //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
+       this.movement = this.movement + (keyboard - this.movement) * blend;
+}
+
+void havocbot_bunnyhop(entity this, vector dir)
+{
+       float bunnyhopdistance;
+       vector deviation;
+       float maxspeed;
+       vector gco, gno;
+
+       // Don't jump when attacking
+       if(this.aistatus & AI_STATUS_ATTACKING)
+               return;
+
+       if(IS_PLAYER(this.goalcurrent))
+               return;
+
+       maxspeed = autocvar_sv_maxspeed;
+
+       if(this.aistatus & AI_STATUS_DANGER_AHEAD)
+       {
+               this.aistatus &= ~AI_STATUS_RUNNING;
+               PHYS_INPUT_BUTTON_JUMP(this) = false;
+               this.bot_canruntogoal = 0;
+               this.bot_timelastseengoal = 0;
+               return;
+       }
+
+       if(this.waterlevel > WATERLEVEL_WETFEET)
+       {
+               this.aistatus &= ~AI_STATUS_RUNNING;
+               return;
+       }
+
+       if(this.bot_lastseengoal != this.goalcurrent && !(this.aistatus & AI_STATUS_RUNNING))
+       {
+               this.bot_canruntogoal = 0;
+               this.bot_timelastseengoal = 0;
+       }
+
+       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
+       bunnyhopdistance = vlen(this.origin - gco);
+
+       // Run only to visible goals
+       if(IS_ONGROUND(this))
+       if(this.speed==maxspeed)
+       if(checkpvs(this.origin + this.view_ofs, this.goalcurrent))
+       {
+                       this.bot_lastseengoal = this.goalcurrent;
+
+                       // seen it before
+                       if(this.bot_timelastseengoal)
+                       {
+                               // for a period of time
+                               if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
+                               {
+                                       float checkdistance;
+                                       checkdistance = true;
+
+                                       // don't run if it is too close
+                                       if(this.bot_canruntogoal==0)
+                                       {
+                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance)
+                                                       this.bot_canruntogoal = 1;
+                                               else
+                                                       this.bot_canruntogoal = -1;
+                                       }
+
+                                       if(this.bot_canruntogoal != 1)
+                                               return;
+
+                                       if(this.aistatus & AI_STATUS_ROAMING)
+                                       if(this.goalcurrent.classname=="waypoint")
+                                       if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
+                                       if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z)
+                                       if(this.goalstack01 && !wasfreed(this.goalstack01))
+                                       {
+                                               gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
+                                               deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin);
+                                               while (deviation.y < -180) deviation.y = deviation.y + 360;
+                                               while (deviation.y > 180) deviation.y = deviation.y - 360;
+
+                                               if(fabs(deviation.y) < 20)
+                                               if(bunnyhopdistance < vlen(this.origin - gno))
+                                               if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z)
+                                               {
+                                                       if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance))
+                                                       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
+                                                       {
+                                                               checkdistance = false;
+                                                       }
+                                               }
+                                       }
+
+                                       if(checkdistance)
+                                       {
+                                               this.aistatus &= ~AI_STATUS_RUNNING;
+                                               // increase stop distance in case the goal is on a slope or a lower platform 
+                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z))
+                                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
+                                       }
+                                       else
+                                       {
+                                               this.aistatus |= AI_STATUS_RUNNING;
+                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               this.bot_timelastseengoal = time;
+                       }
+       }
+       else
+       {
+               this.bot_timelastseengoal = 0;
+       }
+
+#if 0
+       // Release jump button
+       if(!cvar("sv_pogostick"))
+       if((IS_ONGROUND(this)) == 0)
+       {
+               if(this.velocity.z < 0 || vlen(this.velocity)<maxspeed)
+                       PHYS_INPUT_BUTTON_JUMP(this) = false;
+
+               // Strafe
+               if(this.aistatus & AI_STATUS_RUNNING)
+               if(vlen(this.velocity)>maxspeed)
+               {
+                       deviation = vectoangles(dir) - vectoangles(this.velocity);
+                       while (deviation.y < -180) deviation.y = deviation.y + 360;
+                       while (deviation.y > 180) deviation.y = deviation.y - 360;
+
+                       if(fabs(deviation.y)>10)
+                               this.movement_x = 0;
+
+                       if(deviation.y>10)
+                               this.movement_y = maxspeed * -1;
+                       else if(deviation.y<10)
+                               this.movement_y = maxspeed;
+
+               }
+       }
+#endif
+}
+
+void havocbot_movetogoal(entity this)
+{
+       vector destorg;
+       vector diff;
+       vector dir;
+       vector flatdir;
+       vector m1;
+       vector m2;
+       vector evadeobstacle;
+       vector evadelava;
+       float s;
+       float maxspeed;
+       vector gco;
+       //float dist;
+       vector dodge;
+       //if (this.goalentity)
+       //      te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5);
+       this.movement = '0 0 0';
+       maxspeed = autocvar_sv_maxspeed;
+
+       // Jetpack navigation
+       if(this.goalcurrent)
+       if(this.navigation_jetpack_goal)
+       if(this.goalcurrent==this.navigation_jetpack_goal)
+       if(this.ammo_fuel)
+       {
+               if(autocvar_bot_debug_goalstack)
+               {
+                       debuggoalstack(this);
+                       te_wizspike(this.navigation_jetpack_point);
+               }
+
+               // Take off
+               if (!(this.aistatus & AI_STATUS_JETPACK_FLYING))
+               {
+                       // Brake almost completely so it can get a good direction
+                       if(vdist(this.velocity, >, 10))
+                               return;
+                       this.aistatus |= AI_STATUS_JETPACK_FLYING;
+               }
+
+               makevectors(this.v_angle.y * '0 1 0');
+               dir = normalize(this.navigation_jetpack_point - this.origin);
+
+               // Landing
+               if(this.aistatus & AI_STATUS_JETPACK_LANDING)
+               {
+                       // Calculate brake distance in xy
+                       float db, v, d;
+                       vector dxy;
+
+                       dxy = this.origin - ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ); dxy.z = 0;
+                       d = vlen(dxy);
+                       v = vlen(this.velocity -  this.velocity.z * '0 0 1');
+                       db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
+               //      dprint("distance ", ftos(ceil(d)), " velocity ", ftos(ceil(v)), " brake at ", ftos(ceil(db)), "\n");
+                       if(d < db || d < 500)
+                       {
+                               // Brake
+                               if(fabs(this.velocity.x)>maxspeed*0.3)
+                               {
+                                       this.movement_x = dir * v_forward * -maxspeed;
+                                       return;
+                               }
+                               // Switch to normal mode
+                               this.navigation_jetpack_goal = NULL;
+                               this.aistatus &= ~AI_STATUS_JETPACK_LANDING;
+                               this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
+                               return;
+                       }
+               }
+               else if(checkpvs(this.origin,this.goalcurrent))
+               {
+                       // If I can see the goal switch to landing code
+                       this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
+                       this.aistatus |= AI_STATUS_JETPACK_LANDING;
+                       return;
+               }
+
+               // Flying
+               PHYS_INPUT_BUTTON_HOOK(this) = true;
+               if(this.navigation_jetpack_point.z - STAT(PL_MAX, this).z + STAT(PL_MIN, this).z < this.origin.z)
+               {
+                       this.movement_x = dir * v_forward * maxspeed;
+                       this.movement_y = dir * v_right * maxspeed;
+               }
+               return;
+       }
+
+       // Handling of jump pads
+       if(this.jumppadcount)
+       {
+               // If got stuck on the jump pad try to reach the farthest visible waypoint
+               // but with some randomness so it can try out different paths
+               if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
+               {
+                       if(fabs(this.velocity.z)<50)
+                       {
+                               entity newgoal = NULL;
+                               IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 1000),
+                               {
+                                       traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
+
+                                       if(trace_fraction < 1)
+                                               continue;
+
+                                       if(!newgoal || ((random() < 0.8) && vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin)))
+                                               newgoal = it;
+                               });
+
+                               if(newgoal)
+                               {
+                                       this.ignoregoal = this.goalcurrent;
+                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
+                                       navigation_clearroute(this);
+                                       navigation_routetogoal(this, newgoal, this.origin);
+                                       if(autocvar_bot_debug_goalstack)
+                                               debuggoalstack(this);
+                                       this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
+                               }
+                       }
+                       else
+                               return;
+               }
+               else
+               {
+                       if(this.velocity.z>0)
+                       {
+                               float threshold;
+                               vector velxy = this.velocity; velxy_z = 0;
+                               threshold = maxspeed * 0.2;
+                               if(vdist(velxy, <, threshold))
+                               {
+                                       LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now");
+                                       this.aistatus |= AI_STATUS_OUT_JUMPPAD;
+                               }
+                               return;
+                       }
+
+                       // Don't chase players while using a jump pad
+                       if(IS_PLAYER(this.goalcurrent) || IS_PLAYER(this.goalstack01))
+                               return;
+               }
+       }
+       else if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
+               this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
+
+       // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
+       if(skill>6)
+       if (!(IS_ONGROUND(this)))
+       {
+               tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this);
+               if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos ))
+               if(this.items & IT_JETPACK)
+               {
+                       tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this);
+                       if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' ))
+                       {
+                               if(this.velocity.z<0)
+                               {
+                                       PHYS_INPUT_BUTTON_HOOK(this) = true;
+                               }
+                       }
+                       else
+                               PHYS_INPUT_BUTTON_HOOK(this) = true;
+
+                       // If there is no goal try to move forward
+
+                       if(this.goalcurrent==NULL)
+                               dir = v_forward;
+                       else
+                               dir = normalize(( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - this.origin);
+
+                       vector xyvelocity = this.velocity; xyvelocity_z = 0;
+                       float xyspeed = xyvelocity * dir;
+
+                       if(xyspeed < (maxspeed / 2))
+                       {
+                               makevectors(this.v_angle.y * '0 1 0');
+                               tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this);
+                               if(trace_fraction==1)
+                               {
+                                       this.movement_x = dir * v_forward * maxspeed;
+                                       this.movement_y = dir * v_right * maxspeed;
+                                       if (skill < 10)
+                                               havocbot_keyboard_movement(this, this.origin + dir * 100);
+                               }
+                       }
+
+                       this.havocbot_blockhead = true;
+
+                       return;
+               }
+               else if(this.health>WEP_CVAR(devastator, damage)*0.5)
+               {
+                       if(this.velocity.z < 0)
+                       if(client_hasweapon(this, WEP_DEVASTATOR, true, false))
+                       {
+                               this.movement_x = maxspeed;
+
+                               if(this.rocketjumptime)
+                               {
+                                       if(time > this.rocketjumptime)
+                                       {
+                                               PHYS_INPUT_BUTTON_ATCK2(this) = true;
+                                               this.rocketjumptime = 0;
+                                       }
+                                       return;
+                               }
+
+                               PS(this).m_switchweapon = WEP_DEVASTATOR;
+                               this.v_angle_x = 90;
+                               PHYS_INPUT_BUTTON_ATCK(this) = true;
+                               this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
+                               return;
+                       }
+               }
+               else
+               {
+                       // If there is no goal try to move forward
+                       if(this.goalcurrent==NULL)
+                               this.movement_x = maxspeed;
+               }
+       }
+
+       // If we are under water with no goals, swim up
+       if(this.waterlevel)
+       if(this.goalcurrent==NULL)
+       {
+               dir = '0 0 0';
+               if(this.waterlevel>WATERLEVEL_SWIMMING)
+                       dir.z = 1;
+               else if(this.velocity.z >= 0 && !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER))
+                       PHYS_INPUT_BUTTON_JUMP(this) = true;
+               else
+                       PHYS_INPUT_BUTTON_JUMP(this) = false;
+               makevectors(this.v_angle.y * '0 1 0');
+               this.movement_x = dir * v_forward * maxspeed;
+               this.movement_y = dir * v_right * maxspeed;
+               this.movement_z = dir * v_up * maxspeed;
+       }
+
+       // if there is nowhere to go, exit
+       if (this.goalcurrent == NULL)
+               return;
+
+       navigation_poptouchedgoals(this);
+
+       // if ran out of goals try to use an alternative goal or get a new strategy asap
+       if(this.goalcurrent == NULL)
+       {
+               this.bot_strategytime = 0;
+               return;
+       }
+
+
+       if(autocvar_bot_debug_goalstack)
+               debuggoalstack(this);
+
+       m1 = this.goalcurrent.origin + this.goalcurrent.mins;
+       m2 = this.goalcurrent.origin + this.goalcurrent.maxs;
+       destorg = this.origin;
+       destorg.x = bound(m1_x, destorg.x, m2_x);
+       destorg.y = bound(m1_y, destorg.y, m2_y);
+       destorg.z = bound(m1_z, destorg.z, m2_z);
+       diff = destorg - this.origin;
+       //dist = vlen(diff);
+       dir = normalize(diff);
+       flatdir = diff;flatdir.z = 0;
+       flatdir = normalize(flatdir);
+       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
+
+       //if (this.bot_dodgevector_time < time)
+       {
+       //      this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval");
+       //      this.bot_dodgevector_jumpbutton = 1;
+               evadeobstacle = '0 0 0';
+               evadelava = '0 0 0';
+
+               if (this.waterlevel)
+               {
+                       if(this.waterlevel>WATERLEVEL_SWIMMING)
+                       {
+                       //      flatdir_z = 1;
+                               this.aistatus |= AI_STATUS_OUT_WATER;
+                       }
+                       else
+                       {
+                               if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) &&
+                                       ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER))
+                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
+                               else
+                                       PHYS_INPUT_BUTTON_JUMP(this) = false;
+                       }
+                       dir = normalize(flatdir);
+                       makevectors(this.v_angle.y * '0 1 0');
+               }
+               else
+               {
+                       if(this.aistatus & AI_STATUS_OUT_WATER)
+                               this.aistatus &= ~AI_STATUS_OUT_WATER;
+
+                       // jump if going toward an obstacle that doesn't look like stairs we
+                       // can walk up directly
+                       tracebox(this.origin, this.mins, this.maxs, this.origin + this.velocity * 0.2, false, this);
+                       if (trace_fraction < 1)
+                       if (trace_plane_normal.z < 0.7)
+                       {
+                               s = trace_fraction;
+                               tracebox(this.origin + stepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + stepheightvec, false, this);
+                               if (trace_fraction < s + 0.01)
+                               if (trace_plane_normal.z < 0.7)
+                               {
+                                       s = trace_fraction;
+                                       tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + jumpstepheightvec, false, this);
+                                       if (trace_fraction > s)
+                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
+                               }
+                       }
+
+                       // avoiding dangers and obstacles
+                       vector dst_ahead = this.origin + this.view_ofs + this.velocity * 0.5;
+                       vector dst_down = dst_ahead - '0 0 3000';
+
+                       // Look ahead
+                       traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
+
+                       // Check head-banging against walls
+                       if(vdist(this.origin + this.view_ofs - trace_endpos, <, 25) && !(this.aistatus & AI_STATUS_OUT_WATER))
+                       {
+                               PHYS_INPUT_BUTTON_JUMP(this) = true;
+                               if(this.facingwalltime && time > this.facingwalltime)
+                               {
+                                       this.ignoregoal = this.goalcurrent;
+                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
+                                       this.bot_strategytime = 0;
+                                       return;
+                               }
+                               else
+                               {
+                                       this.facingwalltime = time + 0.05;
+                               }
+                       }
+                       else
+                       {
+                               this.facingwalltime = 0;
+
+                               if(this.ignoregoal != NULL && time > this.ignoregoaltime)
+                               {
+                                       this.ignoregoal = NULL;
+                                       this.ignoregoaltime = 0;
+                               }
+                       }
+
+                       // Check for water/slime/lava and dangerous edges
+                       // (only when the bot is on the ground or jumping intentionally)
+                       this.aistatus &= ~AI_STATUS_DANGER_AHEAD;
+
+                       if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired )
+                       if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || (this.aistatus & AI_STATUS_ROAMING) || PHYS_INPUT_BUTTON_JUMP(this))
+                       {
+                               // Look downwards
+                               traceline(dst_ahead , dst_down, true, NULL);
+                               //te_lightning2(NULL, this.origin + this.view_ofs, dst_ahead); // Draw "ahead" look
+                               //te_lightning2(NULL, dst_ahead, dst_down); // Draw "downwards" look
+                               if(trace_endpos.z < this.origin.z + this.mins.z)
+                               {
+                                       s = pointcontents(trace_endpos + '0 0 1');
+                                       if (s != CONTENT_SOLID)
+                                       if (s == CONTENT_LAVA || s == CONTENT_SLIME)
+                                               evadelava = normalize(this.velocity) * -1;
+                                       else if (s == CONTENT_SKY)
+                                               evadeobstacle = normalize(this.velocity) * -1;
+                                       else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
+                                       {
+                                               // the traceline check isn't enough but is good as optimization,
+                                               // when not true (most of the time) this tracebox call is avoided
+                                               tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this);
+                                               if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
+                                               {
+                                                       if (gco.z > this.origin.z + jumpstepheightvec.z)
+                                                       { 
+                                                               // the goal is probably on an upper platform, assume bot can't get there
+                                                               LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
+                                                               navigation_clearroute(this);
+                                                               this.bot_strategytime = 0;
+                                                       }
+                                                       else
+                                                               evadelava = normalize(this.velocity) * -1;
+                                               }
+                                       }
+                               }
+                       }
+
+                       dir = flatdir;
+                       evadeobstacle.z = 0;
+                       evadelava.z = 0;
+                       makevectors(this.v_angle.y * '0 1 0');
+
+                       if(evadeobstacle!='0 0 0'||evadelava!='0 0 0')
+                               this.aistatus |= AI_STATUS_DANGER_AHEAD;
+               }
+
+               dodge = havocbot_dodge(this);
+               dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1);
+               evadelava = evadelava * bound(1,3-(skill+this.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
+               traceline(this.origin, ( ( this.enemy.absmin + this.enemy.absmax ) * 0.5 ), true, NULL);
+               if(IS_PLAYER(trace_ent))
+                       dir = dir * bound(0,(skill+this.bot_dodgeskill)/7,1);
+
+               dir = normalize(dir + dodge + evadeobstacle + evadelava);
+       //      this.bot_dodgevector = dir;
+       //      this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this);
+       }
+
+       if(time < this.ladder_time)
+       {
+               if(this.goalcurrent.origin.z + this.goalcurrent.mins.z > this.origin.z + this.mins.z)
+               {
+                       if(this.origin.z + this.mins.z  < this.ladder_entity.origin.z + this.ladder_entity.maxs.z)
+                               dir.z = 1;
+               }
+               else
+               {
+                       if(this.origin.z + this.mins.z  > this.ladder_entity.origin.z + this.ladder_entity.mins.z)
+                               dir.z = -1;
+               }
+       }
+
+       //dir = this.bot_dodgevector;
+       //if (this.bot_dodgevector_jumpbutton)
+       //      PHYS_INPUT_BUTTON_JUMP(this) = true;
+       this.movement_x = dir * v_forward * maxspeed;
+       this.movement_y = dir * v_right * maxspeed;
+       this.movement_z = dir * v_up * maxspeed;
+
+       // Emulate keyboard interface
+       if (skill < 10)
+               havocbot_keyboard_movement(this, destorg);
+
+       // Bunnyhop!
+//     if(this.aistatus & AI_STATUS_ROAMING)
+       if(this.goalcurrent)
+       if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
+               havocbot_bunnyhop(this, dir);
+
+       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(this))) PHYS_INPUT_BUTTON_JUMP(this) = true;
+       if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) PHYS_INPUT_BUTTON_JUMP(this) = true;
+       if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10);
+}
+
+entity havocbot_gettarget(entity this, bool secondary)
+{
+       entity best = NULL;
+       vector eye = CENTER_OR_VIEWOFS(this);
+       IL_EACH(g_bot_targets, boolean((secondary) ? it.classname == "misc_breakablemodel" : it.classname != "misc_breakablemodel"),
+       {
+               vector v = CENTER_OR_VIEWOFS(it);
+               if(vdist(v - eye, <, autocvar_bot_ai_enemydetectionradius))
+               if(!best || vlen2(CENTER_OR_VIEWOFS(best) - eye) > vlen2(v - eye))
+               if(bot_shouldattack(this, it))
+               {
+                       traceline(eye, v, true, this);
+                       if (trace_ent == it || trace_fraction >= 1)
+                               best = it;
+               }
+       });
+
+       return best;
+}
+
+void havocbot_chooseenemy(entity this)
+{
+       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
+       {
+               this.enemy = NULL;
+               return;
+       }
+       if (this.enemy)
+       {
+               if (!bot_shouldattack(this, this.enemy))
+               {
+                       // enemy died or something, find a new target
+                       this.enemy = NULL;
+                       this.havocbot_chooseenemy_finished = time;
+               }
+               else if (this.havocbot_stickenemy)
+               {
+                       // tracking last chosen enemy
+                       // if enemy is visible
+                       // and not really really far away
+                       // and we're not severely injured
+                       // then keep tracking for a half second into the future
+                       traceline(this.origin+this.view_ofs, ( this.enemy.absmin + this.enemy.absmax ) * 0.5,false,NULL);
+                       if (trace_ent == this.enemy || trace_fraction == 1)
+                       if (vdist(((this.enemy.absmin + this.enemy.absmax) * 0.5) - this.origin, <, 1000))
+                       if (this.health > 30)
+                       {
+                               // remain tracking him for a shot while (case he went after a small corner or pilar
+                               this.havocbot_chooseenemy_finished = time + 0.5;
+                               return;
+                       }
+                       // enemy isn't visible, or is far away, or we're injured severely
+                       // so stop preferring this enemy
+                       // (it will still take a half second until a new one is chosen)
+                       this.havocbot_stickenemy = 0;
+               }
+       }
+       if (time < this.havocbot_chooseenemy_finished)
+               return;
+       this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
+       vector eye = this.origin + this.view_ofs;
+       entity best = NULL;
+       float bestrating = 100000000;
+
+       // Backup hit flags
+       int hf = this.dphitcontentsmask;
+
+       // Search for enemies, if no enemy can be seen directly try to look through transparent objects
+
+       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+
+       bool scan_transparent = false;
+       bool scan_secondary_targets = false;
+       bool have_secondary_targets = false;
+       while(true)
+       {
+               scan_secondary_targets = false;
+LABEL(scan_targets)
+               IL_EACH(g_bot_targets, it.bot_attack,
+               {
+                       if(!scan_secondary_targets)
+                       {
+                               if(it.classname == "misc_breakablemodel")
+                               {
+                                       have_secondary_targets = true;
+                                       continue;
+                               }
+                       }
+                       else if(it.classname != "misc_breakablemodel")
+                               continue;
+
+                       vector v = (it.absmin + it.absmax) * 0.5;
+                       float rating = vlen2(v - eye);
+                       if (vdist(v - eye, <, autocvar_bot_ai_enemydetectionradius))
+                       if (bestrating > rating)
+                       if (bot_shouldattack(this, it))
+                       {
+                               traceline(eye, v, true, this);
+                               if (trace_ent == it || trace_fraction >= 1)
+                               {
+                                       best = it;
+                                       bestrating = rating;
+                               }
+                       }
+               });
+
+               if(!best && have_secondary_targets && !scan_secondary_targets)
+               {
+                       scan_secondary_targets = true;
+                       // restart the loop
+                       bestrating = 100000000;
+                       goto scan_targets;
+               }
+
+               // I want to do a second scan if no enemy was found or I don't have weapons
+               // TODO: Perform the scan when using the rifle (requires changes on the rifle code)
+               if(best || this.weapons) // || this.weapon == WEP_RIFLE.m_id
+                       break;
+               if(scan_transparent)
+                       break;
+
+               // Set flags to see through transparent objects
+               this.dphitcontentsmask |= DPCONTENTS_OPAQUE;
+
+               scan_transparent = true;
+       }
+
+       // Restore hit flags
+       this.dphitcontentsmask = hf;
+
+       this.enemy = best;
+       this.havocbot_stickenemy = true;
+       if(best && best.classname == "misc_breakablemodel")
+               this.havocbot_stickenemy = false;
+}
+
+float havocbot_chooseweapon_checkreload(entity this, int new_weapon)
+{
+       // bots under this skill cannot find unloaded weapons to reload idly when not in combat,
+       // so skip this for them, or they'll never get to reload their weapons at all.
+       // this also allows bots under this skill to be more stupid, and reload more often during combat :)
+       if(skill < 5)
+               return false;
+
+       // if this weapon is scheduled for reloading, don't switch to it during combat
+       if (this.weapon_load[new_weapon] < 0)
+       {
+               bool other_weapon_available = false;
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this))
+                               other_weapon_available = true;
+               ));
+               if(other_weapon_available)
+                       return true;
+       }
+
+       return false;
+}
+
+void havocbot_chooseweapon(entity this)
+{
+       int i;
+
+       // ;)
+       if(g_weaponarena_weapons == WEPSET(TUBA))
+       {
+               PS(this).m_switchweapon = WEP_TUBA;
+               return;
+       }
+
+       // TODO: clean this up by moving it to weapon code
+       if(this.enemy==NULL)
+       {
+               // If no weapon was chosen get the first available weapon
+               if(PS(this).m_weapon==WEP_Null)
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(client_hasweapon(this, it, true, false))
+                       {
+                               PS(this).m_switchweapon = it;
+                               return;
+                       }
+               ));
+               return;
+       }
+
+       // Do not change weapon during the next second after a combo
+       float f = time - this.lastcombotime;
+       if(f < 1)
+               return;
+
+       float w;
+       float distance; distance=bound(10,vlen(this.origin-this.enemy.origin)-200,10000);
+
+       // Should it do a weapon combo?
+       float af, ct, combo_time, combo;
+
+       af = ATTACK_FINISHED(this, 0);
+       ct = autocvar_bot_ai_weapon_combo_threshold;
+
+       // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos
+       // Ideally this 4 should be calculated as longest_weapon_refire / bot_ai_weapon_combo_threshold
+       combo_time = time + ct + (ct * ((-0.3*(skill+this.bot_weaponskill))+3));
+
+       combo = false;
+
+       if(autocvar_bot_ai_weapon_combo)
+       if(PS(this).m_weapon.m_id == this.lastfiredweapon)
+       if(af > combo_time)
+       {
+               combo = true;
+               this.lastcombotime = time;
+       }
+
+       distance *= pow(2, this.bot_rangepreference);
+
+       // Custom weapon list based on distance to the enemy
+       if(bot_custom_weapon){
+
+               // Choose weapons for far distance
+               if ( distance > bot_distance_far ) {
+                       for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){
+                               w = bot_weapons_far[i];
+                               if ( client_hasweapon(this, Weapons_from(w), true, false) )
+                               {
+                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+                                               continue;
+                                       PS(this).m_switchweapon = Weapons_from(w);
+                                       return;
+                               }
+                       }
+               }
+
+               // Choose weapons for mid distance
+               if ( distance > bot_distance_close) {
+                       for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){
+                               w = bot_weapons_mid[i];
+                               if ( client_hasweapon(this, Weapons_from(w), true, false) )
+                               {
+                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+                                               continue;
+                                       PS(this).m_switchweapon = Weapons_from(w);
+                                       return;
+                               }
+                       }
+               }
+
+               // Choose weapons for close distance
+               for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){
+                       w = bot_weapons_close[i];
+                       if ( client_hasweapon(this, Weapons_from(w), true, false) )
+                       {
+                               if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
+                                       continue;
+                               PS(this).m_switchweapon = Weapons_from(w);
+                               return;
+                       }
+               }
+       }
+}
+
+void havocbot_aim(entity this)
+{
+       vector myvel, enemyvel;
+//     if(this.flags & FL_INWATER)
+//             return;
+       if (time < this.nextaim)
+               return;
+       this.nextaim = time + 0.1;
+       myvel = this.velocity;
+       if (!this.waterlevel)
+               myvel.z = 0;
+       if (this.enemy)
+       {
+               enemyvel = this.enemy.velocity;
+               if (!this.enemy.waterlevel)
+                       enemyvel.z = 0;
+               lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel);
+       }
+       else
+               lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0');
+}
+
+bool havocbot_moveto_refresh_route(entity this)
+{
+       // Refresh path to goal if necessary
+       entity wp;
+       wp = this.havocbot_personal_waypoint;
+       navigation_goalrating_start(this);
+       navigation_routerating(this, wp, 10000, 10000);
+       navigation_goalrating_end(this);
+       return this.navigation_hasgoals;
+}
+
+float havocbot_moveto(entity this, vector pos)
+{
+       entity wp;
+
+       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+       {
+               // Step 4: Move to waypoint
+               if(this.havocbot_personal_waypoint==NULL)
+               {
+                       LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint");
+                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                       return CMD_STATUS_ERROR;
+               }
+
+               if (!bot_strategytoken_taken)
+               if(this.havocbot_personal_waypoint_searchtime<time)
+               {
+                       bot_strategytoken_taken = true;
+                       if(havocbot_moveto_refresh_route(this))
+                       {
+                               LOG_TRACE(this.netname, " walking to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts)");
+                               this.havocbot_personal_waypoint_searchtime = time + 10;
+                               this.havocbot_personal_waypoint_failcounter = 0;
+                       }
+                       else
+                       {
+                               this.havocbot_personal_waypoint_failcounter += 1;
+                               this.havocbot_personal_waypoint_searchtime = time + 2;
+                               if(this.havocbot_personal_waypoint_failcounter >= 30)
+                               {
+                                       LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin));
+                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+                                       delete(this.havocbot_personal_waypoint);
+                                       return CMD_STATUS_ERROR;
+                               }
+                               else
+                                       LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later");
+                       }
+               }
+
+               if(autocvar_bot_debug_goalstack)
+                       debuggoalstack(this);
+
+               // Heading
+               vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs);
+               dir.z = 0;
+               bot_aimdir(this, dir, -1);
+
+               // Go!
+               havocbot_movetogoal(this);
+
+               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
+               {
+                       // Step 5: Waypoint reached
+                       LOG_TRACE(this.netname, "'s personal waypoint reached");
+                       delete(this.havocbot_personal_waypoint);
+                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+                       return CMD_STATUS_FINISHED;
+               }
+
+               return CMD_STATUS_EXECUTING;
+       }
+
+       // Step 2: Linking waypoint
+       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING)
+       {
+               // Wait until it is linked
+               if(!this.havocbot_personal_waypoint.wplinked)
+               {
+                       LOG_TRACE(this.netname, " waiting for personal waypoint to be linked");
+                       return CMD_STATUS_EXECUTING;
+               }
+
+               this.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame
+               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
+
+               // Step 3: Route to waypoint
+               LOG_TRACE(this.netname, " walking to its personal waypoint");
+
+               return CMD_STATUS_EXECUTING;
+       }
+
+       // Step 1: Spawning waypoint
+       wp = waypoint_spawnpersonal(this, pos);
+       if(wp==NULL)
+       {
+               LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos));
+               return CMD_STATUS_ERROR;
+       }
+
+       this.havocbot_personal_waypoint = wp;
+       this.havocbot_personal_waypoint_failcounter = 0;
+       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+
+       // if pos is inside a teleport, then let's mark it as teleport waypoint
+       IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(pos, pos, it, NULL),
+       {
+               wp.wpflags |= WAYPOINTFLAG_TELEPORT;
+               this.lastteleporttime = 0;
+       });
+
+/*
+       if(wp.wpflags & WAYPOINTFLAG_TELEPORT)
+               print("routing to a teleporter\n");
+       else
+               print("routing to a non-teleporter\n");
+*/
+
+       return CMD_STATUS_EXECUTING;
+}
+
+float havocbot_resetgoal(entity this)
+{
+       navigation_clearroute(this);
+       return CMD_STATUS_FINISHED;
+}
+
+void havocbot_setupbot(entity this)
+{
+       this.bot_ai = havocbot_ai;
+       this.cmd_moveto = havocbot_moveto;
+       this.cmd_resetgoal = havocbot_resetgoal;
+
+       havocbot_chooserole(this);
+}
+
+vector havocbot_dodge(entity this)
+{
+       // LordHavoc: disabled because this is too expensive
+       return '0 0 0';
+#if 0
+       entity head;
+       vector dodge, v, n;
+       float danger, bestdanger, vl, d;
+       dodge = '0 0 0';
+       bestdanger = -20;
+       // check for dangerous objects near bot or approaching bot
+       head = findchainfloat(bot_dodge, true);
+       while(head)
+       {
+               if (head.owner != this)
+               {
+                       vl = vlen(head.velocity);
+                       if (vl > autocvar_sv_maxspeed * 0.3)
+                       {
+                               n = normalize(head.velocity);
+                               v = this.origin - head.origin;
+                               d = v * n;
+                               if (d > (0 - head.bot_dodgerating))
+                               if (d < (vl * 0.2 + head.bot_dodgerating))
+                               {
+                                       // calculate direction and distance from the flight path, by removing the forward axis
+                                       v = v - (n * (v * n));
+                                       danger = head.bot_dodgerating - vlen(v);
+                                       if (bestdanger < danger)
+                                       {
+                                               bestdanger = danger;
+                                               // dodge to the side of the object
+                                               dodge = normalize(v);
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               danger = head.bot_dodgerating - vlen(head.origin - this.origin);
+                               if (bestdanger < danger)
+                               {
+                                       bestdanger = danger;
+                                       dodge = normalize(this.origin - head.origin);
+                               }
+                       }
+               }
+               head = head.chain;
+       }
+       return dodge;
+#endif
+}
diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qh b/qcsrc/server/bot/default/havocbot/havocbot.qh
new file mode 100644 (file)
index 0000000..4a391b6
--- /dev/null
@@ -0,0 +1,64 @@
+#pragma once
+
+/*
+ * Globals and Fields
+ */
+
+.float havocbot_keyboardskill;
+.float facingwalltime, ignoregoaltime;
+.float lastfiredweapon;
+.float lastcombotime;
+.float havocbot_blockhead;
+
+.float havocbot_keyboardtime;
+.float havocbot_ducktime;
+.float bot_timelastseengoal;
+.float bot_canruntogoal;
+.float bot_chooseweapontime;
+.float rocketjumptime;
+.float nextaim;
+.float havocbot_personal_waypoint_searchtime;
+.float havocbot_personal_waypoint_failcounter;
+.float havocbot_chooseenemy_finished;
+.float havocbot_stickenemy;
+.float havocbot_role_timeout;
+
+.entity ignoregoal;
+.entity bot_lastseengoal;
+.entity havocbot_personal_waypoint;
+
+.vector havocbot_keyboard;
+
+/*
+ * Functions
+ */
+
+void havocbot_ai(entity this);
+void havocbot_aim(entity this);
+void havocbot_setupbot(entity this);
+void havocbot_movetogoal(entity this);
+void havocbot_chooserole(entity this);
+void havocbot_chooseenemy(entity this);
+void havocbot_chooseweapon(entity this);
+void havocbot_bunnyhop(entity this, vector dir);
+void havocbot_keyboard_movement(entity this, vector destorg);
+
+float havocbot_resetgoal(entity this);
+float havocbot_moveto(entity this, vector pos);
+float havocbot_moveto_refresh_route(entity this);
+
+vector havocbot_dodge(entity this);
+
+.void(entity this) havocbot_role;
+.void(entity this) havocbot_previous_role;
+
+void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
+
+/*
+ * Imports
+ */
+
+.entity draggedby;
+.float ladder_time;
+.entity ladder_entity;
diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc
new file mode 100644 (file)
index 0000000..536c8b2
--- /dev/null
@@ -0,0 +1,197 @@
+#include "roles.qh"
+
+#include "havocbot.qh"
+
+#include "../cvars.qh"
+
+#include "../bot.qh"
+#include "../navigation.qh"
+
+.float max_armorvalue;
+.float havocbot_role_timeout;
+
+.void(entity this) havocbot_previous_role;
+.void(entity this) havocbot_role;
+
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
+{
+       float rating, d, discard, friend_distance, enemy_distance;
+       vector o;
+       ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
+
+       IL_EACH(g_items, it.bot_pickup,
+       {
+               o = (it.absmin + it.absmax) * 0.5;
+               friend_distance = 10000; enemy_distance = 10000;
+               rating = 0;
+
+               if(!it.solid || vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) )
+                       continue;
+
+               // Check if the item can be picked up safely
+               if(it.classname == "droppedweapon")
+               {
+                       traceline(o, o + '0 0 -1500', true, NULL);
+
+                       d = pointcontents(trace_endpos + '0 0 1');
+                       if(d == CONTENT_WATER || d == CONTENT_SLIME || d == CONTENT_LAVA)
+                               continue;
+                       if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
+                               continue;
+               }
+               else
+               {
+                       // Ignore items under water
+                       traceline(it.origin + it.maxs, it.origin + it.maxs, MOVE_NORMAL, it);
+                       if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
+                               continue;
+               }
+
+               if(teamplay)
+               {
+                       discard = false;
+
+                       entity picker = it;
+                       FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it),
+                       {
+                               d = vlen(it.origin - o); // distance between player and item
+
+                               if ( it.team == this.team )
+                               {
+                                       if ( !IS_REAL_CLIENT(it) || discard )
+                                               continue;
+
+                                       if( d > friend_distance)
+                                               continue;
+
+                                       friend_distance = d;
+
+                                       discard = true;
+
+                                       if( picker.health && it.health > this.health )
+                                               continue;
+
+                                       if( picker.armorvalue && it.armorvalue > this.armorvalue)
+                                               continue;
+
+                                       if( picker.weapons )
+                                       if( picker.weapons & ~it.weapons )
+                                               continue;
+
+                                       if (picker.ammo_shells && it.ammo_shells > this.ammo_shells)
+                                               continue;
+
+                                       if (picker.ammo_nails && it.ammo_nails > this.ammo_nails)
+                                               continue;
+
+                                       if (picker.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
+                                               continue;
+
+                                       if (picker.ammo_cells && it.ammo_cells > this.ammo_cells)
+                                               continue;
+
+                                       if (picker.ammo_plasma && it.ammo_plasma > this.ammo_plasma)
+                                               continue;
+
+                                       discard = false;
+                               }
+                               else
+                               {
+                                       // If enemy only track distances
+                                       // TODO: track only if visible ?
+                                       if( d < enemy_distance )
+                                               enemy_distance = d;
+                               }
+                       });
+
+                       // Rate the item only if no one needs it, or if an enemy is closer to it
+                       if ( (enemy_distance < friend_distance && vdist(o - org, <, enemy_distance)) ||
+                               (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
+                               rating = it.bot_pickupevalfunc(this, it);
+
+               }
+               else
+                       rating = it.bot_pickupevalfunc(this, it);
+
+               if(rating > 0)
+                       navigation_routerating(this, it, rating * ratingscale, 2000);
+       });
+}
+
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius)
+{
+       if (autocvar_bot_nofire)
+               return;
+
+       // don't chase players if we're under water
+       if(this.waterlevel>WATERLEVEL_WETFEET)
+               return;
+
+       int t;
+
+       FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), LAMBDA(
+               // TODO: Merge this logic with the bot_shouldattack function
+               if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius))
+                       continue;
+
+               // rate only visible enemies
+               /*
+               traceline(this.origin + this.view_ofs, it.origin, MOVE_NOMONSTERS, this);
+               if (trace_fraction < 1 || trace_ent != it)
+                       continue;
+               */
+
+               if((it.flags & FL_INWATER) || (it.flags & FL_PARTIALGROUND))
+                       continue;
+
+               // not falling
+               if(!IS_ONGROUND(it))
+               {
+                       traceline(it.origin, it.origin + '0 0 -1500', true, NULL);
+                       t = pointcontents(trace_endpos + '0 0 1');
+                       if(t != CONTENT_SOLID )
+                       if(t == CONTENT_WATER || t == CONTENT_SLIME || t == CONTENT_LAVA)
+                               continue;
+                       if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
+                               continue;
+               }
+
+               // TODO: rate waypoints near the targeted player at that moment, instead of the player itself
+               //               adding a player as a goal seems to be quite dangerous, especially on space maps
+               //               remove hack in navigation_poptouchedgoals() after performing this change
+
+               t = (this.health + this.armorvalue ) / (it.health + it.armorvalue );
+               navigation_routerating(this, it, t * ratingscale, 2000);
+       ));
+}
+
+// legacy bot role for standard gamemodes
+// go to best items
+void havocbot_role_generic(entity this)
+{
+       if(IS_DEAD(this))
+               return;
+
+       if (this.bot_strategytime < time)
+       {
+               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start(this);
+               havocbot_goalrating_items(this, 10000, this.origin, 10000);
+               havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000);
+               //havocbot_goalrating_waypoints(1, this.origin, 1000);
+               navigation_goalrating_end(this);
+       }
+}
+
+void havocbot_chooserole_generic(entity this)
+{
+       this.havocbot_role = havocbot_role_generic;
+}
+
+void havocbot_chooserole(entity this)
+{
+       LOG_TRACE("choosing a role...");
+       this.bot_strategytime = 0;
+       if(!MUTATOR_CALLHOOK(HavocBot_ChooseRole, this))
+               havocbot_chooserole_generic(this);
+}
diff --git a/qcsrc/server/bot/default/havocbot/roles.qh b/qcsrc/server/bot/default/havocbot/roles.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/bot/default/havocbot/scripting.qh b/qcsrc/server/bot/default/havocbot/scripting.qh
new file mode 100644 (file)
index 0000000..07cb4d6
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void bot_clearqueue(entity bot);
diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc
new file mode 100644 (file)
index 0000000..4c8982e
--- /dev/null
@@ -0,0 +1,1219 @@
+#include "navigation.qh"
+
+#include "cvars.qh"
+
+#include "bot.qh"
+#include "waypoints.qh"
+
+#include <common/t_items.qh>
+
+#include <common/items/_mod.qh>
+
+#include <common/constants.qh>
+#include <common/net_linked.qh>
+#include <common/triggers/trigger/jumppads.qh>
+
+.float speed;
+
+// rough simulation of walking from one point to another to test if a path
+// can be traveled, used for waypoint linking and havocbot
+
+bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode)
+{
+       vector org;
+       vector move;
+       vector dir;
+       float dist;
+       float totaldist;
+       float stepdist;
+       float yaw;
+       float ignorehazards;
+       float swimming;
+
+       if(autocvar_bot_debug_tracewalk)
+       {
+               debugresetnodes();
+               debugnode(e, start);
+       }
+
+       move = end - start;
+       move.z = 0;
+       org = start;
+       dist = totaldist = vlen(move);
+       dir = normalize(move);
+       stepdist = 32;
+       ignorehazards = false;
+       swimming = false;
+
+       // Analyze starting point
+       traceline(start, start, MOVE_NORMAL, e);
+       if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
+               ignorehazards = true;
+       else
+       {
+               traceline( start, start + '0 0 -65536', MOVE_NORMAL, e);
+               if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
+               {
+                       ignorehazards = true;
+                       swimming = true;
+               }
+       }
+       tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e);
+       if (trace_startsolid)
+       {
+               // Bad start
+               if(autocvar_bot_debug_tracewalk)
+                       debugnodestatus(start, DEBUG_NODE_FAIL);
+
+               //print("tracewalk: ", vtos(start), " is a bad start\n");
+               return false;
+       }
+
+       // Movement loop
+       yaw = vectoyaw(move);
+       move = end - org;
+       for (;;)
+       {
+               if (boxesoverlap(end, end, org + m1 + '-1 -1 -1', org + m2 + '1 1 1'))
+               {
+                       // Succeeded
+                       if(autocvar_bot_debug_tracewalk)
+                               debugnodestatus(org, DEBUG_NODE_SUCCESS);
+
+                       //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
+                       return true;
+               }
+               if(autocvar_bot_debug_tracewalk)
+                       debugnode(e, org);
+
+               if (dist <= 0)
+                       break;
+               if (stepdist > dist)
+                       stepdist = dist;
+               dist = dist - stepdist;
+               traceline(org, org, MOVE_NORMAL, e);
+               if (!ignorehazards)
+               {
+                       if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
+                       {
+                               // hazards blocking path
+                               if(autocvar_bot_debug_tracewalk)
+                                       debugnodestatus(org, DEBUG_NODE_FAIL);
+
+                               //print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n");
+                               return false;
+                       }
+               }
+               if (trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
+               {
+                       move = normalize(end - org);
+                       tracebox(org, m1, m2, org + move * stepdist, movemode, e);
+
+                       if(autocvar_bot_debug_tracewalk)
+                               debugnode(e, trace_endpos);
+
+                       if (trace_fraction < 1)
+                       {
+                               swimming = true;
+                               org = trace_endpos - normalize(org - trace_endpos) * stepdist;
+                               for (; org.z < end.z + e.maxs.z; org.z += stepdist)
+                               {
+                                               if(autocvar_bot_debug_tracewalk)
+                                                       debugnode(e, org);
+
+                                       if(pointcontents(org) == CONTENT_EMPTY)
+                                                       break;
+                               }
+
+                               if(pointcontents(org + '0 0 1') != CONTENT_EMPTY)
+                               {
+                                       if(autocvar_bot_debug_tracewalk)
+                                               debugnodestatus(org, DEBUG_NODE_FAIL);
+
+                                       return false;
+                                       //print("tracewalk: ", vtos(start), " failed under water\n");
+                               }
+                               continue;
+
+                       }
+                       else
+                               org = trace_endpos;
+               }
+               else
+               {
+                       move = dir * stepdist + org;
+                       tracebox(org, m1, m2, move, movemode, e);
+
+                       if(autocvar_bot_debug_tracewalk)
+                               debugnode(e, trace_endpos);
+
+                       // hit something
+                       if (trace_fraction < 1)
+                       {
+                               // check if we can walk over this obstacle, possibly by jumpstepping
+                               tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
+                               if (trace_fraction < 1 || trace_startsolid)
+                               {
+                                       tracebox(org + jumpstepheightvec, m1, m2, move + jumpstepheightvec, movemode, e);
+                                       if (trace_fraction < 1 || trace_startsolid)
+                                       {
+                                               if(autocvar_bot_debug_tracewalk)
+                                                       debugnodestatus(trace_endpos, DEBUG_NODE_WARNING);
+
+                                               // check for doors
+                                               traceline( org, move, movemode, e);
+                                               if ( trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
+                                               {
+                                                       vector nextmove;
+                                                       move = trace_endpos;
+                                                       while(trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
+                                                       {
+                                                               nextmove = move + (dir * stepdist);
+                                                               traceline( move, nextmove, movemode, e);
+                                                               move = nextmove;
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       if(autocvar_bot_debug_tracewalk)
+                                                               debugnodestatus(trace_endpos, DEBUG_NODE_FAIL);
+
+                                                       //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
+                                                       //te_explosion(trace_endpos);
+                                                       //print(ftos(e.dphitcontentsmask), "\n");
+                                                       return false; // failed
+                                               }
+                                       }
+                                       else
+                                               move = trace_endpos;
+                               }
+                               else
+                                       move = trace_endpos;
+                       }
+                       else
+                               move = trace_endpos;
+
+                       // trace down from stepheight as far as possible and move there,
+                       // if this starts in solid we try again without the stepup, and
+                       // if that also fails we assume it is a wall
+                       // (this is the same logic as the Quake walkmove function used)
+                       tracebox(move, m1, m2, move + '0 0 -65536', movemode, e);
+
+                       // moved successfully
+                       if(swimming)
+                       {
+                               float c;
+                               c = pointcontents(org + '0 0 1');
+                               if (!(c == CONTENT_WATER || c == CONTENT_LAVA || c == CONTENT_SLIME))
+                                       swimming = false;
+                               else
+                                       continue;
+                       }
+
+                       org = trace_endpos;
+               }
+       }
+
+       //print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n");
+
+       // moved but didn't arrive at the intended destination
+       if(autocvar_bot_debug_tracewalk)
+               debugnodestatus(org, DEBUG_NODE_FAIL);
+
+       return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// goal stack
+/////////////////////////////////////////////////////////////////////////////
+
+// completely empty the goal stack, used when deciding where to go
+void navigation_clearroute(entity this)
+{
+       //print("bot ", etos(this), " clear\n");
+       this.navigation_hasgoals = false;
+       this.goalcurrent = NULL;
+       this.goalstack01 = NULL;
+       this.goalstack02 = NULL;
+       this.goalstack03 = NULL;
+       this.goalstack04 = NULL;
+       this.goalstack05 = NULL;
+       this.goalstack06 = NULL;
+       this.goalstack07 = NULL;
+       this.goalstack08 = NULL;
+       this.goalstack09 = NULL;
+       this.goalstack10 = NULL;
+       this.goalstack11 = NULL;
+       this.goalstack12 = NULL;
+       this.goalstack13 = NULL;
+       this.goalstack14 = NULL;
+       this.goalstack15 = NULL;
+       this.goalstack16 = NULL;
+       this.goalstack17 = NULL;
+       this.goalstack18 = NULL;
+       this.goalstack19 = NULL;
+       this.goalstack20 = NULL;
+       this.goalstack21 = NULL;
+       this.goalstack22 = NULL;
+       this.goalstack23 = NULL;
+       this.goalstack24 = NULL;
+       this.goalstack25 = NULL;
+       this.goalstack26 = NULL;
+       this.goalstack27 = NULL;
+       this.goalstack28 = NULL;
+       this.goalstack29 = NULL;
+       this.goalstack30 = NULL;
+       this.goalstack31 = NULL;
+}
+
+// add a new goal at the beginning of the stack
+// (in other words: add a new prerequisite before going to the later goals)
+// NOTE: when a waypoint is added, the WP gets pushed first, then the
+// next-closest WP on the shortest path to the WP
+// That means, if the stack overflows, the bot will know how to do the FIRST 32
+// steps to the goal, and then recalculate the path.
+void navigation_pushroute(entity this, entity e)
+{
+       //print("bot ", etos(this), " push ", etos(e), "\n");
+       this.goalstack31 = this.goalstack30;
+       this.goalstack30 = this.goalstack29;
+       this.goalstack29 = this.goalstack28;
+       this.goalstack28 = this.goalstack27;
+       this.goalstack27 = this.goalstack26;
+       this.goalstack26 = this.goalstack25;
+       this.goalstack25 = this.goalstack24;
+       this.goalstack24 = this.goalstack23;
+       this.goalstack23 = this.goalstack22;
+       this.goalstack22 = this.goalstack21;
+       this.goalstack21 = this.goalstack20;
+       this.goalstack20 = this.goalstack19;
+       this.goalstack19 = this.goalstack18;
+       this.goalstack18 = this.goalstack17;
+       this.goalstack17 = this.goalstack16;
+       this.goalstack16 = this.goalstack15;
+       this.goalstack15 = this.goalstack14;
+       this.goalstack14 = this.goalstack13;
+       this.goalstack13 = this.goalstack12;
+       this.goalstack12 = this.goalstack11;
+       this.goalstack11 = this.goalstack10;
+       this.goalstack10 = this.goalstack09;
+       this.goalstack09 = this.goalstack08;
+       this.goalstack08 = this.goalstack07;
+       this.goalstack07 = this.goalstack06;
+       this.goalstack06 = this.goalstack05;
+       this.goalstack05 = this.goalstack04;
+       this.goalstack04 = this.goalstack03;
+       this.goalstack03 = this.goalstack02;
+       this.goalstack02 = this.goalstack01;
+       this.goalstack01 = this.goalcurrent;
+       this.goalcurrent = e;
+}
+
+// remove first goal from stack
+// (in other words: remove a prerequisite for reaching the later goals)
+// (used when a spawnfunc_waypoint is reached)
+void navigation_poproute(entity this)
+{
+       //print("bot ", etos(this), " pop\n");
+       this.goalcurrent = this.goalstack01;
+       this.goalstack01 = this.goalstack02;
+       this.goalstack02 = this.goalstack03;
+       this.goalstack03 = this.goalstack04;
+       this.goalstack04 = this.goalstack05;
+       this.goalstack05 = this.goalstack06;
+       this.goalstack06 = this.goalstack07;
+       this.goalstack07 = this.goalstack08;
+       this.goalstack08 = this.goalstack09;
+       this.goalstack09 = this.goalstack10;
+       this.goalstack10 = this.goalstack11;
+       this.goalstack11 = this.goalstack12;
+       this.goalstack12 = this.goalstack13;
+       this.goalstack13 = this.goalstack14;
+       this.goalstack14 = this.goalstack15;
+       this.goalstack15 = this.goalstack16;
+       this.goalstack16 = this.goalstack17;
+       this.goalstack17 = this.goalstack18;
+       this.goalstack18 = this.goalstack19;
+       this.goalstack19 = this.goalstack20;
+       this.goalstack20 = this.goalstack21;
+       this.goalstack21 = this.goalstack22;
+       this.goalstack22 = this.goalstack23;
+       this.goalstack23 = this.goalstack24;
+       this.goalstack24 = this.goalstack25;
+       this.goalstack25 = this.goalstack26;
+       this.goalstack26 = this.goalstack27;
+       this.goalstack27 = this.goalstack28;
+       this.goalstack28 = this.goalstack29;
+       this.goalstack29 = this.goalstack30;
+       this.goalstack30 = this.goalstack31;
+       this.goalstack31 = NULL;
+}
+
+float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist)
+{
+       float dist;
+       dist = vlen(v - org);
+       if (bestdist > dist)
+       {
+               traceline(v, org, true, ent);
+               if (trace_fraction == 1)
+               {
+                       if (walkfromwp)
+                       {
+                               if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, org, bot_navigation_movemode))
+                                       return true;
+                       }
+                       else
+                       {
+                               if (tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, v, bot_navigation_movemode))
+                                       return true;
+                       }
+               }
+       }
+       return false;
+}
+
+// find the spawnfunc_waypoint near a dynamic goal such as a dropped weapon
+entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except)
+{
+       vector pm1 = ent.origin + ent.mins;
+       vector pm2 = ent.origin + ent.maxs;
+
+       // do two scans, because box test is cheaper
+       IL_EACH(g_waypoints, it != ent && it != except,
+       {
+               if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
+                       return it;
+       });
+
+       vector org = ent.origin + 0.5 * (ent.mins + ent.maxs);
+       org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
+       // TODO possibly make other code have the same support for bboxes
+       if(ent.tag_entity)
+               org = org + ent.tag_entity.origin;
+       if (navigation_testtracewalk)
+               te_plasmaburn(org);
+
+       entity best = NULL;
+       vector v;
+
+       // box check failed, try walk
+       IL_EACH(g_waypoints, it != ent,
+       {
+               if(it.wpisbox)
+               {
+                       vector wm1 = it.origin + it.mins;
+                       vector wm2 = it.origin + it.maxs;
+                       v.x = bound(wm1_x, org.x, wm2_x);
+                       v.y = bound(wm1_y, org.y, wm2_y);
+                       v.z = bound(wm1_z, org.z, wm2_z);
+               }
+               else
+                       v = it.origin;
+               if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist))
+               {
+                       bestdist = vlen(v - org);
+                       best = it;
+               }
+       });
+       return best;
+}
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
+{
+       entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL);
+       if (autocvar_g_waypointeditor_auto)
+       {
+               entity wp2 = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, wp);
+               if (wp && !wp2)
+                       wp.wpflags |= WAYPOINTFLAG_PROTECTED;
+       }
+       return wp;
+}
+
+// finds the waypoints near the bot initiating a navigation query
+float navigation_markroutes_nearestwaypoints(entity this, float maxdist)
+{
+       vector v, m1, m2;
+//     navigation_testtracewalk = true;
+       int c = 0;
+       IL_EACH(g_waypoints, !it.wpconsidered,
+       {
+               if (it.wpisbox)
+               {
+                       m1 = it.origin + it.mins;
+                       m2 = it.origin + it.maxs;
+                       v = this.origin;
+                       v.x = bound(m1_x, v.x, m2_x);
+                       v.y = bound(m1_y, v.y, m2_y);
+                       v.z = bound(m1_z, v.z, m2_z);
+               }
+               else
+                       v = it.origin;
+               vector diff = v - this.origin;
+               diff.z = max(0, diff.z);
+               if(vdist(diff, <, maxdist))
+               {
+                       it.wpconsidered = true;
+                       if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode))
+                       {
+                               it.wpnearestpoint = v;
+                               it.wpcost = vlen(v - this.origin) + it.dmg;
+                               it.wpfire = 1;
+                               it.enemy = NULL;
+                               c = c + 1;
+                       }
+               }
+       });
+       //navigation_testtracewalk = false;
+       return c;
+}
+
+// updates a path link if a spawnfunc_waypoint link is better than the current one
+void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p)
+{
+       vector m1;
+       vector m2;
+       vector v;
+       if (wp.wpisbox)
+       {
+               m1 = wp.absmin;
+               m2 = wp.absmax;
+               v.x = bound(m1_x, p.x, m2_x);
+               v.y = bound(m1_y, p.y, m2_y);
+               v.z = bound(m1_z, p.z, m2_z);
+       }
+       else
+               v = wp.origin;
+       cost2 = cost2 + vlen(v - p);
+       if (wp.wpcost > cost2)
+       {
+               wp.wpcost = cost2;
+               wp.enemy = w;
+               wp.wpfire = 1;
+               wp.wpnearestpoint = v;
+       }
+}
+
+// queries the entire spawnfunc_waypoint network for pathes leading away from the bot
+void navigation_markroutes(entity this, entity fixed_source_waypoint)
+{
+       float cost, cost2;
+       vector p;
+
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpconsidered = false;
+               it.wpnearestpoint = '0 0 0';
+               it.wpcost = 10000000;
+               it.wpfire = 0;
+               it.enemy = NULL;
+       });
+
+       if(fixed_source_waypoint)
+       {
+               fixed_source_waypoint.wpconsidered = true;
+               fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
+               fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg;
+               fixed_source_waypoint.wpfire = 1;
+               fixed_source_waypoint.enemy = NULL;
+       }
+       else
+       {
+               // try a short range search for the nearest waypoints, and expand the search repeatedly if none are found
+               // as this search is expensive we will use lower values if the bot is on the air
+               float increment, maxdistance;
+               if(IS_ONGROUND(this))
+               {
+                       increment = 750;
+                       maxdistance = 50000;
+               }
+               else
+               {
+                       increment = 500;
+                       maxdistance = 1500;
+               }
+
+               for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment);
+       }
+
+       bool searching = true;
+       while (searching)
+       {
+               searching = false;
+               IL_EACH(g_waypoints, it.wpfire,
+               {
+                       searching = true;
+                       it.wpfire = 0;
+                       cost = it.wpcost;
+                       p = it.wpnearestpoint;
+                       entity wp;
+                       wp = it.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp00mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp01mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp02mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp03mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp04mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp05mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp06mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp07mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp08mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp09mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp10mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp11mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp12mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp13mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp14mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp15mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp16mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp17mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp18mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp19mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp20mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp21mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp22mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp23mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp24mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp25mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp26mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp27mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp28mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp29mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp30mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       wp = it.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp31mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
+                       }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+               });
+       }
+}
+
+// queries the entire spawnfunc_waypoint network for pathes leading to the bot
+void navigation_markroutes_inverted(entity fixed_source_waypoint)
+{
+       float cost, cost2;
+       vector p;
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpconsidered = false;
+               it.wpnearestpoint = '0 0 0';
+               it.wpcost = 10000000;
+               it.wpfire = 0;
+               it.enemy = NULL;
+       });
+
+       if(fixed_source_waypoint)
+       {
+               fixed_source_waypoint.wpconsidered = true;
+               fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
+               fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; // the cost to get from X to fixed_source_waypoint
+               fixed_source_waypoint.wpfire = 1;
+               fixed_source_waypoint.enemy = NULL;
+       }
+       else
+       {
+               error("need to start with a waypoint\n");
+       }
+
+       bool searching = true;
+       while (searching)
+       {
+               searching = false;
+               IL_EACH(g_waypoints, it.wpfire,
+               {
+                       searching = true;
+                       it.wpfire = 0;
+                       cost = it.wpcost; // cost to walk from it to home
+                       p = it.wpnearestpoint;
+                       entity wp = it;
+                       IL_EACH(g_waypoints, true,
+                       {
+                               if(wp != it.wp00) if(wp != it.wp01) if(wp != it.wp02) if(wp != it.wp03)
+                               if(wp != it.wp04) if(wp != it.wp05) if(wp != it.wp06) if(wp != it.wp07)
+                               if(wp != it.wp08) if(wp != it.wp09) if(wp != it.wp10) if(wp != it.wp11)
+                               if(wp != it.wp12) if(wp != it.wp13) if(wp != it.wp14) if(wp != it.wp15)
+                               if(wp != it.wp16) if(wp != it.wp17) if(wp != it.wp18) if(wp != it.wp19)
+                               if(wp != it.wp20) if(wp != it.wp21) if(wp != it.wp22) if(wp != it.wp23)
+                               if(wp != it.wp24) if(wp != it.wp25) if(wp != it.wp26) if(wp != it.wp27)
+                               if(wp != it.wp28) if(wp != it.wp29) if(wp != it.wp30) if(wp != it.wp31)
+                                       continue;
+                               cost2 = cost + it.dmg;
+                               navigation_markroutes_checkwaypoint(wp, it, cost2, p);
+                       });
+               });
+       }
+}
+
+// updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item
+void navigation_routerating(entity this, entity e, float f, float rangebias)
+{
+       entity nwp;
+       vector o;
+       if (!e)
+               return;
+
+       if(e.blacklisted)
+               return;
+
+       o = (e.absmin + e.absmax) * 0.5;
+
+       //print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n");
+
+       // Evaluate path using jetpack
+       if(g_jetpack)
+       if(this.items & IT_JETPACK)
+       if(autocvar_bot_ai_navigation_jetpack)
+       if(vdist(this.origin - o, >, autocvar_bot_ai_navigation_jetpack_mindistance))
+       {
+               vector pointa, pointb;
+
+               LOG_DEBUG("jetpack ai: evaluating path for ", e.classname);
+
+               // Point A
+               traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this);
+               pointa = trace_endpos - '0 0 1';
+
+               // Point B
+               traceline(o, o + '0 0 65535', MOVE_NORMAL, e);
+               pointb = trace_endpos - '0 0 1';
+
+               // Can I see these two points from the sky?
+               traceline(pointa, pointb, MOVE_NORMAL, this);
+
+               if(trace_fraction==1)
+               {
+                       LOG_DEBUG("jetpack ai: can bridge these two points");
+
+                       // Lower the altitude of these points as much as possible
+                       float zdistance, xydistance, cost, t, fuel;
+                       vector down, npa, npb;
+
+                       down = '0 0 -1' * (STAT(PL_MAX, this).z - STAT(PL_MIN, this).z) * 10;
+
+                       do{
+                               npa = pointa + down;
+                               npb = pointb + down;
+
+                               if(npa.z<=this.absmax.z)
+                                       break;
+
+                               if(npb.z<=e.absmax.z)
+                                       break;
+
+                               traceline(npa, npb, MOVE_NORMAL, this);
+                               if(trace_fraction==1)
+                               {
+                                       pointa = npa;
+                                       pointb = npb;
+                               }
+                       }
+                       while(trace_fraction == 1);
+
+
+                       // Rough estimation of fuel consumption
+                       // (ignores acceleration and current xyz velocity)
+                       xydistance = vlen(pointa - pointb);
+                       zdistance = fabs(pointa.z - this.origin.z);
+
+                       t = zdistance / autocvar_g_jetpack_maxspeed_up;
+                       t += xydistance / autocvar_g_jetpack_maxspeed_side;
+                       fuel = t * autocvar_g_jetpack_fuel * 0.8;
+
+                       LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel));
+
+                       // enough fuel ?
+                       if(this.ammo_fuel>fuel)
+                       {
+                               // Estimate cost
+                               // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship
+                               //  - between air and ground speeds)
+
+                               cost = xydistance / (autocvar_g_jetpack_maxspeed_side/autocvar_sv_maxspeed);
+                               cost += zdistance / (autocvar_g_jetpack_maxspeed_up/autocvar_sv_maxspeed);
+                               cost *= 1.5;
+
+                               // Compare against other goals
+                               f = f * rangebias / (rangebias + cost);
+
+                               if (navigation_bestrating < f)
+                               {
+                                       LOG_DEBUG("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")");
+                                       navigation_bestrating = f;
+                                       navigation_bestgoal = e;
+                                       this.navigation_jetpack_goal = e;
+                                       this.navigation_jetpack_point = pointb;
+                               }
+                               return;
+                       }
+               }
+       }
+
+       //te_wizspike(e.origin);
+       //bprint(etos(e));
+       //bprint("\n");
+       // update the cached spawnfunc_waypoint link on a dynamic item entity
+       if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL))
+       {
+               nwp = e;
+       }
+       else
+       {
+               float search;
+
+               search = true;
+
+               if(e.flags & FL_ITEM)
+               {
+                       if (!(e.flags & FL_WEAPON))
+                       if(e.nearestwaypoint)
+                               search = false;
+               }
+               else if (e.flags & FL_WEAPON)
+               {
+                       if(e.classname != "droppedweapon")
+                       if(e.nearestwaypoint)
+                               search = false;
+               }
+
+               if(search)
+               if (time > e.nearestwaypointtimeout)
+               {
+                       nwp = navigation_findnearestwaypoint(e, true);
+                       if(nwp)
+                               e.nearestwaypoint = nwp;
+                       else
+                       {
+                               LOG_DEBUG("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e));
+
+                               if(e.flags & FL_ITEM)
+                                       e.blacklisted = true;
+                               else if (e.flags & FL_WEAPON)
+                               {
+                                       if(e.classname != "droppedweapon")
+                                               e.blacklisted = true;
+                               }
+
+                               if(e.blacklisted)
+                               {
+                                       LOG_DEBUG("The entity '", e.classname, "' is going to be excluded from path finding during this match");
+                                       return;
+                               }
+                       }
+
+                       // TODO: Cleaner solution, probably handling this timeout from ctf.qc
+                       if(e.classname=="item_flag_team")
+                               e.nearestwaypointtimeout = time + 2;
+                       else
+                               e.nearestwaypointtimeout = time + random() * 3 + 5;
+               }
+               nwp = e.nearestwaypoint;
+       }
+
+       LOG_DEBUG("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")");
+       if (nwp)
+       if (nwp.wpcost < 10000000)
+       {
+               //te_wizspike(nwp.wpnearestpoint);
+               LOG_DEBUG(e.classname, " ", ftos(f), "/(1+", ftos((nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint))), "/", ftos(rangebias), ") = ");
+               f = f * rangebias / (rangebias + (nwp.wpcost + vlen(o - nwp.wpnearestpoint)));
+               LOG_DEBUG("considering ", e.classname, " (with rating ", ftos(f), ")");
+               if (navigation_bestrating < f)
+               {
+                       LOG_DEBUG("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")");
+                       navigation_bestrating = f;
+                       navigation_bestgoal = e;
+               }
+       }
+}
+
+// adds an item to the the goal stack with the path to a given item
+bool navigation_routetogoal(entity this, entity e, vector startposition)
+{
+       this.goalentity = e;
+
+       // if there is no goal, just exit
+       if (!e)
+               return false;
+
+       this.navigation_hasgoals = true;
+
+       // put the entity on the goal stack
+       //print("routetogoal ", etos(e), "\n");
+       navigation_pushroute(this, e);
+
+       if(g_jetpack)
+       if(e==this.navigation_jetpack_goal)
+               return true;
+
+       // if it can reach the goal there is nothing more to do
+       if (tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
+               return true;
+
+       // see if there are waypoints describing a path to the item
+       if(e.classname != "waypoint" || (e.wpflags & WAYPOINTFLAG_PERSONAL))
+               e = e.nearestwaypoint;
+       else
+               e = e.enemy; // we already have added it, so...
+
+       if(e == NULL)
+               return false;
+
+       for (;;)
+       {
+               // add the spawnfunc_waypoint to the path
+               navigation_pushroute(this, e);
+               e = e.enemy;
+
+               if(e==NULL)
+                       break;
+       }
+
+       return false;
+}
+
+// removes any currently touching waypoints from the goal stack
+// (this is how bots detect if they reached a goal)
+void navigation_poptouchedgoals(entity this)
+{
+       vector org, m1, m2;
+       org = this.origin;
+       m1 = org + this.mins;
+       m2 = org + this.maxs;
+
+       while(this.goalcurrent && wasfreed(this.goalcurrent))
+               navigation_poproute(this);
+
+       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+       {
+               // make sure jumppad is really hit, don't rely on distance based checks
+               // as they may report a touch even if it didn't really happen
+               if(this.lastteleporttime>0)
+               if(time - this.lastteleporttime < ((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) ? 2 : 0.15))
+               {
+                       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+                       if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+                       {
+                               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+                       }
+                       navigation_poproute(this);
+                       return;
+               }
+       }
+
+       // If for some reason the bot is closer to the next goal, pop the current one
+       if(this.goalstack01 && !wasfreed(this.goalstack01))
+       if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin))
+       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
+       if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode))
+       {
+               LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue");
+               navigation_poproute(this);
+               // TODO this may also be a nice idea to do "early" (e.g. by
+               // manipulating the vlen() comparisons) to shorten paths in
+               // general - this would make bots walk more "on rails" than
+               // "zigzagging" which they currently do with sufficiently
+               // random-like waypoints, and thus can make a nice bot
+               // personality property
+       }
+
+       // HACK: remove players/bots as goals, they can lead a bot to unexpected places (cliffs, lava, etc)
+       // TODO: rate waypoints near the targetted player at that moment, instead of the player itself
+       if(IS_PLAYER(this.goalcurrent))
+               navigation_poproute(this);
+
+       // Loose goal touching check when running
+       if(this.aistatus & AI_STATUS_RUNNING)
+       if(this.speed >= autocvar_sv_maxspeed) // if -really- running
+       if(this.goalcurrent.classname=="waypoint")
+       if(!(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
+       {
+               if(vdist(this.origin - this.goalcurrent.origin, <, 150))
+               {
+                       traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL);
+                       if(trace_fraction==1)
+                       {
+                               // Detect personal waypoints
+                               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+                               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+                               {
+                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+                               }
+
+                               navigation_poproute(this);
+                       }
+               }
+       }
+
+       while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
+       {
+               if((this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
+                       break;
+
+               // Detect personal waypoints
+               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
+               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+               {
+                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
+                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+               }
+
+               navigation_poproute(this);
+       }
+}
+
+// begin a goal selection session (queries spawnfunc_waypoint network)
+void navigation_goalrating_start(entity this)
+{
+       if(this.aistatus & AI_STATUS_STUCK)
+               return;
+
+       this.navigation_jetpack_goal = NULL;
+       navigation_bestrating = -1;
+       this.navigation_hasgoals = false;
+       navigation_clearroute(this);
+       navigation_bestgoal = NULL;
+       navigation_markroutes(this, NULL);
+}
+
+// ends a goal selection session (updates goal stack to the best goal)
+void navigation_goalrating_end(entity this)
+{
+       if(this.aistatus & AI_STATUS_STUCK)
+               return;
+
+       navigation_routetogoal(this, navigation_bestgoal, this.origin);
+       LOG_DEBUG("best goal ", this.goalcurrent.classname);
+
+       // If the bot got stuck then try to reach the farthest waypoint
+       if (!this.navigation_hasgoals)
+       if (autocvar_bot_wander_enable)
+       {
+               if (!(this.aistatus & AI_STATUS_STUCK))
+               {
+                       LOG_DEBUG(this.netname, " cannot walk to any goal");
+                       this.aistatus |= AI_STATUS_STUCK;
+               }
+
+               this.navigation_hasgoals = false; // Reset this value
+       }
+}
+
+void botframe_updatedangerousobjects(float maxupdate)
+{
+       vector m1, m2, v, o;
+       float c, d, danger;
+       c = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               danger = 0;
+               m1 = it.mins;
+               m2 = it.maxs;
+               IL_EACH(g_bot_dodge, it.bot_dodge,
+               {
+                       v = it.origin;
+                       v.x = bound(m1_x, v.x, m2_x);
+                       v.y = bound(m1_y, v.y, m2_y);
+                       v.z = bound(m1_z, v.z, m2_z);
+                       o = (it.absmin + it.absmax) * 0.5;
+                       d = it.bot_dodgerating - vlen(o - v);
+                       if (d > 0)
+                       {
+                               traceline(o, v, true, NULL);
+                               if (trace_fraction == 1)
+                                       danger = danger + d;
+                       }
+               });
+               it.dmg = danger;
+               c = c + 1;
+               if (c >= maxupdate)
+                       break;
+       });
+}
+
+void navigation_unstuck(entity this)
+{
+       float search_radius = 1000;
+
+       if (!autocvar_bot_wander_enable)
+               return;
+
+       if (!bot_waypoint_queue_owner)
+       {
+               LOG_DEBUG(this.netname, " stuck, taking over the waypoints queue");
+               bot_waypoint_queue_owner = this;
+               bot_waypoint_queue_bestgoal = NULL;
+               bot_waypoint_queue_bestgoalrating = 0;
+       }
+
+       if(bot_waypoint_queue_owner!=this)
+               return;
+
+       if (bot_waypoint_queue_goal)
+       {
+               // evaluate the next goal on the queue
+               float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
+               LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d));
+               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
+               {
+                       if( d > bot_waypoint_queue_bestgoalrating)
+                       {
+                               bot_waypoint_queue_bestgoalrating = d;
+                               bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal;
+                       }
+               }
+               bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal;
+
+               if (!bot_waypoint_queue_goal)
+               {
+                       if (bot_waypoint_queue_bestgoal)
+                       {
+                               LOG_DEBUG(this.netname, " stuck, reachable waypoint found, heading to it");
+                               navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin);
+                               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+                               this.aistatus &= ~AI_STATUS_STUCK;
+                       }
+                       else
+                       {
+                               LOG_DEBUG(this.netname, " stuck, cannot walk to any waypoint at all");
+                       }
+
+                       bot_waypoint_queue_owner = NULL;
+               }
+       }
+       else
+       {
+               if(bot_strategytoken!=this)
+                       return;
+
+               // build a new queue
+               LOG_DEBUG(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu");
+
+               entity first = NULL;
+
+               FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
+               {
+                       if(bot_waypoint_queue_goal)
+                               bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it;
+                       else
+                               first = it;
+
+                       bot_waypoint_queue_goal = it;
+                       bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
+               });
+
+               if (first)
+                       bot_waypoint_queue_goal = first;
+               else
+               {
+                       LOG_DEBUG(this.netname, " stuck, cannot walk to any waypoint at all");
+                       bot_waypoint_queue_owner = NULL;
+               }
+       }
+}
+
+// Support for debugging tracewalk visually
+
+void debugresetnodes()
+{
+       debuglastnode = '0 0 0';
+}
+
+void debugnode(entity this, vector node)
+{
+       if (!IS_PLAYER(this))
+               return;
+
+       if(debuglastnode=='0 0 0')
+       {
+               debuglastnode = node;
+               return;
+       }
+
+       te_lightning2(NULL, node, debuglastnode);
+       debuglastnode = node;
+}
+
+void debugnodestatus(vector position, float status)
+{
+       vector c;
+
+       switch (status)
+       {
+               case DEBUG_NODE_SUCCESS:
+                       c = '0 15 0';
+                       break;
+               case DEBUG_NODE_WARNING:
+                       c = '15 15 0';
+                       break;
+               case DEBUG_NODE_FAIL:
+                       c = '15 0 0';
+                       break;
+               default:
+                       c = '15 15 15';
+       }
+
+       te_customflash(position, 40,  2, c);
+}
+
+// Support for debugging the goal stack visually
+
+.float goalcounter;
+.vector lastposition;
+
+// Debug the goal stack visually
+void debuggoalstack(entity this)
+{
+       entity goal;
+       vector org, go;
+
+       if(this.goalcounter==0)goal=this.goalcurrent;
+       else if(this.goalcounter==1)goal=this.goalstack01;
+       else if(this.goalcounter==2)goal=this.goalstack02;
+       else if(this.goalcounter==3)goal=this.goalstack03;
+       else if(this.goalcounter==4)goal=this.goalstack04;
+       else if(this.goalcounter==5)goal=this.goalstack05;
+       else if(this.goalcounter==6)goal=this.goalstack06;
+       else if(this.goalcounter==7)goal=this.goalstack07;
+       else if(this.goalcounter==8)goal=this.goalstack08;
+       else if(this.goalcounter==9)goal=this.goalstack09;
+       else if(this.goalcounter==10)goal=this.goalstack10;
+       else if(this.goalcounter==11)goal=this.goalstack11;
+       else if(this.goalcounter==12)goal=this.goalstack12;
+       else if(this.goalcounter==13)goal=this.goalstack13;
+       else if(this.goalcounter==14)goal=this.goalstack14;
+       else if(this.goalcounter==15)goal=this.goalstack15;
+       else if(this.goalcounter==16)goal=this.goalstack16;
+       else if(this.goalcounter==17)goal=this.goalstack17;
+       else if(this.goalcounter==18)goal=this.goalstack18;
+       else if(this.goalcounter==19)goal=this.goalstack19;
+       else if(this.goalcounter==20)goal=this.goalstack20;
+       else if(this.goalcounter==21)goal=this.goalstack21;
+       else if(this.goalcounter==22)goal=this.goalstack22;
+       else if(this.goalcounter==23)goal=this.goalstack23;
+       else if(this.goalcounter==24)goal=this.goalstack24;
+       else if(this.goalcounter==25)goal=this.goalstack25;
+       else if(this.goalcounter==26)goal=this.goalstack26;
+       else if(this.goalcounter==27)goal=this.goalstack27;
+       else if(this.goalcounter==28)goal=this.goalstack28;
+       else if(this.goalcounter==29)goal=this.goalstack29;
+       else if(this.goalcounter==30)goal=this.goalstack30;
+       else if(this.goalcounter==31)goal=this.goalstack31;
+       else goal=NULL;
+
+       if(goal==NULL)
+       {
+               this.goalcounter = 0;
+               this.lastposition='0 0 0';
+               return;
+       }
+
+       if(this.lastposition=='0 0 0')
+               org = this.origin;
+       else
+               org = this.lastposition;
+
+
+       go = ( goal.absmin + goal.absmax ) * 0.5;
+       te_lightning2(NULL, org, go);
+       this.lastposition = go;
+
+       this.goalcounter++;
+}
diff --git a/qcsrc/server/bot/default/navigation.qh b/qcsrc/server/bot/default/navigation.qh
new file mode 100644 (file)
index 0000000..ad01776
--- /dev/null
@@ -0,0 +1,77 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+float navigation_bestrating;
+float bot_navigation_movemode;
+float navigation_testtracewalk;
+
+vector jumpstepheightvec;
+vector stepheightvec;
+
+entity navigation_bestgoal;
+
+// stack of current goals (the last one of which may be an item or other
+// desirable object, the rest are typically waypoints to reach it)
+.entity goalcurrent, goalstack01, goalstack02, goalstack03;
+.entity goalstack04, goalstack05, goalstack06, goalstack07;
+.entity goalstack08, goalstack09, goalstack10, goalstack11;
+.entity goalstack12, goalstack13, goalstack14, goalstack15;
+.entity goalstack16, goalstack17, goalstack18, goalstack19;
+.entity goalstack20, goalstack21, goalstack22, goalstack23;
+.entity goalstack24, goalstack25, goalstack26, goalstack27;
+.entity goalstack28, goalstack29, goalstack30, goalstack31;
+.entity nearestwaypoint;
+
+.float nearestwaypointtimeout;
+.float navigation_hasgoals;
+.float lastteleporttime;
+
+.float blacklisted;
+
+.entity navigation_jetpack_goal;
+.vector navigation_jetpack_point;
+
+const float DEBUG_NODE_SUCCESS        = 1;
+const float DEBUG_NODE_WARNING        = 2;
+const float DEBUG_NODE_FAIL           = 3;
+vector debuglastnode;
+
+entity bot_waypoint_queue_owner;       // Owner of the temporary list of goals
+entity bot_waypoint_queue_goal;                // Head of the temporary list of goals
+.entity bot_waypoint_queue_nextgoal;
+entity bot_waypoint_queue_bestgoal;
+float bot_waypoint_queue_bestgoalrating;
+
+/*
+ * Functions
+ */
+
+void debugresetnodes();
+void debugnode(entity this, vector node);
+void debugnodestatus(vector position, float status);
+
+void debuggoalstack(entity this);
+
+float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
+
+float navigation_markroutes_nearestwaypoints(entity this, float maxdist);
+float navigation_routetogoal(entity this, entity e, vector startposition);
+
+void navigation_clearroute(entity this);
+void navigation_pushroute(entity this, entity e);
+void navigation_poproute(entity this);
+void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p);
+void navigation_markroutes(entity this, entity fixed_source_waypoint);
+void navigation_markroutes_inverted(entity fixed_source_waypoint);
+void navigation_routerating(entity this, entity e, float f, float rangebias);
+void navigation_poptouchedgoals(entity this);
+void navigation_goalrating_start(entity this);
+void navigation_goalrating_end(entity this);
+void navigation_unstuck(entity this);
+
+void botframe_updatedangerousobjects(float maxupdate);
+
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp);
+float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist);
diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc
new file mode 100644 (file)
index 0000000..0d34ae3
--- /dev/null
@@ -0,0 +1,1333 @@
+#include "scripting.qh"
+
+#include "cvars.qh"
+
+#include <common/state.qh>
+#include <common/physics/player.qh>
+
+#include "bot.qh"
+
+.int state;
+
+.float bot_cmdqueuebuf_allocated;
+.float bot_cmdqueuebuf;
+.float bot_cmdqueuebuf_start;
+.float bot_cmdqueuebuf_end;
+
+void bot_clearqueue(entity bot)
+{
+       if(!bot.bot_cmdqueuebuf_allocated)
+               return;
+       buf_del(bot.bot_cmdqueuebuf);
+       bot.bot_cmdqueuebuf_allocated = false;
+       LOG_TRACE("bot ", bot.netname, " queue cleared");
+}
+
+void bot_queuecommand(entity bot, string cmdstring)
+{
+       if(!bot.bot_cmdqueuebuf_allocated)
+       {
+               bot.bot_cmdqueuebuf = buf_create();
+               bot.bot_cmdqueuebuf_allocated = true;
+               bot.bot_cmdqueuebuf_start = 0;
+               bot.bot_cmdqueuebuf_end = 0;
+       }
+
+       bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
+
+       // if the command was a "sound" command, precache the sound NOW
+       // this prevents lagging!
+       {
+               float sp;
+               string parm;
+               string cmdstr;
+
+               sp = strstrofs(cmdstring, " ", 0);
+               if(sp >= 0)
+               {
+                       parm = substring(cmdstring, sp + 1, -1);
+                       cmdstr = substring(cmdstring, 0, sp);
+                       if(cmdstr == "sound")
+                       {
+                               // find the LAST word
+                               for (;;)
+                               {
+                                       sp = strstrofs(parm, " ", 0);
+                                       if(sp < 0)
+                                               break;
+                                       parm = substring(parm, sp + 1, -1);
+                               }
+                               precache_sound(parm);
+                       }
+               }
+       }
+
+       bot.bot_cmdqueuebuf_end += 1;
+}
+
+void bot_dequeuecommand(entity bot, float idx)
+{
+       if(!bot.bot_cmdqueuebuf_allocated)
+               error("dequeuecommand but no queue allocated");
+       if(idx < bot.bot_cmdqueuebuf_start)
+               error("dequeueing a command in the past");
+       if(idx >= bot.bot_cmdqueuebuf_end)
+               error("dequeueing a command in the future");
+       bufstr_set(bot.bot_cmdqueuebuf, idx, "");
+       if(idx == bot.bot_cmdqueuebuf_start)
+               bot.bot_cmdqueuebuf_start += 1;
+       if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
+               bot_clearqueue(bot);
+}
+
+string bot_readcommand(entity bot, float idx)
+{
+       if(!bot.bot_cmdqueuebuf_allocated)
+               error("readcommand but no queue allocated");
+       if(idx < bot.bot_cmdqueuebuf_start)
+               error("reading a command in the past");
+       if(idx >= bot.bot_cmdqueuebuf_end)
+               error("reading a command in the future");
+       return bufstr_get(bot.bot_cmdqueuebuf, idx);
+}
+
+bool bot_havecommand(entity this, int idx)
+{
+       if(!this.bot_cmdqueuebuf_allocated)
+               return false;
+       if(idx < this.bot_cmdqueuebuf_start)
+               return false;
+       if(idx >= this.bot_cmdqueuebuf_end)
+               return false;
+       return true;
+}
+
+const int MAX_BOT_PLACES = 4;
+.float bot_places_count;
+.entity bot_places[MAX_BOT_PLACES];
+.string bot_placenames[MAX_BOT_PLACES];
+entity bot_getplace(entity this, string placename)
+{
+       entity e;
+       if(substring(placename, 0, 1) == "@")
+       {
+               int i, p;
+               placename = substring(placename, 1, -1);
+               string s, s2;
+               for(i = 0; i < this.bot_places_count; ++i)
+                       if(this.(bot_placenames[i]) == placename)
+                               return this.(bot_places[i]);
+               // now: i == this.bot_places_count
+               s = s2 = cvar_string(placename);
+               p = strstrofs(s2, " ", 0);
+               if(p >= 0)
+               {
+                       s = substring(s2, 0, p);
+                       //print("places: ", placename, " -> ", cvar_string(placename), "\n");
+                       cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
+                       //print("places: ", placename, " := ", cvar_string(placename), "\n");
+               }
+               e = find(NULL, targetname, s);
+               if(!e)
+                       LOG_INFO("invalid place ", s, "\n");
+               if(i < MAX_BOT_PLACES)
+               {
+                       this.(bot_placenames[i]) = strzone(placename);
+                       this.(bot_places[i]) = e;
+                       this.bot_places_count += 1;
+               }
+               return e;
+       }
+       else
+       {
+               e = find(NULL, targetname, placename);
+               if(!e)
+                       LOG_INFO("invalid place ", placename, "\n");
+               return e;
+       }
+}
+
+
+// Initialize global commands list
+// NOTE: New commands should be initialized here
+void bot_commands_init()
+{
+       bot_cmd_string[BOT_CMD_NULL] = "";
+       bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_PAUSE] = "pause";
+       bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
+       bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_WAIT] = "wait";
+       bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
+
+       bot_cmd_string[BOT_CMD_TURN] = "turn";
+       bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
+
+       bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
+       bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
+
+       bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
+       bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
+       bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_CC] = "cc";
+       bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_IF] = "if";
+       bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_ELSE] = "else";
+       bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_FI] = "fi";
+       bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
+       bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_AIM] = "aim";
+       bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
+       bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
+       bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
+       bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
+       bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
+
+       bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
+       bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
+
+       bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
+       bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
+
+       bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
+       bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
+
+       bot_cmd_string[BOT_CMD_CONSOLE] = "console";
+       bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_SOUND] = "sound";
+       bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
+
+       bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
+       bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
+
+       bot_cmds_initialized = true;
+}
+
+// Returns first bot with matching name
+entity find_bot_by_name(string name)
+{
+       FOREACH_CLIENT(IS_BOT_CLIENT(it) && it.netname == name,
+       {
+               return it;
+       });
+
+       return NULL;
+}
+
+// Returns a bot by number on list
+entity find_bot_by_number(float number)
+{
+       entity bot;
+       float c = 0;
+
+       if(!number)
+               return NULL;
+
+       bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities?
+       while (bot)
+       {
+               if(IS_BOT_CLIENT(bot))
+               {
+                       if(++c==number)
+                               return bot;
+               }
+               bot = bot.chain;
+       }
+
+       return NULL;
+}
+
+float bot_decodecommand(string cmdstring)
+{
+       float cmd_parm_type;
+       float sp;
+       string parm;
+
+       sp = strstrofs(cmdstring, " ", 0);
+       if(sp < 0)
+       {
+               parm = "";
+       }
+       else
+       {
+               parm = substring(cmdstring, sp + 1, -1);
+               cmdstring = substring(cmdstring, 0, sp);
+       }
+
+       if(!bot_cmds_initialized)
+               bot_commands_init();
+
+       int i;
+       for(i=1;i<BOT_CMD_COUNTER;++i)
+       {
+               if(bot_cmd_string[i]!=cmdstring)
+                       continue;
+
+               cmd_parm_type = bot_cmd_parm_type[i];
+
+               if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
+               {
+                       LOG_INFO("ERROR: A parameter is required for this command\n");
+                       return 0;
+               }
+
+               // Load command into queue
+               bot_cmd.bot_cmd_type = i;
+
+               // Attach parameter
+               switch(cmd_parm_type)
+               {
+                       case BOT_CMD_PARAMETER_FLOAT:
+                               bot_cmd.bot_cmd_parm_float = stof(parm);
+                               break;
+                       case BOT_CMD_PARAMETER_STRING:
+                               if(bot_cmd.bot_cmd_parm_string)
+                                       strunzone(bot_cmd.bot_cmd_parm_string);
+                               bot_cmd.bot_cmd_parm_string = strzone(parm);
+                               break;
+                       case BOT_CMD_PARAMETER_VECTOR:
+                               bot_cmd.bot_cmd_parm_vector = stov(parm);
+                               break;
+                       default:
+                               break;
+               }
+               return 1;
+       }
+       LOG_INFO("ERROR: No such command '", cmdstring, "'\n");
+       return 0;
+}
+
+void bot_cmdhelp(string scmd)
+{
+       int i, ntype;
+       string stype;
+
+       if(!bot_cmds_initialized)
+               bot_commands_init();
+
+       for(i=1;i<BOT_CMD_COUNTER;++i)
+       {
+               if(bot_cmd_string[i]!=scmd)
+                       continue;
+
+               ntype = bot_cmd_parm_type[i];
+
+               switch(ntype)
+               {
+                       case BOT_CMD_PARAMETER_FLOAT:
+                               stype = "float number";
+                               break;
+                       case BOT_CMD_PARAMETER_STRING:
+                               stype = "string";
+                               break;
+                       case BOT_CMD_PARAMETER_VECTOR:
+                               stype = "vector";
+                               break;
+                       default:
+                               stype = "none";
+                               break;
+               }
+
+               LOG_INFO(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
+
+               LOG_INFO("Description: ");
+               switch(i)
+               {
+                       case BOT_CMD_PAUSE:
+                               LOG_INFO("Stops the bot completely. Any command other than 'continue' will be ignored.");
+                               break;
+                       case BOT_CMD_CONTINUE:
+                               LOG_INFO("Disable paused status");
+                               break;
+                       case BOT_CMD_WAIT:
+                               LOG_INFO("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
+                               break;
+                       case BOT_CMD_WAIT_UNTIL:
+                               LOG_INFO("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
+                               break;
+                       case BOT_CMD_BARRIER:
+                               LOG_INFO("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
+                               break;
+                       case BOT_CMD_TURN:
+                               LOG_INFO("Look to the right or left N degrees. For turning to the left use positive numbers.");
+                               break;
+                       case BOT_CMD_MOVETO:
+                               LOG_INFO("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
+                               break;
+                       case BOT_CMD_MOVETOTARGET:
+                               LOG_INFO("Walk to the specific target on the map");
+                               break;
+                       case BOT_CMD_RESETGOAL:
+                               LOG_INFO("Resets the goal stack");
+                               break;
+                       case BOT_CMD_CC:
+                               LOG_INFO("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
+                               break;
+                       case BOT_CMD_IF:
+                               LOG_INFO("Perform simple conditional execution.\n");
+                               LOG_INFO("Syntax: \n");
+                               LOG_INFO("        sv_cmd .. if \"condition\"\n");
+                               LOG_INFO("        sv_cmd ..     <instruction if true>\n");
+                               LOG_INFO("        sv_cmd ..     <instruction if true>\n");
+                               LOG_INFO("        sv_cmd .. else\n");
+                               LOG_INFO("        sv_cmd ..     <instruction if false>\n");
+                               LOG_INFO("        sv_cmd ..     <instruction if false>\n");
+                               LOG_INFO("        sv_cmd .. fi\n");
+                               LOG_INFO("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
+                               LOG_INFO("            Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
+                               LOG_INFO("Fields: health, speed, flagcarrier\n");
+                               LOG_INFO("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
+                               break;
+                       case BOT_CMD_RESETAIM:
+                               LOG_INFO("Points the aim to the coordinates x,y 0,0");
+                               break;
+                       case BOT_CMD_AIM:
+                               LOG_INFO("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
+                               LOG_INFO("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
+                               LOG_INFO("Examples: aim \"90 0\"        // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
+                               LOG_INFO("          aim \"0 90 2\"      // Will gradually look to the sky in the next two seconds");
+                               break;
+                       case BOT_CMD_AIMTARGET:
+                               LOG_INFO("Points the aim to given target");
+                               break;
+                       case BOT_CMD_PRESSKEY:
+                               LOG_INFO("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
+                               LOG_INFO("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
+                               LOG_INFO("Note: The script will not return the control to the bot ai until all keys are released");
+                               break;
+                       case BOT_CMD_RELEASEKEY:
+                               LOG_INFO("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
+                               break;
+                       case BOT_CMD_SOUND:
+                               LOG_INFO("play sound file at bot location");
+                               break;
+                       case BOT_CMD_DEBUG_ASSERT_CANFIRE:
+                               LOG_INFO("verify the state of the weapon entity");
+                               break;
+                       default:
+                               LOG_INFO("This command has no description yet.");
+                               break;
+               }
+               LOG_INFO("\n");
+       }
+}
+
+void bot_list_commands()
+{
+       int i;
+       string ptype;
+
+       if(!bot_cmds_initialized)
+               bot_commands_init();
+
+       LOG_INFO("List of all available commands:\n");
+       LOG_INFO("  Command - Parameter Type\n");
+
+       for(i=1;i<BOT_CMD_COUNTER;++i)
+       {
+               switch(bot_cmd_parm_type[i])
+               {
+                       case BOT_CMD_PARAMETER_FLOAT:
+                               ptype = "float number";
+                               break;
+                       case BOT_CMD_PARAMETER_STRING:
+                               ptype = "string";
+                               break;
+                       case BOT_CMD_PARAMETER_VECTOR:
+                               ptype = "vector";
+                               break;
+                       default:
+                               ptype = "none";
+                               break;
+               }
+               LOG_INFO(strcat("  ",bot_cmd_string[i]," - <",ptype,"> \n"));
+       }
+}
+
+// Commands code
+.int bot_exec_status;
+
+float bot_cmd_cc(entity this)
+{
+       SV_ParseClientCommand(this, bot_cmd.bot_cmd_parm_string);
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_impulse(entity this)
+{
+       this.impulse = bot_cmd.bot_cmd_parm_float;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_continue(entity this)
+{
+       bot_relinkplayerlist();
+       this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
+       return CMD_STATUS_FINISHED;
+}
+
+.float bot_cmd_wait_time;
+float bot_cmd_wait(entity this)
+{
+       if(this.bot_exec_status & BOT_EXEC_STATUS_WAITING)
+       {
+               if(time>=this.bot_cmd_wait_time)
+               {
+                       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
+                       return CMD_STATUS_FINISHED;
+               }
+               else
+                       return CMD_STATUS_EXECUTING;
+       }
+
+       this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
+       this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
+       return CMD_STATUS_EXECUTING;
+}
+
+float bot_cmd_wait_until(entity this)
+{
+       if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
+       {
+               this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
+               return CMD_STATUS_EXECUTING;
+       }
+       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_barrier(entity this)
+{
+       // 0 = no barrier, 1 = waiting, 2 = waiting finished
+
+       if(this.bot_barrier == 0) // initialization
+       {
+               this.bot_barrier = 1;
+
+               //this.colormod = '4 4 0';
+       }
+
+       if(this.bot_barrier == 1) // find other bots
+       {
+               FOREACH_CLIENT(it.isbot, LAMBDA(
+                       if(it.bot_cmdqueuebuf_allocated)
+                       if(it.bot_barrier != 1)
+                               return CMD_STATUS_EXECUTING; // not all are at the barrier yet
+               ));
+
+               // all bots hit the barrier!
+
+               // acknowledge barrier
+               FOREACH_CLIENT(it.isbot, LAMBDA(it.bot_barrier = 2));
+
+               bot_barriertime = time;
+       }
+
+       // if we get here, the barrier is finished
+       // so end it...
+       this.bot_barrier = 0;
+       //this.colormod = '0 0 0';
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_turn(entity this)
+{
+       this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float;
+       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_select_weapon(entity this)
+{
+       float id = bot_cmd.bot_cmd_parm_float;
+
+       if(id < WEP_FIRST || id > WEP_LAST)
+               return CMD_STATUS_ERROR;
+
+       if(client_hasweapon(this, Weapons_from(id), true, false))
+               PS(this).m_switchweapon = Weapons_from(id);
+       else
+               return CMD_STATUS_ERROR;
+
+       return CMD_STATUS_FINISHED;
+}
+
+.int bot_cmd_condition_status;
+
+const int CMD_CONDITION_NONE = 0;
+const int CMD_CONDITION_true = 1;
+const int CMD_CONDITION_false = 2;
+const int CMD_CONDITION_true_BLOCK = 4;
+const int CMD_CONDITION_false_BLOCK = 8;
+
+float bot_cmd_eval(entity this, string expr)
+{
+       // Search for numbers
+       if(IS_DIGIT(substring(expr, 0, 1)))
+               return stof(expr);
+
+       // Search for cvars
+       if(substring(expr, 0, 5)=="cvar.")
+               return cvar(substring(expr, 5, strlen(expr)));
+
+       // Search for fields
+       switch(expr)
+       {
+               case "health":
+                       return this.health;
+               case "speed":
+                       return vlen(this.velocity);
+               case "flagcarrier":
+                       return ((this.flagcarried!=NULL));
+       }
+
+       LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
+       return 0;
+}
+
+float bot_cmd_if(entity this)
+{
+       string expr, val_a, val_b;
+       float cmpofs;
+
+       if(this.bot_cmd_condition_status != CMD_CONDITION_NONE)
+       {
+               // Only one "if" block is allowed at time
+               LOG_INFO("ERROR: Only one conditional block can be processed at time");
+               bot_clearqueue(this);
+               return CMD_STATUS_ERROR;
+       }
+
+       this.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK;
+
+       // search for operators
+       expr = bot_cmd.bot_cmd_parm_string;
+
+       cmpofs = strstrofs(expr,"=",0);
+
+       if(cmpofs>0)
+       {
+               val_a = substring(expr,0,cmpofs);
+               val_b = substring(expr,cmpofs+1,strlen(expr));
+
+               if(bot_cmd_eval(this, val_a)==bot_cmd_eval(this, val_b))
+                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
+               else
+                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
+
+               return CMD_STATUS_FINISHED;
+       }
+
+       cmpofs = strstrofs(expr,">",0);
+
+       if(cmpofs>0)
+       {
+               val_a = substring(expr,0,cmpofs);
+               val_b = substring(expr,cmpofs+1,strlen(expr));
+
+               if(bot_cmd_eval(this, val_a)>bot_cmd_eval(this, val_b))
+                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
+               else
+                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
+
+               return CMD_STATUS_FINISHED;
+       }
+
+       cmpofs = strstrofs(expr,"<",0);
+
+       if(cmpofs>0)
+       {
+               val_a = substring(expr,0,cmpofs);
+               val_b = substring(expr,cmpofs+1,strlen(expr));
+
+               if(bot_cmd_eval(this, val_a)<bot_cmd_eval(this, val_b))
+                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
+               else
+                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
+
+               return CMD_STATUS_FINISHED;
+       }
+
+       if(bot_cmd_eval(this, expr))
+               this.bot_cmd_condition_status |= CMD_CONDITION_true;
+       else
+               this.bot_cmd_condition_status |= CMD_CONDITION_false;
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_else(entity this)
+{
+       this.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
+       this.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_fi(entity this)
+{
+       this.bot_cmd_condition_status = CMD_CONDITION_NONE;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_resetaim(entity this)
+{
+       this.v_angle = '0 0 0';
+       return CMD_STATUS_FINISHED;
+}
+
+.float bot_cmd_aim_begintime;
+.float bot_cmd_aim_endtime;
+.vector bot_cmd_aim_begin;
+.vector bot_cmd_aim_end;
+
+float bot_cmd_aim(entity this)
+{
+       // Current direction
+       if(this.bot_cmd_aim_endtime)
+       {
+               float progress;
+
+               progress = min(1 - (this.bot_cmd_aim_endtime - time) / (this.bot_cmd_aim_endtime - this.bot_cmd_aim_begintime),1);
+               this.v_angle = this.bot_cmd_aim_begin + ((this.bot_cmd_aim_end - this.bot_cmd_aim_begin) * progress);
+
+               if(time>=this.bot_cmd_aim_endtime)
+               {
+                       this.bot_cmd_aim_endtime = 0;
+                       return CMD_STATUS_FINISHED;
+               }
+               else
+                       return CMD_STATUS_EXECUTING;
+       }
+
+       // New aiming direction
+       string parms;
+       float tokens, step;
+
+       parms = bot_cmd.bot_cmd_parm_string;
+
+       tokens = tokenizebyseparator(parms, " ");
+
+       if(tokens<2||tokens>3)
+               return CMD_STATUS_ERROR;
+
+       step = (tokens == 3) ? stof(argv(2)) : 0;
+
+       if(step == 0)
+       {
+               this.v_angle_x -= stof(argv(1));
+               this.v_angle_y += stof(argv(0));
+               return CMD_STATUS_FINISHED;
+       }
+
+       this.bot_cmd_aim_begin = this.v_angle;
+
+       this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1));
+       this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0));
+       this.bot_cmd_aim_end_z = 0;
+
+       this.bot_cmd_aim_begintime = time;
+       this.bot_cmd_aim_endtime = time + step;
+
+       return CMD_STATUS_EXECUTING;
+}
+
+float bot_cmd_aimtarget(entity this)
+{
+       if(this.bot_cmd_aim_endtime)
+       {
+               return bot_cmd_aim(this);
+       }
+
+       entity e;
+       string parms;
+       vector v;
+       float tokens, step;
+
+       parms = bot_cmd.bot_cmd_parm_string;
+
+       tokens = tokenizebyseparator(parms, " ");
+
+       e = bot_getplace(this, argv(0));
+       if(!e)
+               return CMD_STATUS_ERROR;
+
+       v = e.origin + (e.mins + e.maxs) * 0.5;
+
+       if(tokens==1)
+       {
+               this.v_angle = vectoangles(v - (this.origin + this.view_ofs));
+               this.v_angle_x = -this.v_angle.x;
+               return CMD_STATUS_FINISHED;
+       }
+
+       if(tokens<1||tokens>2)
+               return CMD_STATUS_ERROR;
+
+       step = stof(argv(1));
+
+       this.bot_cmd_aim_begin = this.v_angle;
+       this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs));
+       this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x;
+
+       this.bot_cmd_aim_begintime = time;
+       this.bot_cmd_aim_endtime = time + step;
+
+       return CMD_STATUS_EXECUTING;
+}
+
+.int bot_cmd_keys;
+
+const int BOT_CMD_KEY_NONE             = 0;
+const int BOT_CMD_KEY_FORWARD  = BIT(0);
+const int BOT_CMD_KEY_BACKWARD         = BIT(1);
+const int BOT_CMD_KEY_RIGHT    = BIT(2);
+const int BOT_CMD_KEY_LEFT             = BIT(3);
+const int BOT_CMD_KEY_JUMP             = BIT(4);
+const int BOT_CMD_KEY_ATTACK1  = BIT(5);
+const int BOT_CMD_KEY_ATTACK2  = BIT(6);
+const int BOT_CMD_KEY_USE              = BIT(7);
+const int BOT_CMD_KEY_HOOK             = BIT(8);
+const int BOT_CMD_KEY_CROUCH   = BIT(9);
+const int BOT_CMD_KEY_CHAT             = BIT(10);
+
+bool bot_presskeys(entity this)
+{
+       this.movement = '0 0 0';
+       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       PHYS_INPUT_BUTTON_CROUCH(this) = false;
+       PHYS_INPUT_BUTTON_ATCK(this) = false;
+       PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       PHYS_INPUT_BUTTON_USE(this) = false;
+       PHYS_INPUT_BUTTON_HOOK(this) = false;
+       PHYS_INPUT_BUTTON_CHAT(this) = false;
+
+       if(this.bot_cmd_keys == BOT_CMD_KEY_NONE)
+               return false;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
+               this.movement_x = autocvar_sv_maxspeed;
+       else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
+               this.movement_x = -autocvar_sv_maxspeed;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
+               this.movement_y = autocvar_sv_maxspeed;
+       else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT)
+               this.movement_y = -autocvar_sv_maxspeed;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_JUMP)
+               PHYS_INPUT_BUTTON_JUMP(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
+               PHYS_INPUT_BUTTON_CROUCH(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
+               PHYS_INPUT_BUTTON_ATCK(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
+               PHYS_INPUT_BUTTON_ATCK2(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_USE)
+               PHYS_INPUT_BUTTON_USE(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_HOOK)
+               PHYS_INPUT_BUTTON_HOOK(this) = true;
+
+       if(this.bot_cmd_keys & BOT_CMD_KEY_CHAT)
+               PHYS_INPUT_BUTTON_CHAT(this) = true;
+
+       return true;
+}
+
+
+float bot_cmd_keypress_handler(entity this, string key, float enabled)
+{
+       switch(key)
+       {
+               case "all":
+                       if(enabled)
+                               this.bot_cmd_keys = power2of(20) - 1; // >:)
+                       else
+                               this.bot_cmd_keys = BOT_CMD_KEY_NONE;
+               case "forward":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
+                       break;
+               case "backward":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
+                       break;
+               case "left":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
+                       break;
+               case "right":
+                       if(enabled)
+                       {
+                               this.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
+                       }
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
+                       break;
+               case "jump":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
+                       break;
+               case "crouch":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
+                       break;
+               case "attack1":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
+                       break;
+               case "attack2":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
+                       break;
+               case "use":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_USE;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
+                       break;
+               case "hook":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
+                       break;
+               case "chat":
+                       if(enabled)
+                               this.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
+                       else
+                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
+                       break;
+               default:
+                       break;
+       }
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_presskey(entity this)
+{
+       string key;
+
+       key = bot_cmd.bot_cmd_parm_string;
+
+       bot_cmd_keypress_handler(this, key,true);
+
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_releasekey(entity this)
+{
+       string key;
+
+       key = bot_cmd.bot_cmd_parm_string;
+
+       return bot_cmd_keypress_handler(this, key,false);
+}
+
+float bot_cmd_pause(entity this)
+{
+       PHYS_INPUT_BUTTON_DRAG(this) = false;
+       PHYS_INPUT_BUTTON_USE(this) = false;
+       PHYS_INPUT_BUTTON_ATCK(this) = false;
+       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       PHYS_INPUT_BUTTON_HOOK(this) = false;
+       PHYS_INPUT_BUTTON_CHAT(this) = false;
+       PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       PHYS_INPUT_BUTTON_CROUCH(this) = false;
+
+       this.movement = '0 0 0';
+       this.bot_cmd_keys = BOT_CMD_KEY_NONE;
+
+       bot_clear(this);
+       this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
+       return CMD_STATUS_FINISHED;
+}
+
+float bot_cmd_moveto(entity this)
+{
+       return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector);
+}
+
+float bot_cmd_movetotarget(entity this)
+{
+       entity e;
+       e = bot_getplace(this, bot_cmd.bot_cmd_parm_string);
+       if(!e)
+               return CMD_STATUS_ERROR;
+       return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5);
+}
+
+float bot_cmd_resetgoal(entity this)
+{
+       return this.cmd_resetgoal(this);
+}
+
+
+float bot_cmd_sound(entity this)
+{
+       string f;
+       f = bot_cmd.bot_cmd_parm_string;
+
+       float n = tokenizebyseparator(f, " ");
+
+       string sample = f;
+       float chan = CH_WEAPON_B;
+       float vol = VOL_BASE;
+       float atten = ATTEN_MIN;
+
+       if(n >= 1)
+               sample = argv(n - 1);
+       if(n >= 2)
+               chan = stof(argv(0));
+       if(n >= 3)
+               vol = stof(argv(1));
+       if(n >= 4)
+               atten = stof(argv(2));
+
+       precache_sound(f);
+       _sound(this, chan, sample, vol, atten);
+
+       return CMD_STATUS_FINISHED;
+}
+
+.entity tuba_note;
+float bot_cmd_debug_assert_canfire(entity this)
+{
+       float f = bot_cmd.bot_cmd_parm_float;
+
+       int slot = 0; // TODO: unhardcode?
+       .entity weaponentity = weaponentities[slot];
+       if(this.(weaponentity).state != WS_READY)
+       {
+               if(f)
+               {
+                       this.colormod = '0 8 8';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by weaponentity state\n");
+               }
+       }
+       else if(ATTACK_FINISHED(this, slot) > time)
+       {
+               if(f)
+               {
+                       this.colormod = '8 0 8';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)\n");
+               }
+       }
+       else if(this.tuba_note)
+       {
+               if(f)
+               {
+                       this.colormod = '8 0 0';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, bot still has an active tuba note\n");
+               }
+       }
+       else
+       {
+               if(!f)
+               {
+                       this.colormod = '8 8 0';
+                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left\n");
+               }
+       }
+
+       return CMD_STATUS_FINISHED;
+}
+
+//
+
+void bot_command_executed(entity this, bool rm)
+{
+       entity cmd;
+
+       cmd = bot_cmd;
+
+       if(rm)
+               bot_dequeuecommand(this, this.bot_cmd_execution_index);
+
+       this.bot_cmd_execution_index++;
+}
+
+void bot_setcurrentcommand(entity this)
+{
+       bot_cmd = NULL;
+
+       if(!this.bot_cmd_current)
+       {
+               this.bot_cmd_current = new_pure(bot_cmd);
+       }
+
+       bot_cmd = this.bot_cmd_current;
+       if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0)
+       {
+               if(bot_havecommand(this, this.bot_cmd_execution_index))
+               {
+                       string cmdstring;
+                       cmdstring = bot_readcommand(this, this.bot_cmd_execution_index);
+                       if(bot_decodecommand(cmdstring))
+                       {
+                               bot_cmd.owner = this;
+                               bot_cmd.bot_cmd_index = this.bot_cmd_execution_index;
+                       }
+                       else
+                       {
+                               // Invalid command, remove from queue
+                               bot_cmd = NULL;
+                               bot_dequeuecommand(this, this.bot_cmd_execution_index);
+                               this.bot_cmd_execution_index++;
+                       }
+               }
+               else
+                       bot_cmd = NULL;
+       }
+}
+
+void bot_resetqueues()
+{
+       FOREACH_CLIENT(it.isbot, LAMBDA(
+               it.bot_cmd_execution_index = 0;
+               bot_clearqueue(it);
+               // also, cancel all barriers
+               it.bot_barrier = 0;
+               for(int i = 0; i < it.bot_places_count; ++i)
+               {
+                       strunzone(it.(bot_placenames[i]));
+                       it.(bot_placenames[i]) = string_null;
+               }
+               it.bot_places_count = 0;
+       ));
+
+       bot_barriertime = time;
+}
+
+// Here we map commands to functions and deal with complex interactions between commands and execution states
+// NOTE: Of course you need to include your commands here too :)
+float bot_execute_commands_once(entity this)
+{
+       float status, ispressingkey;
+
+       // Find command
+       bot_setcurrentcommand(this);
+
+       // Ignore all commands except continue when the bot is paused
+       if(!(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED))
+       {
+               // if we have no bot command, better return
+               // old logic kept pressing previously pressed keys, but that has problems
+               // (namely, it means you cannot make a bot "normal" ever again)
+               // to keep a bot walking for a while, use the "wait" bot command
+               if(bot_cmd == world)
+                       return 0;
+       }
+       else if(bot_cmd.bot_cmd_type != BOT_CMD_CONTINUE)
+       {
+               if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
+               {
+                       bot_command_executed(this, true);
+                       LOG_INFO( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
+               }
+               return 1;
+       }
+
+       // Keep pressing keys raised by the "presskey" command
+       ispressingkey = boolean(bot_presskeys(this));
+
+       // Handle conditions
+       if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
+       if(this.bot_cmd_condition_status & CMD_CONDITION_true && this.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK)
+       {
+               bot_command_executed(this, true);
+               return -1;
+       }
+       else if(this.bot_cmd_condition_status & CMD_CONDITION_false && this.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK)
+       {
+               bot_command_executed(this, true);
+               return -1;
+       }
+
+       // Map commands to functions
+       switch(bot_cmd.bot_cmd_type)
+       {
+               case BOT_CMD_NULL:
+                       return ispressingkey;
+                       //break;
+               case BOT_CMD_PAUSE:
+                       status = bot_cmd_pause(this);
+                       break;
+               case BOT_CMD_CONTINUE:
+                       status = bot_cmd_continue(this);
+                       break;
+               case BOT_CMD_WAIT:
+                       status = bot_cmd_wait(this);
+                       break;
+               case BOT_CMD_WAIT_UNTIL:
+                       status = bot_cmd_wait_until(this);
+                       break;
+               case BOT_CMD_TURN:
+                       status = bot_cmd_turn(this);
+                       break;
+               case BOT_CMD_MOVETO:
+                       status = bot_cmd_moveto(this);
+                       break;
+               case BOT_CMD_MOVETOTARGET:
+                       status = bot_cmd_movetotarget(this);
+                       break;
+               case BOT_CMD_RESETGOAL:
+                       status = bot_cmd_resetgoal(this);
+                       break;
+               case BOT_CMD_CC:
+                       status = bot_cmd_cc(this);
+                       break;
+               case BOT_CMD_IF:
+                       status = bot_cmd_if(this);
+                       break;
+               case BOT_CMD_ELSE:
+                       status = bot_cmd_else(this);
+                       break;
+               case BOT_CMD_FI:
+                       status = bot_cmd_fi(this);
+                       break;
+               case BOT_CMD_RESETAIM:
+                       status = bot_cmd_resetaim(this);
+                       break;
+               case BOT_CMD_AIM:
+                       status = bot_cmd_aim(this);
+                       break;
+               case BOT_CMD_AIMTARGET:
+                       status = bot_cmd_aimtarget(this);
+                       break;
+               case BOT_CMD_PRESSKEY:
+                       status = bot_cmd_presskey(this);
+                       break;
+               case BOT_CMD_RELEASEKEY:
+                       status = bot_cmd_releasekey(this);
+                       break;
+               case BOT_CMD_SELECTWEAPON:
+                       status = bot_cmd_select_weapon(this);
+                       break;
+               case BOT_CMD_IMPULSE:
+                       status = bot_cmd_impulse(this);
+                       break;
+               case BOT_CMD_BARRIER:
+                       status = bot_cmd_barrier(this);
+                       break;
+               case BOT_CMD_CONSOLE:
+                       localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
+                       status = CMD_STATUS_FINISHED;
+                       break;
+               case BOT_CMD_SOUND:
+                       status = bot_cmd_sound(this);
+                       break;
+               case BOT_CMD_DEBUG_ASSERT_CANFIRE:
+                       status = bot_cmd_debug_assert_canfire(this);
+                       break;
+               default:
+                       LOG_INFO(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
+                       return 0;
+       }
+
+       if (status==CMD_STATUS_ERROR)
+               LOG_INFO(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
+
+       // Move execution pointer
+       if(status==CMD_STATUS_EXECUTING)
+       {
+               return 1;
+       }
+       else
+       {
+               if(autocvar_g_debug_bot_commands)
+               {
+                       string parms;
+
+                       switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
+                       {
+                               case BOT_CMD_PARAMETER_FLOAT:
+                                       parms = ftos(bot_cmd.bot_cmd_parm_float);
+                                       break;
+                               case BOT_CMD_PARAMETER_STRING:
+                                       parms = bot_cmd.bot_cmd_parm_string;
+                                       break;
+                               case BOT_CMD_PARAMETER_VECTOR:
+                                       parms = vtos(bot_cmd.bot_cmd_parm_vector);
+                                       break;
+                               default:
+                                       parms = "";
+                                       break;
+                       }
+                       clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
+               }
+
+               bot_command_executed(this, true);
+       }
+
+       if(status == CMD_STATUS_FINISHED)
+               return -1;
+
+       return CMD_STATUS_ERROR;
+}
+
+// This function should be (the only) called directly from the bot ai loop
+int bot_execute_commands(entity this)
+{
+       int f;
+       do
+       {
+               f = bot_execute_commands_once(this);
+       }
+       while(f < 0);
+       return f;
+}
diff --git a/qcsrc/server/bot/default/scripting.qh b/qcsrc/server/bot/default/scripting.qh
new file mode 100644 (file)
index 0000000..b3f0e90
--- /dev/null
@@ -0,0 +1,80 @@
+#pragma once
+
+#define BOT_EXEC_STATUS_IDLE 0
+#define BOT_EXEC_STATUS_PAUSED 1
+#define BOT_EXEC_STATUS_WAITING 2
+
+#define CMD_STATUS_EXECUTING 0
+#define CMD_STATUS_FINISHED 1
+#define CMD_STATUS_ERROR 2
+
+
+// NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
+const int BOT_CMD_NULL                         = 0;
+const int BOT_CMD_PAUSE                = 1;
+const int BOT_CMD_CONTINUE             = 2;
+const int BOT_CMD_WAIT                         = 3;
+const int BOT_CMD_TURN                         = 4;
+const int BOT_CMD_MOVETO               = 5;
+const int BOT_CMD_RESETGOAL    = 6;    // Not implemented yet
+const int BOT_CMD_CC                   = 7;
+const int BOT_CMD_IF                   = 8;
+const int BOT_CMD_ELSE                         = 9;
+const int BOT_CMD_FI                   = 10;
+const int BOT_CMD_RESETAIM             = 11;
+const int BOT_CMD_AIM                  = 12;
+const int BOT_CMD_PRESSKEY             = 13;
+const int BOT_CMD_RELEASEKEY   = 14;
+const int BOT_CMD_SELECTWEAPON         = 15;
+const int BOT_CMD_IMPULSE              = 16;
+const int BOT_CMD_WAIT_UNTIL   = 17;
+const int BOT_CMD_MOVETOTARGET         = 18;
+const int BOT_CMD_AIMTARGET    = 19;
+const int BOT_CMD_BARRIER              = 20;
+const int BOT_CMD_CONSOLE              = 21;
+const int BOT_CMD_SOUND                = 22;
+const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23;
+const int BOT_CMD_WHILE                = 24;   // TODO: Not implemented yet
+const int BOT_CMD_WEND                         = 25;   // TODO: Not implemented yet
+const int BOT_CMD_CHASE                = 26;   // TODO: Not implemented yet
+
+const int BOT_CMD_COUNTER              = 24;   // Update this value if you add/remove a command
+
+// NOTE: Following commands should be implemented on the bot ai
+//              If a new command should be handled by the target ai(s) please declare it here
+.float(entity, vector) cmd_moveto;
+.float(entity) cmd_resetgoal;
+
+//
+const int BOT_CMD_PARAMETER_NONE = 0;
+const int BOT_CMD_PARAMETER_FLOAT = 1;
+const int BOT_CMD_PARAMETER_STRING = 2;
+const int BOT_CMD_PARAMETER_VECTOR = 3;
+
+float bot_cmds_initialized;
+int bot_cmd_parm_type[BOT_CMD_COUNTER];
+string bot_cmd_string[BOT_CMD_COUNTER];
+
+// Bots command queue
+entity bot_cmd;        // global current command
+.entity bot_cmd_current; // current command of this bot
+
+.float bot_cmd_index;                  // Position of the command in the queue
+.int bot_cmd_type;                     // If of command (see the BOT_CMD_* defines)
+.float bot_cmd_parm_float;             // Field to store a float parameter
+.string bot_cmd_parm_string;           // Field to store a string parameter
+.vector bot_cmd_parm_vector;           // Field to store a vector parameter
+
+float bot_barriertime;
+.float bot_barrier;
+
+.float bot_cmd_execution_index;                // Position in the queue of the command to be executed
+
+
+void bot_resetqueues();
+void bot_queuecommand(entity bot, string cmdstring);
+void bot_cmdhelp(string scmd);
+void bot_list_commands();
+float bot_execute_commands(entity this);
+entity find_bot_by_name(string name);
+entity find_bot_by_number(float number);
diff --git a/qcsrc/server/bot/default/waypoints.qc b/qcsrc/server/bot/default/waypoints.qc
new file mode 100644 (file)
index 0000000..1e03bf5
--- /dev/null
@@ -0,0 +1,1097 @@
+#include "waypoints.qh"
+
+#include "cvars.qh"
+
+#include "bot.qh"
+#include "navigation.qh"
+
+#include <common/state.qh>
+
+#include "../../antilag.qh"
+
+#include <common/constants.qh>
+#include <common/net_linked.qh>
+
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+
+void waypoint_setupmodel(entity wp)
+{
+       if (autocvar_g_waypointeditor)
+       {
+               // TODO: add some sort of visible box in edit mode for box waypoints
+               vector m1 = wp.mins;
+               vector m2 = wp.maxs;
+               setmodel(wp, MDL_WAYPOINT);
+               setsize(wp, m1, m2);
+               wp.effects = EF_LOWPRECISION;
+               if (wp.wpflags & WAYPOINTFLAG_ITEM)
+                       wp.colormod = '1 0 0';
+               else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
+                       wp.colormod = '1 1 0';
+               else
+                       wp.colormod = '1 1 1';
+       }
+       else
+               wp.model = "";
+}
+
+// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
+// them back to it as well
+// (suitable for spawnfunc_waypoint editor)
+entity waypoint_spawn(vector m1, vector m2, float f)
+{
+       if(!(f & WAYPOINTFLAG_PERSONAL))
+       {
+               IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
+               {
+                       return it;
+               });
+       }
+
+       entity w = new(waypoint);
+       IL_PUSH(g_waypoints, w);
+       w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       w.wpflags = f;
+       w.solid = SOLID_TRIGGER;
+       setorigin(w, (m1 + m2) * 0.5);
+       setsize(w, m1 - w.origin, m2 - w.origin);
+       if (w.size)
+               w.wpisbox = true;
+
+       if(!w.wpisbox)
+       {
+               setsize(w, PL_MIN_CONST - '1 1 0', PL_MAX_CONST + '1 1 0');
+               if(!move_out_of_solid(w))
+               {
+                       if(!(f & WAYPOINTFLAG_GENERATED))
+                       {
+                               LOG_TRACE("Killed a waypoint that was stuck in solid at ", vtos(w.origin));
+                               delete(w);
+                               return NULL;
+                       }
+                       else
+                       {
+                               if(autocvar_developer)
+                               {
+                                       LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n");
+                                       backtrace("Waypoint stuck");
+                               }
+                       }
+               }
+               setsize(w, '0 0 0', '0 0 0');
+       }
+
+       waypoint_clearlinks(w);
+       //waypoint_schedulerelink(w);
+
+       waypoint_setupmodel(w);
+
+       return w;
+}
+
+// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has
+void waypoint_addlink(entity from, entity to)
+{
+       float c;
+
+       if (from == to)
+               return;
+       if (from.wpflags & WAYPOINTFLAG_NORELINK)
+               return;
+
+       if (from.wp00 == to) return;if (from.wp01 == to) return;if (from.wp02 == to) return;if (from.wp03 == to) return;
+       if (from.wp04 == to) return;if (from.wp05 == to) return;if (from.wp06 == to) return;if (from.wp07 == to) return;
+       if (from.wp08 == to) return;if (from.wp09 == to) return;if (from.wp10 == to) return;if (from.wp11 == to) return;
+       if (from.wp12 == to) return;if (from.wp13 == to) return;if (from.wp14 == to) return;if (from.wp15 == to) return;
+       if (from.wp16 == to) return;if (from.wp17 == to) return;if (from.wp18 == to) return;if (from.wp19 == to) return;
+       if (from.wp20 == to) return;if (from.wp21 == to) return;if (from.wp22 == to) return;if (from.wp23 == to) return;
+       if (from.wp24 == to) return;if (from.wp25 == to) return;if (from.wp26 == to) return;if (from.wp27 == to) return;
+       if (from.wp28 == to) return;if (from.wp29 == to) return;if (from.wp30 == to) return;if (from.wp31 == to) return;
+
+       if (to.wpisbox || from.wpisbox)
+       {
+               // if either is a box we have to find the nearest points on them to
+               // calculate the distance properly
+               vector v1, v2, m1, m2;
+               v1 = from.origin;
+               m1 = to.absmin;
+               m2 = to.absmax;
+               v1_x = bound(m1_x, v1_x, m2_x);
+               v1_y = bound(m1_y, v1_y, m2_y);
+               v1_z = bound(m1_z, v1_z, m2_z);
+               v2 = to.origin;
+               m1 = from.absmin;
+               m2 = from.absmax;
+               v2_x = bound(m1_x, v2_x, m2_x);
+               v2_y = bound(m1_y, v2_y, m2_y);
+               v2_z = bound(m1_z, v2_z, m2_z);
+               v2 = to.origin;
+               c = vlen(v2 - v1);
+       }
+       else
+               c = vlen(to.origin - from.origin);
+
+       if (from.wp31mincost < c) return;
+       if (from.wp30mincost < c) {from.wp31 = to;from.wp31mincost = c;return;} from.wp31 = from.wp30;from.wp31mincost = from.wp30mincost;
+       if (from.wp29mincost < c) {from.wp30 = to;from.wp30mincost = c;return;} from.wp30 = from.wp29;from.wp30mincost = from.wp29mincost;
+       if (from.wp28mincost < c) {from.wp29 = to;from.wp29mincost = c;return;} from.wp29 = from.wp28;from.wp29mincost = from.wp28mincost;
+       if (from.wp27mincost < c) {from.wp28 = to;from.wp28mincost = c;return;} from.wp28 = from.wp27;from.wp28mincost = from.wp27mincost;
+       if (from.wp26mincost < c) {from.wp27 = to;from.wp27mincost = c;return;} from.wp27 = from.wp26;from.wp27mincost = from.wp26mincost;
+       if (from.wp25mincost < c) {from.wp26 = to;from.wp26mincost = c;return;} from.wp26 = from.wp25;from.wp26mincost = from.wp25mincost;
+       if (from.wp24mincost < c) {from.wp25 = to;from.wp25mincost = c;return;} from.wp25 = from.wp24;from.wp25mincost = from.wp24mincost;
+       if (from.wp23mincost < c) {from.wp24 = to;from.wp24mincost = c;return;} from.wp24 = from.wp23;from.wp24mincost = from.wp23mincost;
+       if (from.wp22mincost < c) {from.wp23 = to;from.wp23mincost = c;return;} from.wp23 = from.wp22;from.wp23mincost = from.wp22mincost;
+       if (from.wp21mincost < c) {from.wp22 = to;from.wp22mincost = c;return;} from.wp22 = from.wp21;from.wp22mincost = from.wp21mincost;
+       if (from.wp20mincost < c) {from.wp21 = to;from.wp21mincost = c;return;} from.wp21 = from.wp20;from.wp21mincost = from.wp20mincost;
+       if (from.wp19mincost < c) {from.wp20 = to;from.wp20mincost = c;return;} from.wp20 = from.wp19;from.wp20mincost = from.wp19mincost;
+       if (from.wp18mincost < c) {from.wp19 = to;from.wp19mincost = c;return;} from.wp19 = from.wp18;from.wp19mincost = from.wp18mincost;
+       if (from.wp17mincost < c) {from.wp18 = to;from.wp18mincost = c;return;} from.wp18 = from.wp17;from.wp18mincost = from.wp17mincost;
+       if (from.wp16mincost < c) {from.wp17 = to;from.wp17mincost = c;return;} from.wp17 = from.wp16;from.wp17mincost = from.wp16mincost;
+       if (from.wp15mincost < c) {from.wp16 = to;from.wp16mincost = c;return;} from.wp16 = from.wp15;from.wp16mincost = from.wp15mincost;
+       if (from.wp14mincost < c) {from.wp15 = to;from.wp15mincost = c;return;} from.wp15 = from.wp14;from.wp15mincost = from.wp14mincost;
+       if (from.wp13mincost < c) {from.wp14 = to;from.wp14mincost = c;return;} from.wp14 = from.wp13;from.wp14mincost = from.wp13mincost;
+       if (from.wp12mincost < c) {from.wp13 = to;from.wp13mincost = c;return;} from.wp13 = from.wp12;from.wp13mincost = from.wp12mincost;
+       if (from.wp11mincost < c) {from.wp12 = to;from.wp12mincost = c;return;} from.wp12 = from.wp11;from.wp12mincost = from.wp11mincost;
+       if (from.wp10mincost < c) {from.wp11 = to;from.wp11mincost = c;return;} from.wp11 = from.wp10;from.wp11mincost = from.wp10mincost;
+       if (from.wp09mincost < c) {from.wp10 = to;from.wp10mincost = c;return;} from.wp10 = from.wp09;from.wp10mincost = from.wp09mincost;
+       if (from.wp08mincost < c) {from.wp09 = to;from.wp09mincost = c;return;} from.wp09 = from.wp08;from.wp09mincost = from.wp08mincost;
+       if (from.wp07mincost < c) {from.wp08 = to;from.wp08mincost = c;return;} from.wp08 = from.wp07;from.wp08mincost = from.wp07mincost;
+       if (from.wp06mincost < c) {from.wp07 = to;from.wp07mincost = c;return;} from.wp07 = from.wp06;from.wp07mincost = from.wp06mincost;
+       if (from.wp05mincost < c) {from.wp06 = to;from.wp06mincost = c;return;} from.wp06 = from.wp05;from.wp06mincost = from.wp05mincost;
+       if (from.wp04mincost < c) {from.wp05 = to;from.wp05mincost = c;return;} from.wp05 = from.wp04;from.wp05mincost = from.wp04mincost;
+       if (from.wp03mincost < c) {from.wp04 = to;from.wp04mincost = c;return;} from.wp04 = from.wp03;from.wp04mincost = from.wp03mincost;
+       if (from.wp02mincost < c) {from.wp03 = to;from.wp03mincost = c;return;} from.wp03 = from.wp02;from.wp03mincost = from.wp02mincost;
+       if (from.wp01mincost < c) {from.wp02 = to;from.wp02mincost = c;return;} from.wp02 = from.wp01;from.wp02mincost = from.wp01mincost;
+       if (from.wp00mincost < c) {from.wp01 = to;from.wp01mincost = c;return;} from.wp01 = from.wp00;from.wp01mincost = from.wp00mincost;
+       from.wp00 = to;from.wp00mincost = c;return;
+}
+
+// relink this spawnfunc_waypoint
+// (precompile a list of all reachable waypoints from this spawnfunc_waypoint)
+// (SLOW!)
+void waypoint_think(entity this)
+{
+       vector sv, sm1, sm2, ev, em1, em2, dv;
+
+       bot_calculate_stepheightvec();
+
+       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
+
+       //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
+       sm1 = this.origin + this.mins;
+       sm2 = this.origin + this.maxs;
+       IL_EACH(g_waypoints, true,
+       {
+               if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax))
+               {
+                       waypoint_addlink(this, it);
+                       waypoint_addlink(it, this);
+               }
+               else
+               {
+                       ++relink_total;
+                       if(!checkpvs(this.origin, it))
+                       {
+                               ++relink_pvsculled;
+                               continue;
+                       }
+                       sv = it.origin;
+                       sv.x = bound(sm1_x, sv.x, sm2_x);
+                       sv.y = bound(sm1_y, sv.y, sm2_y);
+                       sv.z = bound(sm1_z, sv.z, sm2_z);
+                       ev = this.origin;
+                       em1 = it.origin + it.mins;
+                       em2 = it.origin + it.maxs;
+                       ev.x = bound(em1_x, ev.x, em2_x);
+                       ev.y = bound(em1_y, ev.y, em2_y);
+                       ev.z = bound(em1_z, ev.z, em2_z);
+                       dv = ev - sv;
+                       dv.z = 0;
+                       if(vdist(dv, >=, 1050)) // max search distance in XY
+                       {
+                               ++relink_lengthculled;
+                               continue;
+                       }
+                       navigation_testtracewalk = 0;
+                       if (!this.wpisbox)
+                       {
+                               tracebox(sv - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, sv, false, this);
+                               if (!trace_startsolid)
+                               {
+                                       //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
+                                       sv = trace_endpos + '0 0 1';
+                               }
+                       }
+                       if (!it.wpisbox)
+                       {
+                               tracebox(ev - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, ev, false, it);
+                               if (!trace_startsolid)
+                               {
+                                       //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
+                                       ev = trace_endpos + '0 0 1';
+                               }
+                       }
+                       //traceline(this.origin, it.origin, false, NULL);
+                       //if (trace_fraction == 1)
+                       if (!this.wpisbox && tracewalk(this, sv, PL_MIN_CONST, PL_MAX_CONST, ev, MOVE_NOMONSTERS))
+                               waypoint_addlink(this, it);
+                       else
+                               relink_walkculled += 0.5;
+                       if (!it.wpisbox && tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv, MOVE_NOMONSTERS))
+                               waypoint_addlink(it, this);
+                       else
+                               relink_walkculled += 0.5;
+               }
+       });
+       navigation_testtracewalk = 0;
+       this.wplinked = true;
+}
+
+void waypoint_clearlinks(entity wp)
+{
+       // clear links to other waypoints
+       float f;
+       f = 10000000;
+       wp.wp00 = wp.wp01 = wp.wp02 = wp.wp03 = wp.wp04 = wp.wp05 = wp.wp06 = wp.wp07 = NULL;
+       wp.wp08 = wp.wp09 = wp.wp10 = wp.wp11 = wp.wp12 = wp.wp13 = wp.wp14 = wp.wp15 = NULL;
+       wp.wp16 = wp.wp17 = wp.wp18 = wp.wp19 = wp.wp20 = wp.wp21 = wp.wp22 = wp.wp23 = NULL;
+       wp.wp24 = wp.wp25 = wp.wp26 = wp.wp27 = wp.wp28 = wp.wp29 = wp.wp30 = wp.wp31 = NULL;
+
+       wp.wp00mincost = wp.wp01mincost = wp.wp02mincost = wp.wp03mincost = wp.wp04mincost = wp.wp05mincost = wp.wp06mincost = wp.wp07mincost = f;
+       wp.wp08mincost = wp.wp09mincost = wp.wp10mincost = wp.wp11mincost = wp.wp12mincost = wp.wp13mincost = wp.wp14mincost = wp.wp15mincost = f;
+       wp.wp16mincost = wp.wp17mincost = wp.wp18mincost = wp.wp19mincost = wp.wp20mincost = wp.wp21mincost = wp.wp22mincost = wp.wp23mincost = f;
+       wp.wp24mincost = wp.wp25mincost = wp.wp26mincost = wp.wp27mincost = wp.wp28mincost = wp.wp29mincost = wp.wp30mincost = wp.wp31mincost = f;
+
+       wp.wplinked = false;
+}
+
+// tell a spawnfunc_waypoint to relink
+void waypoint_schedulerelink(entity wp)
+{
+       if (wp == NULL)
+               return;
+
+       waypoint_setupmodel(wp);
+       wp.wpisbox = vdist(wp.size, >, 0);
+       wp.enemy = NULL;
+       if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL))
+               wp.owner = NULL;
+       if (!(wp.wpflags & WAYPOINTFLAG_NORELINK))
+               waypoint_clearlinks(wp);
+       // schedule an actual relink on next frame
+       setthink(wp, waypoint_think);
+       wp.nextthink = time;
+       wp.effects = EF_LOWPRECISION;
+}
+
+// spawnfunc_waypoint map entity
+spawnfunc(waypoint)
+{
+       IL_PUSH(g_waypoints, this);
+
+       setorigin(this, this.origin);
+       // schedule a relink after other waypoints have had a chance to spawn
+       waypoint_clearlinks(this);
+       //waypoint_schedulerelink(this);
+}
+
+// remove a spawnfunc_waypoint, and schedule all neighbors to relink
+void waypoint_remove(entity e)
+{
+       // tell all linked waypoints that they need to relink
+       waypoint_schedulerelink(e.wp00);
+       waypoint_schedulerelink(e.wp01);
+       waypoint_schedulerelink(e.wp02);
+       waypoint_schedulerelink(e.wp03);
+       waypoint_schedulerelink(e.wp04);
+       waypoint_schedulerelink(e.wp05);
+       waypoint_schedulerelink(e.wp06);
+       waypoint_schedulerelink(e.wp07);
+       waypoint_schedulerelink(e.wp08);
+       waypoint_schedulerelink(e.wp09);
+       waypoint_schedulerelink(e.wp10);
+       waypoint_schedulerelink(e.wp11);
+       waypoint_schedulerelink(e.wp12);
+       waypoint_schedulerelink(e.wp13);
+       waypoint_schedulerelink(e.wp14);
+       waypoint_schedulerelink(e.wp15);
+       waypoint_schedulerelink(e.wp16);
+       waypoint_schedulerelink(e.wp17);
+       waypoint_schedulerelink(e.wp18);
+       waypoint_schedulerelink(e.wp19);
+       waypoint_schedulerelink(e.wp20);
+       waypoint_schedulerelink(e.wp21);
+       waypoint_schedulerelink(e.wp22);
+       waypoint_schedulerelink(e.wp23);
+       waypoint_schedulerelink(e.wp24);
+       waypoint_schedulerelink(e.wp25);
+       waypoint_schedulerelink(e.wp26);
+       waypoint_schedulerelink(e.wp27);
+       waypoint_schedulerelink(e.wp28);
+       waypoint_schedulerelink(e.wp29);
+       waypoint_schedulerelink(e.wp30);
+       waypoint_schedulerelink(e.wp31);
+       // and now remove the spawnfunc_waypoint
+       delete(e);
+}
+
+// empties the map of waypoints
+void waypoint_removeall()
+{
+       IL_EACH(g_waypoints, true,
+       {
+               delete(it);
+       });
+}
+
+// tell all waypoints to relink
+// (is this useful at all?)
+void waypoint_schedulerelinkall()
+{
+       relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               waypoint_schedulerelink(it);
+       });
+}
+
+// Load waypoint links from file
+float waypoint_load_links()
+{
+       string filename, s;
+       float file, tokens, c = 0, found;
+       entity wp_from = NULL, wp_to;
+       vector wp_to_pos, wp_from_pos;
+       filename = strcat("maps/", mapname);
+       filename = strcat(filename, ".waypoints.cache");
+       file = fopen(filename, FILE_READ);
+
+       if (file < 0)
+       {
+               LOG_TRACE("waypoint links load from ");
+               LOG_TRACE(filename);
+               LOG_TRACE(" failed");
+               return false;
+       }
+
+       while ((s = fgets(file)))
+       {
+               tokens = tokenizebyseparator(s, "*");
+
+               if (tokens!=2)
+               {
+                       // bad file format
+                       fclose(file);
+                       return false;
+               }
+
+               wp_from_pos     = stov(argv(0));
+               wp_to_pos       = stov(argv(1));
+
+               // Search "from" waypoint
+               if(!wp_from || wp_from.origin!=wp_from_pos)
+               {
+                       wp_from = findradius(wp_from_pos, 1);
+                       found = false;
+                       while(wp_from)
+                       {
+                               if(vdist(wp_from.origin - wp_from_pos, <, 1))
+                               if(wp_from.classname == "waypoint")
+                               {
+                                       found = true;
+                                       break;
+                               }
+                               wp_from = wp_from.chain;
+                       }
+
+                       if(!found)
+                       {
+                               LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from.origin));
+                               continue;
+                       }
+
+               }
+
+               // Search "to" waypoint
+               wp_to = findradius(wp_to_pos, 1);
+               found = false;
+               while(wp_to)
+               {
+                       if(vdist(wp_to.origin - wp_to_pos, <, 1))
+                       if(wp_to.classname == "waypoint")
+                       {
+                               found = true;
+                               break;
+                       }
+                       wp_to = wp_to.chain;
+               }
+
+               if(!found)
+               {
+                       LOG_TRACE("waypoint_load_links: couldn't find 'to' waypoint at ", vtos(wp_to.origin));
+                       continue;
+               }
+
+               ++c;
+               waypoint_addlink(wp_from, wp_to);
+       }
+
+       fclose(file);
+
+       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache");
+
+       botframe_cachedwaypointlinks = true;
+       return true;
+}
+
+void waypoint_load_links_hardwired()
+{
+       string filename, s;
+       float file, tokens, c = 0, found;
+       entity wp_from = NULL, wp_to;
+       vector wp_to_pos, wp_from_pos;
+       filename = strcat("maps/", mapname);
+       filename = strcat(filename, ".waypoints.hardwired");
+       file = fopen(filename, FILE_READ);
+
+       botframe_loadedforcedlinks = true;
+
+       if (file < 0)
+       {
+               LOG_TRACE("waypoint links load from ", filename, " failed");
+               return;
+       }
+
+       while ((s = fgets(file)))
+       {
+               if(substring(s, 0, 2)=="//")
+                       continue;
+
+               if(substring(s, 0, 1)=="#")
+                       continue;
+
+               tokens = tokenizebyseparator(s, "*");
+
+               if (tokens!=2)
+                       continue;
+
+               wp_from_pos     = stov(argv(0));
+               wp_to_pos       = stov(argv(1));
+
+               // Search "from" waypoint
+               if(!wp_from || wp_from.origin!=wp_from_pos)
+               {
+                       wp_from = findradius(wp_from_pos, 5);
+                       found = false;
+                       while(wp_from)
+                       {
+                               if(vdist(wp_from.origin - wp_from_pos, <, 5))
+                               if(wp_from.classname == "waypoint")
+                               {
+                                       found = true;
+                                       break;
+                               }
+                               wp_from = wp_from.chain;
+                       }
+
+                       if(!found)
+                       {
+                               LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n"));
+                               continue;
+                       }
+               }
+
+               // Search "to" waypoint
+               wp_to = findradius(wp_to_pos, 5);
+               found = false;
+               while(wp_to)
+               {
+                       if(vdist(wp_to.origin - wp_to_pos, <, 5))
+                       if(wp_to.classname == "waypoint")
+                       {
+                               found = true;
+                               break;
+                       }
+                       wp_to = wp_to.chain;
+               }
+
+               if(!found)
+               {
+                       LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n"));
+                       continue;
+               }
+
+               ++c;
+               waypoint_addlink(wp_from, wp_to);
+               wp_from.wphardwired = true;
+               wp_to.wphardwired = true;
+       }
+
+       fclose(file);
+
+       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired");
+}
+
+entity waypoint_get_link(entity w, float i)
+{
+       switch(i)
+       {
+               case  0:return w.wp00;
+               case  1:return w.wp01;
+               case  2:return w.wp02;
+               case  3:return w.wp03;
+               case  4:return w.wp04;
+               case  5:return w.wp05;
+               case  6:return w.wp06;
+               case  7:return w.wp07;
+               case  8:return w.wp08;
+               case  9:return w.wp09;
+               case 10:return w.wp10;
+               case 11:return w.wp11;
+               case 12:return w.wp12;
+               case 13:return w.wp13;
+               case 14:return w.wp14;
+               case 15:return w.wp15;
+               case 16:return w.wp16;
+               case 17:return w.wp17;
+               case 18:return w.wp18;
+               case 19:return w.wp19;
+               case 20:return w.wp20;
+               case 21:return w.wp21;
+               case 22:return w.wp22;
+               case 23:return w.wp23;
+               case 24:return w.wp24;
+               case 25:return w.wp25;
+               case 26:return w.wp26;
+               case 27:return w.wp27;
+               case 28:return w.wp28;
+               case 29:return w.wp29;
+               case 30:return w.wp30;
+               case 31:return w.wp31;
+               default:return NULL;
+       }
+}
+
+// Save all waypoint links to a file
+void waypoint_save_links()
+{
+       string filename = sprintf("maps/%s.waypoints.cache", mapname);
+       int file = fopen(filename, FILE_WRITE);
+       if (file < 0)
+       {
+               LOG_INFOF("waypoint link save to %s failed\n", filename);
+               return;
+       }
+
+       int c = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               for(int j = 0; j < 32; ++j)
+               {
+                       entity link = waypoint_get_link(it, j);
+                       if(link)
+                       {
+                               string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n");
+                               fputs(file, s);
+                               ++c;
+                       }
+               }
+       });
+       fclose(file);
+       botframe_cachedwaypointlinks = true;
+
+       LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache\n", c, mapname);
+}
+
+// save waypoints to gamedir/data/maps/mapname.waypoints
+void waypoint_saveall()
+{
+       string filename = sprintf("maps/%s.waypoints", mapname);
+       int file = fopen(filename, FILE_WRITE);
+       if (file < 0)
+       {
+               waypoint_save_links(); // save anyway?
+               botframe_loadedforcedlinks = false;
+
+               LOG_INFOF("waypoint links: save to %s failed\n", filename);
+               return;
+       }
+
+       int c = 0;
+       IL_EACH(g_waypoints, true,
+       {
+               if(it.wpflags & WAYPOINTFLAG_GENERATED)
+                       continue;
+
+               for(int j = 0; j < 32; ++j)
+               {
+                       string s;
+                       s = strcat(vtos(it.origin + it.mins), "\n");
+                       s = strcat(s, vtos(it.origin + it.maxs));
+                       s = strcat(s, "\n");
+                       s = strcat(s, ftos(it.wpflags));
+                       s = strcat(s, "\n");
+                       fputs(file, s);
+                       ++c;
+               }
+       });
+       fclose(file);
+       waypoint_save_links();
+       botframe_loadedforcedlinks = false;
+
+       LOG_INFOF("saved %d waypoints to maps/%s.waypoints\n", c, mapname);
+}
+
+// load waypoints from file
+float waypoint_loadall()
+{
+       string filename, s;
+       float file, cwp, cwb, fl;
+       vector m1, m2;
+       cwp = 0;
+       cwb = 0;
+       filename = strcat("maps/", mapname);
+       filename = strcat(filename, ".waypoints");
+       file = fopen(filename, FILE_READ);
+       if (file >= 0)
+       {
+               while ((s = fgets(file)))
+               {
+                       m1 = stov(s);
+                       s = fgets(file);
+                       if (!s)
+                               break;
+                       m2 = stov(s);
+                       s = fgets(file);
+                       if (!s)
+                               break;
+                       fl = stof(s);
+                       waypoint_spawn(m1, m2, fl);
+                       if (m1 == m2)
+                               cwp = cwp + 1;
+                       else
+                               cwb = cwb + 1;
+               }
+               fclose(file);
+               LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints");
+       }
+       else
+       {
+               LOG_TRACE("waypoint load from ", filename, " failed");
+       }
+       return cwp + cwb;
+}
+
+vector waypoint_fixorigin(vector position)
+{
+       tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
+       if(trace_fraction < 1)
+               position = trace_endpos;
+       //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
+       //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n");
+       return position;
+}
+
+void waypoint_spawnforitem_force(entity e, vector org)
+{
+       // Fix the waypoint altitude if necessary
+       org = waypoint_fixorigin(org);
+
+       // don't spawn an item spawnfunc_waypoint if it already exists
+       IL_EACH(g_waypoints, true,
+       {
+               if(it.wpisbox)
+               {
+                       if(boxesoverlap(org, org, it.absmin, it.absmax))
+                       {
+                               e.nearestwaypoint = it;
+                               return;
+                       }
+               }
+               else
+               {
+                       if(vdist(it.origin - org, <, 16))
+                       {
+                               e.nearestwaypoint = it;
+                               return;
+                       }
+               }
+       });
+
+       e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM);
+}
+
+void waypoint_spawnforitem(entity e)
+{
+       if(!bot_waypoints_for_items)
+               return;
+
+       waypoint_spawnforitem_force(e, e.origin);
+}
+
+void waypoint_spawnforteleporter_boxes(entity e, vector org1, vector org2, vector destination1, vector destination2, float timetaken)
+{
+       entity w;
+       entity dw;
+       w = waypoint_spawn(org1, org2, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK);
+       dw = waypoint_spawn(destination1, destination2, WAYPOINTFLAG_GENERATED);
+       // one way link to the destination
+       w.wp00 = dw;
+       w.wp00mincost = timetaken; // this is just for jump pads
+       // the teleporter's nearest spawnfunc_waypoint is this one
+       // (teleporters are not goals, so this is probably useless)
+       e.nearestwaypoint = w;
+       e.nearestwaypointtimeout = time + 1000000000;
+}
+
+void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken)
+{
+       org = waypoint_fixorigin(org);
+       destination = waypoint_fixorigin(destination);
+       waypoint_spawnforteleporter_boxes(e, org, org, destination, destination, timetaken);
+}
+
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken)
+{
+       destination = waypoint_fixorigin(destination);
+       waypoint_spawnforteleporter_boxes(e, e.absmin, e.absmax, destination, destination, timetaken);
+}
+
+entity waypoint_spawnpersonal(entity this, vector position)
+{
+       entity w;
+
+       // drop the waypoint to a proper location:
+       //   first move it up by a player height
+       //   then move it down to hit the floor with player bbox size
+       position = waypoint_fixorigin(position);
+
+       w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL);
+       w.nearestwaypoint = NULL;
+       w.nearestwaypointtimeout = 0;
+       w.owner = this;
+
+       waypoint_schedulerelink(w);
+
+       return w;
+}
+
+void botframe_showwaypointlinks()
+{
+       if (time < botframe_waypointeditorlightningtime)
+               return;
+       botframe_waypointeditorlightningtime = time + 0.5;
+       FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot,
+       {
+               if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
+               {
+                       //navigation_testtracewalk = true;
+                       entity head = navigation_findnearestwaypoint(it, false);
+               //      print("currently selected WP is ", etos(head), "\n");
+                       //navigation_testtracewalk = false;
+                       if (head)
+                       {
+                               entity w;
+                               w = head     ;if (w) te_lightning2(NULL, w.origin, it.origin);
+                               w = head.wp00;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp01;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp02;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp03;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp04;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp05;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp06;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp07;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp08;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp09;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp10;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp11;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp12;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp13;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp14;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp15;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp16;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp17;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp18;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp19;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp20;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp21;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp22;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp23;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp24;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp25;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp26;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp27;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp28;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp29;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp30;if (w) te_lightning2(NULL, w.origin, head.origin);
+                               w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin);
+                       }
+               }
+       });
+}
+
+float botframe_autowaypoints_fixdown(vector v)
+{
+       tracebox(v, PL_MIN_CONST, PL_MAX_CONST, v + '0 0 -64', MOVE_NOMONSTERS, NULL);
+       if(trace_fraction >= 1)
+               return 0;
+       return 1;
+}
+
+float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f)
+{
+       IL_EACH(g_waypoints, boxesoverlap(v - '32 32 32', v + '32 32 32', it.absmin, it.absmax),
+       {
+               // if a matching spawnfunc_waypoint already exists, don't add a duplicate
+               return 0;
+       });
+
+       waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f));
+       return 1;
+}
+
+// return value:
+//    1 = WP created
+//    0 = no action needed
+//   -1 = temp fail, try from world too
+//   -2 = permanent fail, do not retry
+float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .entity fld)
+{
+       // make it possible to go from p to wp, if we can
+       // if wp is NULL, nearest is chosen
+
+       entity w;
+       vector porg;
+       float t, tmin, tmax;
+       vector o;
+       vector save;
+
+       if(!botframe_autowaypoints_fixdown(p.origin))
+               return -2;
+       porg = trace_endpos;
+
+       if(wp)
+       {
+               // if any WP w fulfills wp -> w -> porg and w is closer than wp, then switch from wp to w
+
+               // if wp -> porg, then OK
+               float maxdist;
+               if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050))
+               {
+                       // we may find a better one
+                       maxdist = vlen(wp.origin - porg);
+               }
+               else
+               {
+                       // accept any "good"
+                       maxdist = 2100;
+               }
+
+               float bestdist = maxdist;
+               IL_EACH(g_waypoints, it != wp && !(it.wpflags & WAYPOINTFLAG_NORELINK),
+               {
+                       float d = vlen(wp.origin - it.origin) + vlen(it.origin - porg);
+                       if(d < bestdist)
+                       if(navigation_waypoint_will_link(wp.origin, it.origin, p, walkfromwp, 1050))
+                       if(navigation_waypoint_will_link(it.origin, porg, p, walkfromwp, 1050))
+                       {
+                               bestdist = d;
+                               p.(fld) = it;
+                       }
+               });
+               if(bestdist < maxdist)
+               {
+                       LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
+                       return 0;
+               }
+
+               if(bestdist < 2100)
+               {
+                       // we know maxdist < 2100
+                       // so wp -> porg is still valid
+                       // all is good
+                       p.(fld) = wp;
+                       return 0;
+               }
+
+               // otherwise, no existing WP can fix our issues
+       }
+       else
+       {
+               save = p.origin;
+               setorigin(p, porg);
+               w = navigation_findnearestwaypoint(p, walkfromwp);
+               setorigin(p, save);
+               if(w)
+               {
+                       p.(fld) = w;
+                       return 0;
+               }
+       }
+
+       tmin = 0;
+       tmax = 1;
+       for (;;)
+       {
+               if(tmax - tmin < 0.001)
+               {
+                       // did not get a good candidate
+                       return -1;
+               }
+
+               t = (tmin + tmax) * 0.5;
+               o = antilag_takebackorigin(p, CS(p), time - t);
+               if(!botframe_autowaypoints_fixdown(o))
+                       return -2;
+               o = trace_endpos;
+
+               if(wp)
+               {
+                       if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050))
+                       {
+                               // we cannot walk from wp.origin to o
+                               // get closer to tmax
+                               tmin = t;
+                               continue;
+                       }
+               }
+               else
+               {
+                       save = p.origin;
+                       setorigin(p, o);
+                       w = navigation_findnearestwaypoint(p, walkfromwp);
+                       setorigin(p, save);
+                       if(!w)
+                       {
+                               // we cannot walk from any WP to o
+                               // get closer to tmax
+                               tmin = t;
+                               continue;
+                       }
+               }
+
+               // if we get here, o is valid regarding waypoints
+               // check if o is connected right to the player
+               // we break if it succeeds, as that means o is a good waypoint location
+               if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050))
+                       break;
+
+               // o is no good, we need to get closer to the player
+               tmax = t;
+       }
+
+       LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n");
+       botframe_autowaypoints_createwp(o, p, fld, 0);
+       return 1;
+}
+
+// automatically create missing waypoints
+.entity botframe_autowaypoints_lastwp0, botframe_autowaypoints_lastwp1;
+void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld)
+{
+       float r = botframe_autowaypoints_fix_from(p, walkfromwp, p.(fld), fld);
+       if(r != -1)
+               return;
+       r = botframe_autowaypoints_fix_from(p, walkfromwp, NULL, fld);
+       if(r != -1)
+               return;
+
+       LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n");
+       if(!botframe_autowaypoints_fixdown(p.origin))
+               return; // shouldn't happen, caught above
+       botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED);
+}
+
+void botframe_deleteuselesswaypoints()
+{
+       IL_EACH(g_items, it.bot_pickup,
+       {
+               // NOTE: this protects waypoints if they're the ONLY nearest
+               // waypoint. That's the intention.
+               navigation_findnearestwaypoint(it, false);  // Walk TO item.
+               navigation_findnearestwaypoint(it, true);  // Walk FROM item.
+       });
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpflags |= WAYPOINTFLAG_DEAD_END;
+               it.wpflags &= ~WAYPOINTFLAG_USEFUL;
+               // WP is useful if:
+               if (it.wpflags & WAYPOINTFLAG_ITEM)
+                       it.wpflags |= WAYPOINTFLAG_USEFUL;
+               if (it.wpflags & WAYPOINTFLAG_TELEPORT)
+                       it.wpflags |= WAYPOINTFLAG_USEFUL;
+               if (it.wpflags & WAYPOINTFLAG_PROTECTED)
+                       it.wpflags |= WAYPOINTFLAG_USEFUL;
+               // b) WP is closest WP for an item/spawnpoint/other entity
+               //    This has been done above by protecting these WPs.
+       });
+       // c) There are w1, w, w2 so that w1 -> w, w -> w2 and not w1 -> w2.
+       IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_PERSONAL),
+       {
+               for (int m = 0; m < 32; ++m)
+               {
+                       entity w = waypoint_get_link(it, m);
+                       if (!w)
+                               break;
+                       if (w.wpflags & WAYPOINTFLAG_PERSONAL)
+                               continue;
+                       if (w.wpflags & WAYPOINTFLAG_USEFUL)
+                               continue;
+                       for (int j = 0; j < 32; ++j)
+                       {
+                               entity w2 = waypoint_get_link(w, j);
+                               if (!w2)
+                                       break;
+                               if (it == w2)
+                                       continue;
+                               if (w2.wpflags & WAYPOINTFLAG_PERSONAL)
+                                       continue;
+                               // If we got here, it != w2 exist with it -> w
+                               // and w -> w2. That means the waypoint is not
+                               // a dead end.
+                               w.wpflags &= ~WAYPOINTFLAG_DEAD_END;
+                               for (int k = 0; k < 32; ++k)
+                               {
+                                       if (waypoint_get_link(it, k) == w2)
+                                               continue;
+                                       // IF WE GET HERE, w is proven useful
+                                       // to get from it to w2!
+                                       w.wpflags |= WAYPOINTFLAG_USEFUL;
+                                       goto next;
+                               }
+                       }
+LABEL(next)
+               }
+       });
+       // d) The waypoint is a dead end. Dead end waypoints must be kept as
+       //     they are needed to complete routes while autowaypointing.
+
+       IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)),
+       {
+               LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", it.origin);
+               te_explosion(it.origin);
+               waypoint_remove(it);
+               break;
+       });
+
+       IL_EACH(g_waypoints, true,
+       {
+               it.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag
+       });
+}
+
+void botframe_autowaypoints()
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA(
+               // going back is broken, so only fix waypoints to walk TO the player
+               //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0);
+               botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1);
+               //te_explosion(p.botframe_autowaypoints_lastwp0.origin);
+       ));
+
+       if (autocvar_g_waypointeditor_auto >= 2) {
+               botframe_deleteuselesswaypoints();
+       }
+}
+
diff --git a/qcsrc/server/bot/default/waypoints.qh b/qcsrc/server/bot/default/waypoints.qh
new file mode 100644 (file)
index 0000000..38d57a0
--- /dev/null
@@ -0,0 +1,58 @@
+#pragma once
+/*
+ * Globals and Fields
+ */
+
+// fields you can query using prvm_global server to get some statistics about waypoint linking culling
+float relink_total, relink_walkculled, relink_pvsculled, relink_lengthculled;
+
+float botframe_waypointeditorlightningtime;
+float botframe_loadedforcedlinks;
+float botframe_cachedwaypointlinks;
+
+.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15;
+.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31;
+
+// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance
+.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost;
+.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost;
+.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost;
+.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost;
+
+.float wpfire, wpcost, wpconsidered, wpisbox, wplinked, wphardwired;
+.int wpflags;
+
+.vector wpnearestpoint;
+
+/*
+ * Functions
+ */
+
+spawnfunc(waypoint);
+void waypoint_addlink(entity from, entity to);
+void waypoint_think(entity this);
+void waypoint_clearlinks(entity wp);
+void waypoint_schedulerelink(entity wp);
+
+void waypoint_remove(entity e);
+void waypoint_removeall();
+void waypoint_schedulerelinkall();
+void waypoint_load_links_hardwired();
+void waypoint_save_links();
+void waypoint_saveall();
+
+void waypoint_spawnforitem_force(entity e, vector org);
+void waypoint_spawnforitem(entity e);
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
+void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
+void botframe_showwaypointlinks();
+
+float waypoint_loadall();
+float waypoint_load_links();
+
+entity waypoint_spawn(vector m1, vector m2, float f);
+entity waypoint_spawnpersonal(entity this, vector position);
+
+vector waypoint_fixorigin(vector position);
+
+void botframe_autowaypoints();
diff --git a/qcsrc/server/bot/havocbot/_mod.inc b/qcsrc/server/bot/havocbot/_mod.inc
deleted file mode 100644 (file)
index a6270bc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-// generated file; do not modify
-#include <server/bot/havocbot/havocbot.qc>
-#include <server/bot/havocbot/roles.qc>
diff --git a/qcsrc/server/bot/havocbot/_mod.qh b/qcsrc/server/bot/havocbot/_mod.qh
deleted file mode 100644 (file)
index 4b62d1b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-// generated file; do not modify
-#include <server/bot/havocbot/havocbot.qh>
-#include <server/bot/havocbot/roles.qh>
diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc
deleted file mode 100644 (file)
index f6c0348..0000000
+++ /dev/null
@@ -1,1321 +0,0 @@
-#include "havocbot.qh"
-
-#include "../aim.qh"
-#include "../bot.qh"
-#include "../navigation.qh"
-#include "../scripting.qh"
-#include "../waypoints.qh"
-
-#include <common/constants.qh>
-#include <common/physics/player.qh>
-#include <common/state.qh>
-#include <common/items/all.qh>
-
-#include <common/triggers/trigger/jumppads.qh>
-
-#include <lib/warpzone/common.qh>
-
-.float speed;
-
-void havocbot_ai(entity this)
-{
-       if(this.draggedby)
-               return;
-
-       if(bot_execute_commands(this))
-               return;
-
-       if (bot_strategytoken == this)
-       if (!bot_strategytoken_taken)
-       {
-               if(this.havocbot_blockhead)
-               {
-                       this.havocbot_blockhead = false;
-               }
-               else
-               {
-                       if (!this.jumppadcount)
-                               this.havocbot_role(this); // little too far down the rabbit hole
-               }
-
-               // TODO: tracewalk() should take care of this job (better path finding under water)
-               // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
-               if(IS_DEAD(this))
-               if(this.goalcurrent==NULL)
-               if(this.waterlevel==WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
-               {
-                       // Look for the closest waypoint out of water
-                       entity newgoal, head;
-                       float bestdistance, distance;
-
-                       newgoal = NULL;
-                       bestdistance = 10000;
-                       for (head = findchain(classname, "waypoint"); head; head = head.chain)
-                       {
-                               distance = vlen(head.origin - this.origin);
-                               if(distance>10000)
-                                       continue;
-
-                               if(head.origin.z < this.origin.z)
-                                       continue;
-
-                               if(head.origin.z - this.origin.z - this.view_ofs.z > 100)
-                                       continue;
-
-                               if (pointcontents(head.origin + head.maxs + '0 0 1') != CONTENT_EMPTY)
-                                       continue;
-
-                               traceline(this.origin + this.view_ofs , head.origin, true, head);
-
-                               if(trace_fraction<1)
-                                       continue;
-
-                               if(distance<bestdistance)
-                               {
-                                       newgoal = head;
-                                       bestdistance = distance;
-                               }
-                       }
-
-                       if(newgoal)
-                       {
-                       //      te_wizspike(newgoal.origin);
-                               navigation_pushroute(this, newgoal);
-                       }
-               }
-
-               // token has been used this frame
-               bot_strategytoken_taken = true;
-       }
-
-       if(IS_DEAD(this))
-               return;
-
-       havocbot_chooseenemy(this);
-       if (this.bot_chooseweapontime < time )
-       {
-               this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
-               havocbot_chooseweapon(this);
-       }
-       havocbot_aim(this);
-       lag_update(this);
-       if (this.bot_aimtarg)
-       {
-               this.aistatus |= AI_STATUS_ATTACKING;
-               this.aistatus &= ~AI_STATUS_ROAMING;
-
-               if(this.weapons)
-               {
-                       Weapon w = PS(this).m_weapon;
-                       w.wr_aim(w, this);
-                       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
-                       {
-                               PHYS_INPUT_BUTTON_ATCK(this) = false;
-                               PHYS_INPUT_BUTTON_ATCK2(this) = false;
-                       }
-                       else
-                       {
-                               if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))
-                                       this.lastfiredweapon = PS(this).m_weapon.m_id;
-                       }
-               }
-               else
-               {
-                       if(IS_PLAYER(this.bot_aimtarg))
-                               bot_aimdir(this, this.bot_aimtarg.origin + this.bot_aimtarg.view_ofs - this.origin - this.view_ofs , -1);
-               }
-       }
-       else if (this.goalcurrent)
-       {
-               this.aistatus |= AI_STATUS_ROAMING;
-               this.aistatus &= ~AI_STATUS_ATTACKING;
-
-               vector now,v,next;//,heading;
-               float aimdistance,skillblend,distanceblend,blend;
-               next = now = ( (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5) - (this.origin + this.view_ofs);
-               aimdistance = vlen(now);
-               //heading = this.velocity;
-               //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n");
-               if(
-                       this.goalstack01 != this && this.goalstack01 != NULL && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
-                       !(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
-               )
-                       next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs);
-
-               skillblend=bound(0,(skill+this.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
-               distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1);
-               blend = skillblend * (1-distanceblend);
-               //v = (now * (distanceblend) + next * (1-distanceblend)) * (skillblend) + now * (1-skillblend);
-               //v = now * (distanceblend) * (skillblend) + next * (1-distanceblend) * (skillblend) + now * (1-skillblend);
-               //v = now * ((1-skillblend) + (distanceblend) * (skillblend)) + next * (1-distanceblend) * (skillblend);
-               v = now + blend * (next - now);
-               //dprint(etos(this), " ");
-               //dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n");
-               //v = now * (distanceblend) + next * (1-distanceblend);
-               if (this.waterlevel < WATERLEVEL_SWIMMING)
-                       v.z = 0;
-               //dprint("walk at:", vtos(v), "\n");
-               //te_lightning2(NULL, this.origin, this.goalcurrent.origin);
-               bot_aimdir(this, v, -1);
-       }
-       havocbot_movetogoal(this);
-
-       // if the bot is not attacking, consider reloading weapons
-       if (!(this.aistatus & AI_STATUS_ATTACKING))
-       {
-               // we are currently holding a weapon that's not fully loaded, reload it
-               if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
-               if(this.clip_load < this.clip_size)
-                       this.impulse = 20; // "press" the reload button, not sure if this is done right
-
-               // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
-               // the code above executes next frame, starting the reloading then
-               if(skill >= 5) // bots can only look for unloaded weapons past this skill
-               if(this.clip_load >= 0) // only if we're not reloading a weapon already
-               {
-                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                               if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo))
-                                       PS(this).m_switchweapon = it;
-                       ));
-               }
-       }
-}
-
-void havocbot_keyboard_movement(entity this, vector destorg)
-{
-       vector keyboard;
-       float blend, maxspeed;
-       float sk;
-
-       sk = skill + this.bot_moveskill;
-
-       maxspeed = autocvar_sv_maxspeed;
-
-       if (time < this.havocbot_keyboardtime)
-               return;
-
-       this.havocbot_keyboardtime =
-               max(
-                       this.havocbot_keyboardtime
-                               + 0.05/max(1, sk+this.havocbot_keyboardskill)
-                               + random()*0.025/max(0.00025, skill+this.havocbot_keyboardskill)
-               , time);
-       keyboard = this.movement * (1.0 / maxspeed);
-
-       float trigger, trigger1;
-       blend = bound(0,sk*0.1,1);
-       trigger = autocvar_bot_ai_keyboard_threshold;
-       trigger1 = 0 - trigger;
-
-       // categorize forward movement
-       // at skill < 1.5 only forward
-       // at skill < 2.5 only individual directions
-       // at skill < 4.5 only individual directions, and forward diagonals
-       // at skill >= 4.5, all cases allowed
-       if (keyboard.x > trigger)
-       {
-               keyboard.x = 1;
-               if (sk < 2.5)
-                       keyboard.y = 0;
-       }
-       else if (keyboard.x < trigger1 && sk > 1.5)
-       {
-               keyboard.x = -1;
-               if (sk < 4.5)
-                       keyboard.y = 0;
-       }
-       else
-       {
-               keyboard.x = 0;
-               if (sk < 1.5)
-                       keyboard.y = 0;
-       }
-       if (sk < 4.5)
-               keyboard.z = 0;
-
-       if (keyboard.y > trigger)
-               keyboard.y = 1;
-       else if (keyboard.y < trigger1)
-               keyboard.y = -1;
-       else
-               keyboard.y = 0;
-
-       if (keyboard.z > trigger)
-               keyboard.z = 1;
-       else if (keyboard.z < trigger1)
-               keyboard.z = -1;
-       else
-               keyboard.z = 0;
-
-       this.havocbot_keyboard = keyboard * maxspeed;
-       if (this.havocbot_ducktime>time) PHYS_INPUT_BUTTON_CROUCH(this) = true;
-
-       keyboard = this.havocbot_keyboard;
-       blend = bound(0,vlen(destorg-this.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree
-       //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
-       this.movement = this.movement + (keyboard - this.movement) * blend;
-}
-
-void havocbot_bunnyhop(entity this, vector dir)
-{
-       float bunnyhopdistance;
-       vector deviation;
-       float maxspeed;
-       vector gco, gno;
-
-       // Don't jump when attacking
-       if(this.aistatus & AI_STATUS_ATTACKING)
-               return;
-
-       if(IS_PLAYER(this.goalcurrent))
-               return;
-
-       maxspeed = autocvar_sv_maxspeed;
-
-       if(this.aistatus & AI_STATUS_DANGER_AHEAD)
-       {
-               this.aistatus &= ~AI_STATUS_RUNNING;
-               PHYS_INPUT_BUTTON_JUMP(this) = false;
-               this.bot_canruntogoal = 0;
-               this.bot_timelastseengoal = 0;
-               return;
-       }
-
-       if(this.waterlevel > WATERLEVEL_WETFEET)
-       {
-               this.aistatus &= ~AI_STATUS_RUNNING;
-               return;
-       }
-
-       if(this.bot_lastseengoal != this.goalcurrent && !(this.aistatus & AI_STATUS_RUNNING))
-       {
-               this.bot_canruntogoal = 0;
-               this.bot_timelastseengoal = 0;
-       }
-
-       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
-       bunnyhopdistance = vlen(this.origin - gco);
-
-       // Run only to visible goals
-       if(IS_ONGROUND(this))
-       if(this.speed==maxspeed)
-       if(checkpvs(this.origin + this.view_ofs, this.goalcurrent))
-       {
-                       this.bot_lastseengoal = this.goalcurrent;
-
-                       // seen it before
-                       if(this.bot_timelastseengoal)
-                       {
-                               // for a period of time
-                               if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
-                               {
-                                       float checkdistance;
-                                       checkdistance = true;
-
-                                       // don't run if it is too close
-                                       if(this.bot_canruntogoal==0)
-                                       {
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance)
-                                                       this.bot_canruntogoal = 1;
-                                               else
-                                                       this.bot_canruntogoal = -1;
-                                       }
-
-                                       if(this.bot_canruntogoal != 1)
-                                               return;
-
-                                       if(this.aistatus & AI_STATUS_ROAMING)
-                                       if(this.goalcurrent.classname=="waypoint")
-                                       if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
-                                       if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z)
-                                       if(this.goalstack01!=NULL)
-                                       {
-                                               gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
-                                               deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin);
-                                               while (deviation.y < -180) deviation.y = deviation.y + 360;
-                                               while (deviation.y > 180) deviation.y = deviation.y - 360;
-
-                                               if(fabs(deviation.y) < 20)
-                                               if(bunnyhopdistance < vlen(this.origin - gno))
-                                               if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z)
-                                               {
-                                                       if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance))
-                                                       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
-                                                       {
-                                                               checkdistance = false;
-                                                       }
-                                               }
-                                       }
-
-                                       if(checkdistance)
-                                       {
-                                               this.aistatus &= ~AI_STATUS_RUNNING;
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
-                                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
-                                       }
-                                       else
-                                       {
-                                               this.aistatus |= AI_STATUS_RUNNING;
-                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               this.bot_timelastseengoal = time;
-                       }
-       }
-       else
-       {
-               this.bot_timelastseengoal = 0;
-       }
-
-#if 0
-       // Release jump button
-       if(!cvar("sv_pogostick"))
-       if((IS_ONGROUND(this)) == 0)
-       {
-               if(this.velocity.z < 0 || vlen(this.velocity)<maxspeed)
-                       PHYS_INPUT_BUTTON_JUMP(this) = false;
-
-               // Strafe
-               if(this.aistatus & AI_STATUS_RUNNING)
-               if(vlen(this.velocity)>maxspeed)
-               {
-                       deviation = vectoangles(dir) - vectoangles(this.velocity);
-                       while (deviation.y < -180) deviation.y = deviation.y + 360;
-                       while (deviation.y > 180) deviation.y = deviation.y - 360;
-
-                       if(fabs(deviation.y)>10)
-                               this.movement_x = 0;
-
-                       if(deviation.y>10)
-                               this.movement_y = maxspeed * -1;
-                       else if(deviation.y<10)
-                               this.movement_y = maxspeed;
-
-               }
-       }
-#endif
-}
-
-void havocbot_movetogoal(entity this)
-{
-       vector destorg;
-       vector diff;
-       vector dir;
-       vector flatdir;
-       vector m1;
-       vector m2;
-       vector evadeobstacle;
-       vector evadelava;
-       float s;
-       float maxspeed;
-       vector gco;
-       //float dist;
-       vector dodge;
-       //if (this.goalentity)
-       //      te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5);
-       this.movement = '0 0 0';
-       maxspeed = autocvar_sv_maxspeed;
-
-       // Jetpack navigation
-       if(this.goalcurrent)
-       if(this.navigation_jetpack_goal)
-       if(this.goalcurrent==this.navigation_jetpack_goal)
-       if(this.ammo_fuel)
-       {
-               if(autocvar_bot_debug_goalstack)
-               {
-                       debuggoalstack(this);
-                       te_wizspike(this.navigation_jetpack_point);
-               }
-
-               // Take off
-               if (!(this.aistatus & AI_STATUS_JETPACK_FLYING))
-               {
-                       // Brake almost completely so it can get a good direction
-                       if(vdist(this.velocity, >, 10))
-                               return;
-                       this.aistatus |= AI_STATUS_JETPACK_FLYING;
-               }
-
-               makevectors(this.v_angle.y * '0 1 0');
-               dir = normalize(this.navigation_jetpack_point - this.origin);
-
-               // Landing
-               if(this.aistatus & AI_STATUS_JETPACK_LANDING)
-               {
-                       // Calculate brake distance in xy
-                       float db, v, d;
-                       vector dxy;
-
-                       dxy = this.origin - ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ); dxy.z = 0;
-                       d = vlen(dxy);
-                       v = vlen(this.velocity -  this.velocity.z * '0 0 1');
-                       db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
-               //      dprint("distance ", ftos(ceil(d)), " velocity ", ftos(ceil(v)), " brake at ", ftos(ceil(db)), "\n");
-                       if(d < db || d < 500)
-                       {
-                               // Brake
-                               if(fabs(this.velocity.x)>maxspeed*0.3)
-                               {
-                                       this.movement_x = dir * v_forward * -maxspeed;
-                                       return;
-                               }
-                               // Switch to normal mode
-                               this.navigation_jetpack_goal = NULL;
-                               this.aistatus &= ~AI_STATUS_JETPACK_LANDING;
-                               this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
-                               return;
-                       }
-               }
-               else if(checkpvs(this.origin,this.goalcurrent))
-               {
-                       // If I can see the goal switch to landing code
-                       this.aistatus &= ~AI_STATUS_JETPACK_FLYING;
-                       this.aistatus |= AI_STATUS_JETPACK_LANDING;
-                       return;
-               }
-
-               // Flying
-               PHYS_INPUT_BUTTON_HOOK(this) = true;
-               if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z)
-               {
-                       this.movement_x = dir * v_forward * maxspeed;
-                       this.movement_y = dir * v_right * maxspeed;
-               }
-               return;
-       }
-
-       // Handling of jump pads
-       if(this.jumppadcount)
-       {
-               // If got stuck on the jump pad try to reach the farthest visible waypoint
-               if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
-               {
-                       if(fabs(this.velocity.z)<50)
-                       {
-                               entity head, newgoal = NULL;
-                               float distance, bestdistance = 0;
-
-                               for (head = findchain(classname, "waypoint"); head; head = head.chain)
-                               {
-
-                                       distance = vlen(head.origin - this.origin);
-                                       if(distance>1000)
-                                               continue;
-
-                                       traceline(this.origin + this.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), true, NULL);
-
-                                       if(trace_fraction<1)
-                                               continue;
-
-                                       if(distance>bestdistance)
-                                       {
-                                               newgoal = head;
-                                               bestdistance = distance;
-                                       }
-                               }
-
-                               if(newgoal)
-                               {
-                                       this.ignoregoal = this.goalcurrent;
-                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
-                                       navigation_clearroute(this);
-                                       navigation_routetogoal(this, newgoal, this.origin);
-                                       this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
-                               }
-                       }
-                       else
-                               return;
-               }
-               else
-               {
-                       if(this.velocity.z>0)
-                       {
-                               float threshold;
-                               vector velxy = this.velocity; velxy_z = 0;
-                               threshold = maxspeed * 0.2;
-                               if(vdist(velxy, <, threshold))
-                               {
-                                       LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now\n");
-                                       this.aistatus |= AI_STATUS_OUT_JUMPPAD;
-                               }
-                               return;
-                       }
-
-                       // Don't chase players while using a jump pad
-                       if(IS_PLAYER(this.goalcurrent) || IS_PLAYER(this.goalstack01))
-                               return;
-               }
-       }
-       else if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
-               this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
-
-       // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
-       if(skill>6)
-       if (!(IS_ONGROUND(this)))
-       {
-               tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this);
-               if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos ))
-               if(this.items & IT_JETPACK)
-               {
-                       tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this);
-                       if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' ))
-                       {
-                               if(this.velocity.z<0)
-                               {
-                                       PHYS_INPUT_BUTTON_HOOK(this) = true;
-                               }
-                       }
-                       else
-                               PHYS_INPUT_BUTTON_HOOK(this) = true;
-
-                       // If there is no goal try to move forward
-
-                       if(this.goalcurrent==NULL)
-                               dir = v_forward;
-                       else
-                               dir = normalize(( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - this.origin);
-
-                       vector xyvelocity = this.velocity; xyvelocity_z = 0;
-                       float xyspeed = xyvelocity * dir;
-
-                       if(xyspeed < (maxspeed / 2))
-                       {
-                               makevectors(this.v_angle.y * '0 1 0');
-                               tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this);
-                               if(trace_fraction==1)
-                               {
-                                       this.movement_x = dir * v_forward * maxspeed;
-                                       this.movement_y = dir * v_right * maxspeed;
-                                       if (skill < 10)
-                                               havocbot_keyboard_movement(this, this.origin + dir * 100);
-                               }
-                       }
-
-                       this.havocbot_blockhead = true;
-
-                       return;
-               }
-               else if(this.health>WEP_CVAR(devastator, damage)*0.5)
-               {
-                       if(this.velocity.z < 0)
-                       if(client_hasweapon(this, WEP_DEVASTATOR, true, false))
-                       {
-                               this.movement_x = maxspeed;
-
-                               if(this.rocketjumptime)
-                               {
-                                       if(time > this.rocketjumptime)
-                                       {
-                                               PHYS_INPUT_BUTTON_ATCK2(this) = true;
-                                               this.rocketjumptime = 0;
-                                       }
-                                       return;
-                               }
-
-                               PS(this).m_switchweapon = WEP_DEVASTATOR;
-                               this.v_angle_x = 90;
-                               PHYS_INPUT_BUTTON_ATCK(this) = true;
-                               this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
-                               return;
-                       }
-               }
-               else
-               {
-                       // If there is no goal try to move forward
-                       if(this.goalcurrent==NULL)
-                               this.movement_x = maxspeed;
-               }
-       }
-
-       // If we are under water with no goals, swim up
-       if(this.waterlevel)
-       if(this.goalcurrent==NULL)
-       {
-               dir = '0 0 0';
-               if(this.waterlevel>WATERLEVEL_SWIMMING)
-                       dir.z = 1;
-               else if(this.velocity.z >= 0 && !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER))
-                       PHYS_INPUT_BUTTON_JUMP(this) = true;
-               else
-                       PHYS_INPUT_BUTTON_JUMP(this) = false;
-               makevectors(this.v_angle.y * '0 1 0');
-               this.movement_x = dir * v_forward * maxspeed;
-               this.movement_y = dir * v_right * maxspeed;
-               this.movement_z = dir * v_up * maxspeed;
-       }
-
-       // if there is nowhere to go, exit
-       if (this.goalcurrent == NULL)
-               return;
-
-       if (this.goalcurrent)
-               navigation_poptouchedgoals(this);
-
-       // if ran out of goals try to use an alternative goal or get a new strategy asap
-       if(this.goalcurrent == NULL)
-       {
-               this.bot_strategytime = 0;
-               return;
-       }
-
-
-       if(autocvar_bot_debug_goalstack)
-               debuggoalstack(this);
-
-       m1 = this.goalcurrent.origin + this.goalcurrent.mins;
-       m2 = this.goalcurrent.origin + this.goalcurrent.maxs;
-       destorg = this.origin;
-       destorg.x = bound(m1_x, destorg.x, m2_x);
-       destorg.y = bound(m1_y, destorg.y, m2_y);
-       destorg.z = bound(m1_z, destorg.z, m2_z);
-       diff = destorg - this.origin;
-       //dist = vlen(diff);
-       dir = normalize(diff);
-       flatdir = diff;flatdir.z = 0;
-       flatdir = normalize(flatdir);
-       gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5;
-
-       //if (this.bot_dodgevector_time < time)
-       {
-       //      this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval");
-       //      this.bot_dodgevector_jumpbutton = 1;
-               evadeobstacle = '0 0 0';
-               evadelava = '0 0 0';
-
-               if (this.waterlevel)
-               {
-                       if(this.waterlevel>WATERLEVEL_SWIMMING)
-                       {
-                       //      flatdir_z = 1;
-                               this.aistatus |= AI_STATUS_OUT_WATER;
-                       }
-                       else
-                       {
-                               if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) &&
-                                       ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER))
-                                       PHYS_INPUT_BUTTON_JUMP(this) = true;
-                               else
-                                       PHYS_INPUT_BUTTON_JUMP(this) = false;
-                       }
-                       dir = normalize(flatdir);
-                       makevectors(this.v_angle.y * '0 1 0');
-               }
-               else
-               {
-                       if(this.aistatus & AI_STATUS_OUT_WATER)
-                               this.aistatus &= ~AI_STATUS_OUT_WATER;
-
-                       // jump if going toward an obstacle that doesn't look like stairs we
-                       // can walk up directly
-                       tracebox(this.origin, this.mins, this.maxs, this.origin + this.velocity * 0.2, false, this);
-                       if (trace_fraction < 1)
-                       if (trace_plane_normal.z < 0.7)
-                       {
-                               s = trace_fraction;
-                               tracebox(this.origin + stepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + stepheightvec, false, this);
-                               if (trace_fraction < s + 0.01)
-                               if (trace_plane_normal.z < 0.7)
-                               {
-                                       s = trace_fraction;
-                                       tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + jumpstepheightvec, false, this);
-                                       if (trace_fraction > s)
-                                               PHYS_INPUT_BUTTON_JUMP(this) = true;
-                               }
-                       }
-
-                       // avoiding dangers and obstacles
-                       vector dst_ahead, dst_down;
-                       makevectors(this.v_angle.y * '0 1 0');
-                       dst_ahead = this.origin + this.view_ofs + (this.velocity * 0.4) + (v_forward * 32 * 3);
-                       dst_down = dst_ahead - '0 0 1500';
-
-                       // Look ahead
-                       traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
-
-                       // Check head-banging against walls
-                       if(vdist(this.origin + this.view_ofs - trace_endpos, <, 25) && !(this.aistatus & AI_STATUS_OUT_WATER))
-                       {
-                               PHYS_INPUT_BUTTON_JUMP(this) = true;
-                               if(this.facingwalltime && time > this.facingwalltime)
-                               {
-                                       this.ignoregoal = this.goalcurrent;
-                                       this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
-                                       this.bot_strategytime = 0;
-                                       return;
-                               }
-                               else
-                               {
-                                       this.facingwalltime = time + 0.05;
-                               }
-                       }
-                       else
-                       {
-                               this.facingwalltime = 0;
-
-                               if(this.ignoregoal != NULL && time > this.ignoregoaltime)
-                               {
-                                       this.ignoregoal = NULL;
-                                       this.ignoregoaltime = 0;
-                               }
-                       }
-
-                       // Check for water/slime/lava and dangerous edges
-                       // (only when the bot is on the ground or jumping intentionally)
-                       this.aistatus &= ~AI_STATUS_DANGER_AHEAD;
-
-                       if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired )
-                       if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || PHYS_INPUT_BUTTON_JUMP(this))
-                       {
-                               // Look downwards
-                               traceline(dst_ahead , dst_down, true, NULL);
-                       //      te_lightning2(NULL, this.origin, dst_ahead);    // Draw "ahead" look
-                       //      te_lightning2(NULL, dst_ahead, dst_down);               // Draw "downwards" look
-                               if(trace_endpos.z < this.origin.z + this.mins.z)
-                               {
-                                       s = pointcontents(trace_endpos + '0 0 1');
-                                       if (s != CONTENT_SOLID)
-                                       if (s == CONTENT_LAVA || s == CONTENT_SLIME)
-                                               evadelava = normalize(this.velocity) * -1;
-                                       else if (s == CONTENT_SKY)
-                                               evadeobstacle = normalize(this.velocity) * -1;
-                                       else if (!boxesoverlap(dst_ahead - this.view_ofs + this.mins, dst_ahead - this.view_ofs + this.maxs,
-                                                               this.goalcurrent.absmin, this.goalcurrent.absmax))
-                                       {
-                                               // if ain't a safe goal with "holes" (like the jumpad on soylent)
-                                               // and there is a trigger_hurt below
-                                               if(tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
-                                               {
-                                                       // Remove dangerous dynamic goals from stack
-                                                       LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
-                                                       navigation_clearroute(this);
-                                                       return;
-                                               }
-                                       }
-                               }
-                       }
-
-                       dir = flatdir;
-                       evadeobstacle.z = 0;
-                       evadelava.z = 0;
-                       makevectors(this.v_angle.y * '0 1 0');
-
-                       if(evadeobstacle!='0 0 0'||evadelava!='0 0 0')
-                               this.aistatus |= AI_STATUS_DANGER_AHEAD;
-               }
-
-               dodge = havocbot_dodge(this);
-               dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1);
-               evadelava = evadelava * bound(1,3-(skill+this.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
-               traceline(this.origin, ( ( this.enemy.absmin + this.enemy.absmax ) * 0.5 ), true, NULL);
-               if(IS_PLAYER(trace_ent))
-                       dir = dir * bound(0,(skill+this.bot_dodgeskill)/7,1);
-
-               dir = normalize(dir + dodge + evadeobstacle + evadelava);
-       //      this.bot_dodgevector = dir;
-       //      this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this);
-       }
-
-       if(time < this.ladder_time)
-       {
-               if(this.goalcurrent.origin.z + this.goalcurrent.mins.z > this.origin.z + this.mins.z)
-               {
-                       if(this.origin.z + this.mins.z  < this.ladder_entity.origin.z + this.ladder_entity.maxs.z)
-                               dir.z = 1;
-               }
-               else
-               {
-                       if(this.origin.z + this.mins.z  > this.ladder_entity.origin.z + this.ladder_entity.mins.z)
-                               dir.z = -1;
-               }
-       }
-
-       //dir = this.bot_dodgevector;
-       //if (this.bot_dodgevector_jumpbutton)
-       //      PHYS_INPUT_BUTTON_JUMP(this) = true;
-       this.movement_x = dir * v_forward * maxspeed;
-       this.movement_y = dir * v_right * maxspeed;
-       this.movement_z = dir * v_up * maxspeed;
-
-       // Emulate keyboard interface
-       if (skill < 10)
-               havocbot_keyboard_movement(this, destorg);
-
-       // Bunnyhop!
-//     if(this.aistatus & AI_STATUS_ROAMING)
-       if(this.goalcurrent)
-       if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
-               havocbot_bunnyhop(this, dir);
-
-       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(this))) PHYS_INPUT_BUTTON_JUMP(this) = true;
-       if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) PHYS_INPUT_BUTTON_JUMP(this) = true;
-       if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10);
-}
-
-void havocbot_chooseenemy(entity this)
-{
-       entity head, best, head2;
-       float rating, bestrating, hf;
-       vector eye, v;
-       if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
-       {
-               this.enemy = NULL;
-               return;
-       }
-       if (this.enemy)
-       {
-               if (!bot_shouldattack(this, this.enemy))
-               {
-                       // enemy died or something, find a new target
-                       this.enemy = NULL;
-                       this.havocbot_chooseenemy_finished = time;
-               }
-               else if (this.havocbot_stickenemy)
-               {
-                       // tracking last chosen enemy
-                       // if enemy is visible
-                       // and not really really far away
-                       // and we're not severely injured
-                       // then keep tracking for a half second into the future
-                       traceline(this.origin+this.view_ofs, ( this.enemy.absmin + this.enemy.absmax ) * 0.5,false,NULL);
-                       if (trace_ent == this.enemy || trace_fraction == 1)
-                       if (vdist(((this.enemy.absmin + this.enemy.absmax) * 0.5) - this.origin, <, 1000))
-                       if (this.health > 30)
-                       {
-                               // remain tracking him for a shot while (case he went after a small corner or pilar
-                               this.havocbot_chooseenemy_finished = time + 0.5;
-                               return;
-                       }
-                       // enemy isn't visible, or is far away, or we're injured severely
-                       // so stop preferring this enemy
-                       // (it will still take a half second until a new one is chosen)
-                       this.havocbot_stickenemy = 0;
-               }
-       }
-       if (time < this.havocbot_chooseenemy_finished)
-               return;
-       this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
-       eye = this.origin + this.view_ofs;
-       best = NULL;
-       bestrating = 100000000;
-       head = head2 = findchainfloat(bot_attack, true);
-
-       // Backup hit flags
-       hf = this.dphitcontentsmask;
-
-       // Search for enemies, if no enemy can be seen directly try to look through transparent objects
-
-       this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-
-       bool scan_transparent = false;
-       bool scan_secondary_targets = false;
-       bool have_secondary_targets = false;
-       while(true)
-       {
-               scan_secondary_targets = false;
-LABEL(scan_targets)
-               for( ; head; head = head.chain)
-               {
-                       if(!scan_secondary_targets)
-                       {
-                               if(head.classname == "misc_breakablemodel")
-                               {
-                                       have_secondary_targets = true;
-                                       continue;
-                               }
-                       }
-                       else
-                       {
-                               if(head.classname != "misc_breakablemodel")
-                                       continue;
-                       }
-
-                       v = (head.absmin + head.absmax) * 0.5;
-                       rating = vlen(v - eye);
-                       if (rating<autocvar_bot_ai_enemydetectionradius)
-                       if (bestrating > rating)
-                       if (bot_shouldattack(this, head))
-                       {
-                               traceline(eye, v, true, this);
-                               if (trace_ent == head || trace_fraction >= 1)
-                               {
-                                       best = head;
-                                       bestrating = rating;
-                               }
-                       }
-               }
-
-               if(!best && have_secondary_targets && !scan_secondary_targets)
-               {
-                       scan_secondary_targets = true;
-                       // restart the loop
-                       head = head2;
-                       bestrating = 100000000;
-                       goto scan_targets;
-               }
-
-               // I want to do a second scan if no enemy was found or I don't have weapons
-               // TODO: Perform the scan when using the rifle (requires changes on the rifle code)
-               if(best || this.weapons) // || this.weapon == WEP_RIFLE.m_id
-                       break;
-               if(scan_transparent)
-                       break;
-
-               // Set flags to see through transparent objects
-               this.dphitcontentsmask |= DPCONTENTS_OPAQUE;
-
-               head = head2;
-               scan_transparent = true;
-       }
-
-       // Restore hit flags
-       this.dphitcontentsmask = hf;
-
-       this.enemy = best;
-       this.havocbot_stickenemy = true;
-       if(best && best.classname == "misc_breakablemodel")
-               this.havocbot_stickenemy = false;
-}
-
-float havocbot_chooseweapon_checkreload(entity this, int new_weapon)
-{
-       // bots under this skill cannot find unloaded weapons to reload idly when not in combat,
-       // so skip this for them, or they'll never get to reload their weapons at all.
-       // this also allows bots under this skill to be more stupid, and reload more often during combat :)
-       if(skill < 5)
-               return false;
-
-       // if this weapon is scheduled for reloading, don't switch to it during combat
-       if (this.weapon_load[new_weapon] < 0)
-       {
-               bool other_weapon_available = false;
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this))
-                               other_weapon_available = true;
-               ));
-               if(other_weapon_available)
-                       return true;
-       }
-
-       return false;
-}
-
-void havocbot_chooseweapon(entity this)
-{
-       int i;
-
-       // ;)
-       if(g_weaponarena_weapons == WEPSET(TUBA))
-       {
-               PS(this).m_switchweapon = WEP_TUBA;
-               return;
-       }
-
-       // TODO: clean this up by moving it to weapon code
-       if(this.enemy==NULL)
-       {
-               // If no weapon was chosen get the first available weapon
-               if(PS(this).m_weapon==WEP_Null)
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       if(client_hasweapon(this, it, true, false))
-                       {
-                               PS(this).m_switchweapon = it;
-                               return;
-                       }
-               ));
-               return;
-       }
-
-       // Do not change weapon during the next second after a combo
-       float f = time - this.lastcombotime;
-       if(f < 1)
-               return;
-
-       float w;
-       float distance; distance=bound(10,vlen(this.origin-this.enemy.origin)-200,10000);
-
-       // Should it do a weapon combo?
-       float af, ct, combo_time, combo;
-
-       af = ATTACK_FINISHED(this, 0);
-       ct = autocvar_bot_ai_weapon_combo_threshold;
-
-       // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos
-       // Ideally this 4 should be calculated as longest_weapon_refire / bot_ai_weapon_combo_threshold
-       combo_time = time + ct + (ct * ((-0.3*(skill+this.bot_weaponskill))+3));
-
-       combo = false;
-
-       if(autocvar_bot_ai_weapon_combo)
-       if(PS(this).m_weapon.m_id == this.lastfiredweapon)
-       if(af > combo_time)
-       {
-               combo = true;
-               this.lastcombotime = time;
-       }
-
-       distance *= pow(2, this.bot_rangepreference);
-
-       // Custom weapon list based on distance to the enemy
-       if(bot_custom_weapon){
-
-               // Choose weapons for far distance
-               if ( distance > bot_distance_far ) {
-                       for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){
-                               w = bot_weapons_far[i];
-                               if ( client_hasweapon(this, Weapons_from(w), true, false) )
-                               {
-                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
-                                               continue;
-                                       PS(this).m_switchweapon = Weapons_from(w);
-                                       return;
-                               }
-                       }
-               }
-
-               // Choose weapons for mid distance
-               if ( distance > bot_distance_close) {
-                       for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){
-                               w = bot_weapons_mid[i];
-                               if ( client_hasweapon(this, Weapons_from(w), true, false) )
-                               {
-                                       if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
-                                               continue;
-                                       PS(this).m_switchweapon = Weapons_from(w);
-                                       return;
-                               }
-                       }
-               }
-
-               // Choose weapons for close distance
-               for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){
-                       w = bot_weapons_close[i];
-                       if ( client_hasweapon(this, Weapons_from(w), true, false) )
-                       {
-                               if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
-                                       continue;
-                               PS(this).m_switchweapon = Weapons_from(w);
-                               return;
-                       }
-               }
-       }
-}
-
-void havocbot_aim(entity this)
-{
-       vector myvel, enemyvel;
-//     if(this.flags & FL_INWATER)
-//             return;
-       if (time < this.nextaim)
-               return;
-       this.nextaim = time + 0.1;
-       myvel = this.velocity;
-       if (!this.waterlevel)
-               myvel.z = 0;
-       if (this.enemy)
-       {
-               enemyvel = this.enemy.velocity;
-               if (!this.enemy.waterlevel)
-                       enemyvel.z = 0;
-               lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel);
-       }
-       else
-               lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0');
-}
-
-bool havocbot_moveto_refresh_route(entity this)
-{
-       // Refresh path to goal if necessary
-       entity wp;
-       wp = this.havocbot_personal_waypoint;
-       navigation_goalrating_start(this);
-       navigation_routerating(this, wp, 10000, 10000);
-       navigation_goalrating_end(this);
-       return this.navigation_hasgoals;
-}
-
-float havocbot_moveto(entity this, vector pos)
-{
-       entity wp;
-
-       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-       {
-               // Step 4: Move to waypoint
-               if(this.havocbot_personal_waypoint==NULL)
-               {
-                       LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint\n");
-                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                       return CMD_STATUS_ERROR;
-               }
-
-               if (!bot_strategytoken_taken)
-               if(this.havocbot_personal_waypoint_searchtime<time)
-               {
-                       bot_strategytoken_taken = true;
-                       if(havocbot_moveto_refresh_route(this))
-                       {
-                               LOG_TRACE(this.netname, " walking to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
-                               this.havocbot_personal_waypoint_searchtime = time + 10;
-                               this.havocbot_personal_waypoint_failcounter = 0;
-                       }
-                       else
-                       {
-                               this.havocbot_personal_waypoint_failcounter += 1;
-                               this.havocbot_personal_waypoint_searchtime = time + 2;
-                               if(this.havocbot_personal_waypoint_failcounter >= 30)
-                               {
-                                       LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin),"\n");
-                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-                                       remove(this.havocbot_personal_waypoint);
-                                       return CMD_STATUS_ERROR;
-                               }
-                               else
-                                       LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
-                       }
-               }
-
-               if(autocvar_bot_debug_goalstack)
-                       debuggoalstack(this);
-
-               // Heading
-               vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs);
-               dir.z = 0;
-               bot_aimdir(this, dir, -1);
-
-               // Go!
-               havocbot_movetogoal(this);
-
-               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
-               {
-                       // Step 5: Waypoint reached
-                       LOG_TRACE(this.netname, "'s personal waypoint reached\n");
-                       remove(this.havocbot_personal_waypoint);
-                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                       return CMD_STATUS_FINISHED;
-               }
-
-               return CMD_STATUS_EXECUTING;
-       }
-
-       // Step 2: Linking waypoint
-       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING)
-       {
-               // Wait until it is linked
-               if(!this.havocbot_personal_waypoint.wplinked)
-               {
-                       LOG_TRACE(this.netname, " waiting for personal waypoint to be linked\n");
-                       return CMD_STATUS_EXECUTING;
-               }
-
-               this.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame
-               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
-
-               // Step 3: Route to waypoint
-               LOG_TRACE(this.netname, " walking to its personal waypoint\n");
-
-               return CMD_STATUS_EXECUTING;
-       }
-
-       // Step 1: Spawning waypoint
-       wp = waypoint_spawnpersonal(this, pos);
-       if(wp==NULL)
-       {
-               LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
-               return CMD_STATUS_ERROR;
-       }
-
-       this.havocbot_personal_waypoint = wp;
-       this.havocbot_personal_waypoint_failcounter = 0;
-       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
-
-       // if pos is inside a teleport, then let's mark it as teleport waypoint
-       entity head;
-       for(head = NULL; (head = find(head, classname, "trigger_teleport")); )
-       {
-               if(WarpZoneLib_BoxTouchesBrush(pos, pos, head, NULL))
-               {
-                       wp.wpflags |= WAYPOINTFLAG_TELEPORT;
-                       this.lastteleporttime = 0;
-               }
-       }
-
-/*
-       if(wp.wpflags & WAYPOINTFLAG_TELEPORT)
-               print("routing to a teleporter\n");
-       else
-               print("routing to a non-teleporter\n");
-*/
-
-       return CMD_STATUS_EXECUTING;
-}
-
-float havocbot_resetgoal(entity this)
-{
-       navigation_clearroute(this);
-       return CMD_STATUS_FINISHED;
-}
-
-void havocbot_setupbot(entity this)
-{
-       this.bot_ai = havocbot_ai;
-       this.cmd_moveto = havocbot_moveto;
-       this.cmd_resetgoal = havocbot_resetgoal;
-
-       havocbot_chooserole(this);
-}
-
-vector havocbot_dodge(entity this)
-{
-       // LordHavoc: disabled because this is too expensive
-       return '0 0 0';
-#if 0
-       entity head;
-       vector dodge, v, n;
-       float danger, bestdanger, vl, d;
-       dodge = '0 0 0';
-       bestdanger = -20;
-       // check for dangerous objects near bot or approaching bot
-       head = findchainfloat(bot_dodge, true);
-       while(head)
-       {
-               if (head.owner != this)
-               {
-                       vl = vlen(head.velocity);
-                       if (vl > autocvar_sv_maxspeed * 0.3)
-                       {
-                               n = normalize(head.velocity);
-                               v = this.origin - head.origin;
-                               d = v * n;
-                               if (d > (0 - head.bot_dodgerating))
-                               if (d < (vl * 0.2 + head.bot_dodgerating))
-                               {
-                                       // calculate direction and distance from the flight path, by removing the forward axis
-                                       v = v - (n * (v * n));
-                                       danger = head.bot_dodgerating - vlen(v);
-                                       if (bestdanger < danger)
-                                       {
-                                               bestdanger = danger;
-                                               // dodge to the side of the object
-                                               dodge = normalize(v);
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               danger = head.bot_dodgerating - vlen(head.origin - this.origin);
-                               if (bestdanger < danger)
-                               {
-                                       bestdanger = danger;
-                                       dodge = normalize(this.origin - head.origin);
-                               }
-                       }
-               }
-               head = head.chain;
-       }
-       return dodge;
-#endif
-}
diff --git a/qcsrc/server/bot/havocbot/havocbot.qh b/qcsrc/server/bot/havocbot/havocbot.qh
deleted file mode 100644 (file)
index 4a391b6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-/*
- * Globals and Fields
- */
-
-.float havocbot_keyboardskill;
-.float facingwalltime, ignoregoaltime;
-.float lastfiredweapon;
-.float lastcombotime;
-.float havocbot_blockhead;
-
-.float havocbot_keyboardtime;
-.float havocbot_ducktime;
-.float bot_timelastseengoal;
-.float bot_canruntogoal;
-.float bot_chooseweapontime;
-.float rocketjumptime;
-.float nextaim;
-.float havocbot_personal_waypoint_searchtime;
-.float havocbot_personal_waypoint_failcounter;
-.float havocbot_chooseenemy_finished;
-.float havocbot_stickenemy;
-.float havocbot_role_timeout;
-
-.entity ignoregoal;
-.entity bot_lastseengoal;
-.entity havocbot_personal_waypoint;
-
-.vector havocbot_keyboard;
-
-/*
- * Functions
- */
-
-void havocbot_ai(entity this);
-void havocbot_aim(entity this);
-void havocbot_setupbot(entity this);
-void havocbot_movetogoal(entity this);
-void havocbot_chooserole(entity this);
-void havocbot_chooseenemy(entity this);
-void havocbot_chooseweapon(entity this);
-void havocbot_bunnyhop(entity this, vector dir);
-void havocbot_keyboard_movement(entity this, vector destorg);
-
-float havocbot_resetgoal(entity this);
-float havocbot_moveto(entity this, vector pos);
-float havocbot_moveto_refresh_route(entity this);
-
-vector havocbot_dodge(entity this);
-
-.void(entity this) havocbot_role;
-.void(entity this) havocbot_previous_role;
-
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-/*
- * Imports
- */
-
-.entity draggedby;
-.float ladder_time;
-.entity ladder_entity;
diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc
deleted file mode 100644 (file)
index 034e29f..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "roles.qh"
-
-#include "havocbot.qh"
-
-#include "../bot.qh"
-#include "../navigation.qh"
-
-.float max_armorvalue;
-.float havocbot_role_timeout;
-
-.void(entity this) havocbot_previous_role;
-.void(entity this) havocbot_role;
-
-void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
-{
-       entity head;
-       float rating, d, discard, distance, friend_distance, enemy_distance;
-       vector o;
-       ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
-       head = findchainfloat(bot_pickup, true);
-
-       while (head)
-       {
-               o = (head.absmin + head.absmax) * 0.5;
-               distance = vlen(o - org);
-               friend_distance = 10000; enemy_distance = 10000;
-               rating = 0;
-
-               if(!head.solid || distance > sradius || (head == this.ignoregoal && time < this.ignoregoaltime) )
-               {
-                       head = head.chain;
-                       continue;
-               }
-
-               // Check if the item can be picked up safely
-               if(head.classname == "droppedweapon")
-               {
-                       traceline(o, o + '0 0 -1500', true, NULL);
-
-                       d = pointcontents(trace_endpos + '0 0 1');
-                       if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
-                       {
-                               head = head.chain;
-                               continue;
-                       }
-                       if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
-                       {
-                               head = head.chain;
-                               continue;
-                       }
-               }
-               else
-               {
-                       // Ignore items under water
-                       traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
-                       if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
-                       {
-                               head = head.chain;
-                               continue;
-                       }
-               }
-
-               if(teamplay)
-               {
-                       discard = false;
-
-                       FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it), LAMBDA(
-                               d = vlen(it.origin - o); // distance between player and item
-
-                               if ( it.team == this.team )
-                               {
-                                       if ( !IS_REAL_CLIENT(it) || discard )
-                                               continue;
-
-                                       if( d > friend_distance)
-                                               continue;
-
-                                       friend_distance = d;
-
-                                       discard = true;
-
-                                       if( head.health && it.health > this.health )
-                                               continue;
-
-                                       if( head.armorvalue && it.armorvalue > this.armorvalue)
-                                               continue;
-
-                                       if( head.weapons )
-                                       if( head.weapons & ~it.weapons )
-                                               continue;
-
-                                       if (head.ammo_shells && it.ammo_shells > this.ammo_shells)
-                                               continue;
-
-                                       if (head.ammo_nails && it.ammo_nails > this.ammo_nails)
-                                               continue;
-
-                                       if (head.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
-                                               continue;
-
-                                       if (head.ammo_cells && it.ammo_cells > this.ammo_cells)
-                                               continue;
-
-                                       if (head.ammo_plasma && it.ammo_plasma > this.ammo_plasma)
-                                               continue;
-
-                                       discard = false;
-                               }
-                               else
-                               {
-                                       // If enemy only track distances
-                                       // TODO: track only if visible ?
-                                       if( d < enemy_distance )
-                                               enemy_distance = d;
-                               }
-                       ));
-
-                       // Rate the item only if no one needs it, or if an enemy is closer to it
-                       if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
-                               (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
-                               rating = head.bot_pickupevalfunc(this, head);
-
-               }
-               else
-                       rating = head.bot_pickupevalfunc(this, head);
-
-               if(rating > 0)
-                       navigation_routerating(this, head, rating * ratingscale, 2000);
-               head = head.chain;
-       }
-}
-
-void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius)
-{
-       entity head;
-       head = findchain(classname, "dom_controlpoint");
-       while (head)
-       {
-               if(vdist((((head.absmin + head.absmax) * 0.5) - org), <, sradius))
-               {
-                       if(head.cnt > -1) // this is just being fought for
-                               navigation_routerating(this, head, ratingscale, 5000);
-                       else if(head.goalentity.cnt == 0) // unclaimed point
-                               navigation_routerating(this, head, ratingscale * 0.5, 5000);
-                       else if(head.goalentity.team != this.team) // other team's point
-                               navigation_routerating(this, head, ratingscale * 0.2, 5000);
-               }
-               head = head.chain;
-       }
-}
-
-void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius)
-{
-       if (autocvar_bot_nofire)
-               return;
-
-       // don't chase players if we're under water
-       if(this.waterlevel>WATERLEVEL_WETFEET)
-               return;
-
-       int t;
-
-       FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), LAMBDA(
-               // TODO: Merge this logic with the bot_shouldattack function
-               if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius))
-                       continue;
-
-               // rate only visible enemies
-               /*
-               traceline(this.origin + this.view_ofs, it.origin, MOVE_NOMONSTERS, this);
-               if (trace_fraction < 1 || trace_ent != it)
-                       continue;
-               */
-
-               if((it.flags & FL_INWATER) || (it.flags & FL_PARTIALGROUND))
-                       continue;
-
-               // not falling
-               if((IS_ONGROUND(it)) == 0)
-               {
-                       traceline(it.origin, it.origin + '0 0 -1500', true, NULL);
-                       t = pointcontents(trace_endpos + '0 0 1');
-                       if(t != CONTENT_SOLID )
-                       if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
-                               continue;
-                       if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
-                               continue;
-               }
-
-               // TODO: rate waypoints near the targetted player at that moment, instead of the player itthis
-               //               adding a player as a goal seems to be quite dangerous, especially on space maps
-               //               remove hack in navigation_poptouchedgoals() after performing this change
-
-               t = (this.health + this.armorvalue ) / (it.health + it.armorvalue );
-               navigation_routerating(this, it, t * ratingscale, 2000);
-       ));
-}
-
-// legacy bot role for standard gamemodes
-// go to best items
-void havocbot_role_generic(entity this)
-{
-       if(IS_DEAD(this))
-               return;
-
-       if (this.bot_strategytime < time)
-       {
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start(this);
-               havocbot_goalrating_items(this, 10000, this.origin, 10000);
-               havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000);
-               //havocbot_goalrating_waypoints(1, this.origin, 1000);
-               navigation_goalrating_end(this);
-       }
-}
-
-void havocbot_chooserole_generic(entity this)
-{
-       this.havocbot_role = havocbot_role_generic;
-}
-
-void havocbot_chooserole(entity this)
-{
-       LOG_TRACE("choosing a role...\n");
-       this.bot_strategytime = 0;
-       if(!MUTATOR_CALLHOOK(HavocBot_ChooseRole, this))
-               havocbot_chooserole_generic(this);
-}
diff --git a/qcsrc/server/bot/havocbot/roles.qh b/qcsrc/server/bot/havocbot/roles.qh
deleted file mode 100644 (file)
index 5b1f2b5..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#pragma once
-void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius);
diff --git a/qcsrc/server/bot/havocbot/scripting.qh b/qcsrc/server/bot/havocbot/scripting.qh
deleted file mode 100644 (file)
index 07cb4d6..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-void bot_clearqueue(entity bot);
diff --git a/qcsrc/server/bot/navigation.qc b/qcsrc/server/bot/navigation.qc
deleted file mode 100644 (file)
index 8deefdb..0000000
+++ /dev/null
@@ -1,1260 +0,0 @@
-#include "navigation.qh"
-
-#include "bot.qh"
-#include "waypoints.qh"
-
-#include <common/t_items.qh>
-
-#include <common/items/all.qh>
-
-#include <common/constants.qh>
-#include <common/triggers/trigger/jumppads.qh>
-
-.float speed;
-
-// rough simulation of walking from one point to another to test if a path
-// can be traveled, used for waypoint linking and havocbot
-
-bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode)
-{
-       vector org;
-       vector move;
-       vector dir;
-       float dist;
-       float totaldist;
-       float stepdist;
-       float yaw;
-       float ignorehazards;
-       float swimming;
-
-       if(autocvar_bot_debug_tracewalk)
-       {
-               debugresetnodes();
-               debugnode(e, start);
-       }
-
-       move = end - start;
-       move.z = 0;
-       org = start;
-       dist = totaldist = vlen(move);
-       dir = normalize(move);
-       stepdist = 32;
-       ignorehazards = false;
-       swimming = false;
-
-       // Analyze starting point
-       traceline(start, start, MOVE_NORMAL, e);
-       if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
-               ignorehazards = true;
-       else
-       {
-               traceline( start, start + '0 0 -65536', MOVE_NORMAL, e);
-               if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
-               {
-                       ignorehazards = true;
-                       swimming = true;
-               }
-       }
-       tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e);
-       if (trace_startsolid)
-       {
-               // Bad start
-               if(autocvar_bot_debug_tracewalk)
-                       debugnodestatus(start, DEBUG_NODE_FAIL);
-
-               //print("tracewalk: ", vtos(start), " is a bad start\n");
-               return false;
-       }
-
-       // Movement loop
-       yaw = vectoyaw(move);
-       move = end - org;
-       for (;;)
-       {
-               if (boxesoverlap(end, end, org + m1 + '-1 -1 -1', org + m2 + '1 1 1'))
-               {
-                       // Succeeded
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnodestatus(org, DEBUG_NODE_SUCCESS);
-
-                       //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
-                       return true;
-               }
-               if(autocvar_bot_debug_tracewalk)
-                       debugnode(e, org);
-
-               if (dist <= 0)
-                       break;
-               if (stepdist > dist)
-                       stepdist = dist;
-               dist = dist - stepdist;
-               traceline(org, org, MOVE_NORMAL, e);
-               if (!ignorehazards)
-               {
-                       if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
-                       {
-                               // hazards blocking path
-                               if(autocvar_bot_debug_tracewalk)
-                                       debugnodestatus(org, DEBUG_NODE_FAIL);
-
-                               //print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n");
-                               return false;
-                       }
-               }
-               if (trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
-               {
-                       move = normalize(end - org);
-                       tracebox(org, m1, m2, org + move * stepdist, movemode, e);
-
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnode(e, trace_endpos);
-
-                       if (trace_fraction < 1)
-                       {
-                               swimming = true;
-                               org = trace_endpos - normalize(org - trace_endpos) * stepdist;
-                               for (; org.z < end.z + e.maxs.z; org.z += stepdist)
-                               {
-                                               if(autocvar_bot_debug_tracewalk)
-                                                       debugnode(e, org);
-
-                                       if(pointcontents(org) == CONTENT_EMPTY)
-                                                       break;
-                               }
-
-                               if(pointcontents(org + '0 0 1') != CONTENT_EMPTY)
-                               {
-                                       if(autocvar_bot_debug_tracewalk)
-                                               debugnodestatus(org, DEBUG_NODE_FAIL);
-
-                                       return false;
-                                       //print("tracewalk: ", vtos(start), " failed under water\n");
-                               }
-                               continue;
-
-                       }
-                       else
-                               org = trace_endpos;
-               }
-               else
-               {
-                       move = dir * stepdist + org;
-                       tracebox(org, m1, m2, move, movemode, e);
-
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnode(e, trace_endpos);
-
-                       // hit something
-                       if (trace_fraction < 1)
-                       {
-                               // check if we can walk over this obstacle, possibly by jumpstepping
-                               tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
-                               if (trace_fraction < 1 || trace_startsolid)
-                               {
-                                       tracebox(org + jumpstepheightvec, m1, m2, move + jumpstepheightvec, movemode, e);
-                                       if (trace_fraction < 1 || trace_startsolid)
-                                       {
-                                               if(autocvar_bot_debug_tracewalk)
-                                                       debugnodestatus(trace_endpos, DEBUG_NODE_WARNING);
-
-                                               // check for doors
-                                               traceline( org, move, movemode, e);
-                                               if ( trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
-                                               {
-                                                       vector nextmove;
-                                                       move = trace_endpos;
-                                                       while(trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
-                                                       {
-                                                               nextmove = move + (dir * stepdist);
-                                                               traceline( move, nextmove, movemode, e);
-                                                               move = nextmove;
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       if(autocvar_bot_debug_tracewalk)
-                                                               debugnodestatus(trace_endpos, DEBUG_NODE_FAIL);
-
-                                                       //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
-                                                       //te_explosion(trace_endpos);
-                                                       //print(ftos(e.dphitcontentsmask), "\n");
-                                                       return false; // failed
-                                               }
-                                       }
-                                       else
-                                               move = trace_endpos;
-                               }
-                               else
-                                       move = trace_endpos;
-                       }
-                       else
-                               move = trace_endpos;
-
-                       // trace down from stepheight as far as possible and move there,
-                       // if this starts in solid we try again without the stepup, and
-                       // if that also fails we assume it is a wall
-                       // (this is the same logic as the Quake walkmove function used)
-                       tracebox(move, m1, m2, move + '0 0 -65536', movemode, e);
-
-                       // moved successfully
-                       if(swimming)
-                       {
-                               float c;
-                               c = pointcontents(org + '0 0 1');
-                               if (!(c == CONTENT_WATER || c == CONTENT_LAVA || c == CONTENT_SLIME))
-                                       swimming = false;
-                               else
-                                       continue;
-                       }
-
-                       org = trace_endpos;
-               }
-       }
-
-       //print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n");
-
-       // moved but didn't arrive at the intended destination
-       if(autocvar_bot_debug_tracewalk)
-               debugnodestatus(org, DEBUG_NODE_FAIL);
-
-       return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// goal stack
-/////////////////////////////////////////////////////////////////////////////
-
-// completely empty the goal stack, used when deciding where to go
-void navigation_clearroute(entity this)
-{
-       //print("bot ", etos(this), " clear\n");
-       this.navigation_hasgoals = false;
-       this.goalcurrent = NULL;
-       this.goalstack01 = NULL;
-       this.goalstack02 = NULL;
-       this.goalstack03 = NULL;
-       this.goalstack04 = NULL;
-       this.goalstack05 = NULL;
-       this.goalstack06 = NULL;
-       this.goalstack07 = NULL;
-       this.goalstack08 = NULL;
-       this.goalstack09 = NULL;
-       this.goalstack10 = NULL;
-       this.goalstack11 = NULL;
-       this.goalstack12 = NULL;
-       this.goalstack13 = NULL;
-       this.goalstack14 = NULL;
-       this.goalstack15 = NULL;
-       this.goalstack16 = NULL;
-       this.goalstack17 = NULL;
-       this.goalstack18 = NULL;
-       this.goalstack19 = NULL;
-       this.goalstack20 = NULL;
-       this.goalstack21 = NULL;
-       this.goalstack22 = NULL;
-       this.goalstack23 = NULL;
-       this.goalstack24 = NULL;
-       this.goalstack25 = NULL;
-       this.goalstack26 = NULL;
-       this.goalstack27 = NULL;
-       this.goalstack28 = NULL;
-       this.goalstack29 = NULL;
-       this.goalstack30 = NULL;
-       this.goalstack31 = NULL;
-}
-
-// add a new goal at the beginning of the stack
-// (in other words: add a new prerequisite before going to the later goals)
-// NOTE: when a waypoint is added, the WP gets pushed first, then the
-// next-closest WP on the shortest path to the WP
-// That means, if the stack overflows, the bot will know how to do the FIRST 32
-// steps to the goal, and then recalculate the path.
-void navigation_pushroute(entity this, entity e)
-{
-       //print("bot ", etos(this), " push ", etos(e), "\n");
-       this.goalstack31 = this.goalstack30;
-       this.goalstack30 = this.goalstack29;
-       this.goalstack29 = this.goalstack28;
-       this.goalstack28 = this.goalstack27;
-       this.goalstack27 = this.goalstack26;
-       this.goalstack26 = this.goalstack25;
-       this.goalstack25 = this.goalstack24;
-       this.goalstack24 = this.goalstack23;
-       this.goalstack23 = this.goalstack22;
-       this.goalstack22 = this.goalstack21;
-       this.goalstack21 = this.goalstack20;
-       this.goalstack20 = this.goalstack19;
-       this.goalstack19 = this.goalstack18;
-       this.goalstack18 = this.goalstack17;
-       this.goalstack17 = this.goalstack16;
-       this.goalstack16 = this.goalstack15;
-       this.goalstack15 = this.goalstack14;
-       this.goalstack14 = this.goalstack13;
-       this.goalstack13 = this.goalstack12;
-       this.goalstack12 = this.goalstack11;
-       this.goalstack11 = this.goalstack10;
-       this.goalstack10 = this.goalstack09;
-       this.goalstack09 = this.goalstack08;
-       this.goalstack08 = this.goalstack07;
-       this.goalstack07 = this.goalstack06;
-       this.goalstack06 = this.goalstack05;
-       this.goalstack05 = this.goalstack04;
-       this.goalstack04 = this.goalstack03;
-       this.goalstack03 = this.goalstack02;
-       this.goalstack02 = this.goalstack01;
-       this.goalstack01 = this.goalcurrent;
-       this.goalcurrent = e;
-}
-
-// remove first goal from stack
-// (in other words: remove a prerequisite for reaching the later goals)
-// (used when a spawnfunc_waypoint is reached)
-void navigation_poproute(entity this)
-{
-       //print("bot ", etos(this), " pop\n");
-       this.goalcurrent = this.goalstack01;
-       this.goalstack01 = this.goalstack02;
-       this.goalstack02 = this.goalstack03;
-       this.goalstack03 = this.goalstack04;
-       this.goalstack04 = this.goalstack05;
-       this.goalstack05 = this.goalstack06;
-       this.goalstack06 = this.goalstack07;
-       this.goalstack07 = this.goalstack08;
-       this.goalstack08 = this.goalstack09;
-       this.goalstack09 = this.goalstack10;
-       this.goalstack10 = this.goalstack11;
-       this.goalstack11 = this.goalstack12;
-       this.goalstack12 = this.goalstack13;
-       this.goalstack13 = this.goalstack14;
-       this.goalstack14 = this.goalstack15;
-       this.goalstack15 = this.goalstack16;
-       this.goalstack16 = this.goalstack17;
-       this.goalstack17 = this.goalstack18;
-       this.goalstack18 = this.goalstack19;
-       this.goalstack19 = this.goalstack20;
-       this.goalstack20 = this.goalstack21;
-       this.goalstack21 = this.goalstack22;
-       this.goalstack22 = this.goalstack23;
-       this.goalstack23 = this.goalstack24;
-       this.goalstack24 = this.goalstack25;
-       this.goalstack25 = this.goalstack26;
-       this.goalstack26 = this.goalstack27;
-       this.goalstack27 = this.goalstack28;
-       this.goalstack28 = this.goalstack29;
-       this.goalstack29 = this.goalstack30;
-       this.goalstack30 = this.goalstack31;
-       this.goalstack31 = NULL;
-}
-
-float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist)
-{
-       float dist;
-       dist = vlen(v - org);
-       if (bestdist > dist)
-       {
-               traceline(v, org, true, ent);
-               if (trace_fraction == 1)
-               {
-                       if (walkfromwp)
-                       {
-                               if (tracewalk(ent, v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), org, bot_navigation_movemode))
-                                       return true;
-                       }
-                       else
-                       {
-                               if (tracewalk(ent, org, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, bot_navigation_movemode))
-                                       return true;
-                       }
-               }
-       }
-       return false;
-}
-
-// find the spawnfunc_waypoint near a dynamic goal such as a dropped weapon
-entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except)
-{
-       entity waylist, w, best;
-       vector v, org, pm1, pm2;
-
-       pm1 = ent.origin + ent.mins;
-       pm2 = ent.origin + ent.maxs;
-       waylist = findchain(classname, "waypoint");
-
-       // do two scans, because box test is cheaper
-       w = waylist;
-       while (w)
-       {
-               // if object is touching spawnfunc_waypoint
-               if(w != ent && w != except)
-                       if (boxesoverlap(pm1, pm2, w.absmin, w.absmax))
-                               return w;
-               w = w.chain;
-       }
-
-       org = ent.origin + 0.5 * (ent.mins + ent.maxs);
-       org.z = ent.origin.z + ent.mins.z - STAT(PL_MIN, NULL).z; // player height
-       // TODO possibly make other code have the same support for bboxes
-       if(ent.tag_entity)
-               org = org + ent.tag_entity.origin;
-       if (navigation_testtracewalk)
-               te_plasmaburn(org);
-
-       best = NULL;
-
-       // box check failed, try walk
-       w = waylist;
-       while (w)
-       {
-               // if object can walk from spawnfunc_waypoint
-               if(w != ent)
-               {
-                       if (w.wpisbox)
-                       {
-                               vector wm1, wm2;
-                               wm1 = w.origin + w.mins;
-                               wm2 = w.origin + w.maxs;
-                               v.x = bound(wm1_x, org.x, wm2_x);
-                               v.y = bound(wm1_y, org.y, wm2_y);
-                               v.z = bound(wm1_z, org.z, wm2_z);
-                       }
-                       else
-                               v = w.origin;
-                       if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist))
-                       {
-                               bestdist = vlen(v - org);
-                               best = w;
-                       }
-               }
-               w = w.chain;
-       }
-       return best;
-}
-entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
-{
-       entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL);
-       if (autocvar_g_waypointeditor_auto)
-       {
-               entity wp2 = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, wp);
-               if (wp && !wp2)
-                       wp.wpflags |= WAYPOINTFLAG_PROTECTED;
-       }
-       return wp;
-}
-
-// finds the waypoints near the bot initiating a navigation query
-float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist)
-{
-       entity head;
-       vector v, m1, m2, diff;
-       float c;
-//     navigation_testtracewalk = true;
-       c = 0;
-       head = waylist;
-       while (head)
-       {
-               if (!head.wpconsidered)
-               {
-                       if (head.wpisbox)
-                       {
-                               m1 = head.origin + head.mins;
-                               m2 = head.origin + head.maxs;
-                               v = this.origin;
-                               v.x = bound(m1_x, v.x, m2_x);
-                               v.y = bound(m1_y, v.y, m2_y);
-                               v.z = bound(m1_z, v.z, m2_z);
-                       }
-                       else
-                               v = head.origin;
-                       diff = v - this.origin;
-                       diff.z = max(0, diff.z);
-                       if(vdist(diff, <, maxdist))
-                       {
-                               head.wpconsidered = true;
-                               if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode))
-                               {
-                                       head.wpnearestpoint = v;
-                                       head.wpcost = vlen(v - this.origin) + head.dmg;
-                                       head.wpfire = 1;
-                                       head.enemy = NULL;
-                                       c = c + 1;
-                               }
-                       }
-               }
-               head = head.chain;
-       }
-       //navigation_testtracewalk = false;
-       return c;
-}
-
-// updates a path link if a spawnfunc_waypoint link is better than the current one
-void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p)
-{
-       vector m1;
-       vector m2;
-       vector v;
-       if (wp.wpisbox)
-       {
-               m1 = wp.absmin;
-               m2 = wp.absmax;
-               v.x = bound(m1_x, p.x, m2_x);
-               v.y = bound(m1_y, p.y, m2_y);
-               v.z = bound(m1_z, p.z, m2_z);
-       }
-       else
-               v = wp.origin;
-       cost2 = cost2 + vlen(v - p);
-       if (wp.wpcost > cost2)
-       {
-               wp.wpcost = cost2;
-               wp.enemy = w;
-               wp.wpfire = 1;
-               wp.wpnearestpoint = v;
-       }
-}
-
-// queries the entire spawnfunc_waypoint network for pathes leading away from the bot
-void navigation_markroutes(entity this, entity fixed_source_waypoint)
-{
-       entity w, wp, waylist;
-       float searching, cost, cost2;
-       vector p;
-       w = waylist = findchain(classname, "waypoint");
-       while (w)
-       {
-               w.wpconsidered = false;
-               w.wpnearestpoint = '0 0 0';
-               w.wpcost = 10000000;
-               w.wpfire = 0;
-               w.enemy = NULL;
-               w = w.chain;
-       }
-
-       if(fixed_source_waypoint)
-       {
-               fixed_source_waypoint.wpconsidered = true;
-               fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
-               fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg;
-               fixed_source_waypoint.wpfire = 1;
-               fixed_source_waypoint.enemy = NULL;
-       }
-       else
-       {
-               // try a short range search for the nearest waypoints, and expand the search repeatedly if none are found
-               // as this search is expensive we will use lower values if the bot is on the air
-               float i, increment, maxdistance;
-               if(IS_ONGROUND(this))
-               {
-                       increment = 750;
-                       maxdistance = 50000;
-               }
-               else
-               {
-                       increment = 500;
-                       maxdistance = 1500;
-               }
-
-               for(i=increment;!navigation_markroutes_nearestwaypoints(this, waylist, i)&&i<maxdistance;i+=increment);
-       }
-
-       searching = true;
-       while (searching)
-       {
-               searching = false;
-               w = waylist;
-               while (w)
-               {
-                       if (w.wpfire)
-                       {
-                               searching = true;
-                               w.wpfire = 0;
-                               cost = w.wpcost;
-                               p = w.wpnearestpoint;
-                               wp = w.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp00mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp01mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp02mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp03mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp04mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp05mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp06mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp07mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp08mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp09mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp10mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp11mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp12mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp13mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp14mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp15mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp16mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp17mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp18mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp19mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp20mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp21mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp22mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp23mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp24mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp25mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp26mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp27mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp28mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp29mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp30mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               wp = w.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp31mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
-                       }
-                       w = w.chain;
-               }
-       }
-}
-
-// queries the entire spawnfunc_waypoint network for pathes leading to the bot
-void navigation_markroutes_inverted(entity fixed_source_waypoint)
-{
-       entity w, wp, waylist;
-       float searching, cost, cost2;
-       vector p;
-       w = waylist = findchain(classname, "waypoint");
-       while (w)
-       {
-               w.wpconsidered = false;
-               w.wpnearestpoint = '0 0 0';
-               w.wpcost = 10000000;
-               w.wpfire = 0;
-               w.enemy = NULL;
-               w = w.chain;
-       }
-
-       if(fixed_source_waypoint)
-       {
-               fixed_source_waypoint.wpconsidered = true;
-               fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
-               fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; // the cost to get from X to fixed_source_waypoint
-               fixed_source_waypoint.wpfire = 1;
-               fixed_source_waypoint.enemy = NULL;
-       }
-       else
-       {
-               error("need to start with a waypoint\n");
-       }
-
-       searching = true;
-       while (searching)
-       {
-               searching = false;
-               w = waylist;
-               while (w)
-               {
-                       if (w.wpfire)
-                       {
-                               searching = true;
-                               w.wpfire = 0;
-                               cost = w.wpcost; // cost to walk from w to home
-                               p = w.wpnearestpoint;
-                               for(wp = waylist; wp; wp = wp.chain)
-                               {
-                                       if(w != wp.wp00) if(w != wp.wp01) if(w != wp.wp02) if(w != wp.wp03)
-                                       if(w != wp.wp04) if(w != wp.wp05) if(w != wp.wp06) if(w != wp.wp07)
-                                       if(w != wp.wp08) if(w != wp.wp09) if(w != wp.wp10) if(w != wp.wp11)
-                                       if(w != wp.wp12) if(w != wp.wp13) if(w != wp.wp14) if(w != wp.wp15)
-                                       if(w != wp.wp16) if(w != wp.wp17) if(w != wp.wp18) if(w != wp.wp19)
-                                       if(w != wp.wp20) if(w != wp.wp21) if(w != wp.wp22) if(w != wp.wp23)
-                                       if(w != wp.wp24) if(w != wp.wp25) if(w != wp.wp26) if(w != wp.wp27)
-                                       if(w != wp.wp28) if(w != wp.wp29) if(w != wp.wp30) if(w != wp.wp31)
-                                               continue;
-                                       cost2 = cost + wp.dmg;
-                                       navigation_markroutes_checkwaypoint(w, wp, cost2, p);
-                               }
-                       }
-                       w = w.chain;
-               }
-       }
-}
-
-// updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item
-void navigation_routerating(entity this, entity e, float f, float rangebias)
-{
-       entity nwp;
-       vector o;
-       if (!e)
-               return;
-
-       if(e.blacklisted)
-               return;
-
-       o = (e.absmin + e.absmax) * 0.5;
-
-       //print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n");
-
-       // Evaluate path using jetpack
-       if(g_jetpack)
-       if(this.items & IT_JETPACK)
-       if(autocvar_bot_ai_navigation_jetpack)
-       if(vdist(this.origin - o, >, autocvar_bot_ai_navigation_jetpack_mindistance))
-       {
-               vector pointa, pointb;
-
-               LOG_DEBUG("jetpack ai: evaluating path for ", e.classname, "\n");
-
-               // Point A
-               traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this);
-               pointa = trace_endpos - '0 0 1';
-
-               // Point B
-               traceline(o, o + '0 0 65535', MOVE_NORMAL, e);
-               pointb = trace_endpos - '0 0 1';
-
-               // Can I see these two points from the sky?
-               traceline(pointa, pointb, MOVE_NORMAL, this);
-
-               if(trace_fraction==1)
-               {
-                       LOG_DEBUG("jetpack ai: can bridge these two points\n");
-
-                       // Lower the altitude of these points as much as possible
-                       float zdistance, xydistance, cost, t, fuel;
-                       vector down, npa, npb;
-
-                       down = '0 0 -1' * (STAT(PL_MAX, NULL).z - STAT(PL_MIN, NULL).z) * 10;
-
-                       do{
-                               npa = pointa + down;
-                               npb = pointb + down;
-
-                               if(npa.z<=this.absmax.z)
-                                       break;
-
-                               if(npb.z<=e.absmax.z)
-                                       break;
-
-                               traceline(npa, npb, MOVE_NORMAL, this);
-                               if(trace_fraction==1)
-                               {
-                                       pointa = npa;
-                                       pointb = npb;
-                               }
-                       }
-                       while(trace_fraction == 1);
-
-
-                       // Rough estimation of fuel consumption
-                       // (ignores acceleration and current xyz velocity)
-                       xydistance = vlen(pointa - pointb);
-                       zdistance = fabs(pointa.z - this.origin.z);
-
-                       t = zdistance / autocvar_g_jetpack_maxspeed_up;
-                       t += xydistance / autocvar_g_jetpack_maxspeed_side;
-                       fuel = t * autocvar_g_jetpack_fuel * 0.8;
-
-                       LOG_DEBUG(strcat("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel), "\n"));
-
-                       // enough fuel ?
-                       if(this.ammo_fuel>fuel)
-                       {
-                               // Estimate cost
-                               // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship
-                               //  - between air and ground speeds)
-
-                               cost = xydistance / (autocvar_g_jetpack_maxspeed_side/autocvar_sv_maxspeed);
-                               cost += zdistance / (autocvar_g_jetpack_maxspeed_up/autocvar_sv_maxspeed);
-                               cost *= 1.5;
-
-                               // Compare against other goals
-                               f = f * rangebias / (rangebias + cost);
-
-                               if (navigation_bestrating < f)
-                               {
-                                       LOG_DEBUG(strcat("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"));
-                                       navigation_bestrating = f;
-                                       navigation_bestgoal = e;
-                                       this.navigation_jetpack_goal = e;
-                                       this.navigation_jetpack_point = pointb;
-                               }
-                               return;
-                       }
-               }
-       }
-
-       //te_wizspike(e.origin);
-       //bprint(etos(e));
-       //bprint("\n");
-       // update the cached spawnfunc_waypoint link on a dynamic item entity
-       if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL))
-       {
-               nwp = e;
-       }
-       else
-       {
-               float search;
-
-               search = true;
-
-               if(e.flags & FL_ITEM)
-               {
-                       if (!(e.flags & FL_WEAPON))
-                       if(e.nearestwaypoint)
-                               search = false;
-               }
-               else if (e.flags & FL_WEAPON)
-               {
-                       if(e.classname != "droppedweapon")
-                       if(e.nearestwaypoint)
-                               search = false;
-               }
-
-               if(search)
-               if (time > e.nearestwaypointtimeout)
-               {
-                       nwp = navigation_findnearestwaypoint(e, true);
-                       if(nwp)
-                               e.nearestwaypoint = nwp;
-                       else
-                       {
-                               LOG_DEBUG(strcat("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e), "\n"));
-
-                               if(e.flags & FL_ITEM)
-                                       e.blacklisted = true;
-                               else if (e.flags & FL_WEAPON)
-                               {
-                                       if(e.classname != "droppedweapon")
-                                               e.blacklisted = true;
-                               }
-
-                               if(e.blacklisted)
-                               {
-                                       LOG_DEBUG(strcat("The entity '", e.classname, "' is going to be excluded from path finding during this match\n"));
-                                       return;
-                               }
-                       }
-
-                       // TODO: Cleaner solution, probably handling this timeout from ctf.qc
-                       if(e.classname=="item_flag_team")
-                               e.nearestwaypointtimeout = time + 2;
-                       else
-                               e.nearestwaypointtimeout = time + random() * 3 + 5;
-               }
-               nwp = e.nearestwaypoint;
-       }
-
-       LOG_DEBUG(strcat("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")\n"));
-       if (nwp)
-       if (nwp.wpcost < 10000000)
-       {
-               //te_wizspike(nwp.wpnearestpoint);
-               LOG_DEBUG(strcat(e.classname, " ", ftos(f), "/(1+", ftos((nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint))), "/", ftos(rangebias), ") = "));
-               f = f * rangebias / (rangebias + (nwp.wpcost + vlen(o - nwp.wpnearestpoint)));
-               LOG_DEBUG(strcat("considering ", e.classname, " (with rating ", ftos(f), ")\n"));
-               if (navigation_bestrating < f)
-               {
-                       LOG_DEBUG(strcat("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")\n"));
-                       navigation_bestrating = f;
-                       navigation_bestgoal = e;
-               }
-       }
-}
-
-// adds an item to the the goal stack with the path to a given item
-bool navigation_routetogoal(entity this, entity e, vector startposition)
-{
-       this.goalentity = e;
-
-       // if there is no goal, just exit
-       if (!e)
-               return false;
-
-       this.navigation_hasgoals = true;
-
-       // put the entity on the goal stack
-       //print("routetogoal ", etos(e), "\n");
-       navigation_pushroute(this, e);
-
-       if(g_jetpack)
-       if(e==this.navigation_jetpack_goal)
-               return true;
-
-       // if it can reach the goal there is nothing more to do
-       if (tracewalk(this, startposition, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
-               return true;
-
-       // see if there are waypoints describing a path to the item
-       if(e.classname != "waypoint" || (e.wpflags & WAYPOINTFLAG_PERSONAL))
-               e = e.nearestwaypoint;
-       else
-               e = e.enemy; // we already have added it, so...
-
-       if(e == NULL)
-               return false;
-
-       for (;;)
-       {
-               // add the spawnfunc_waypoint to the path
-               navigation_pushroute(this, e);
-               e = e.enemy;
-
-               if(e==NULL)
-                       break;
-       }
-
-       return false;
-}
-
-// removes any currently touching waypoints from the goal stack
-// (this is how bots detect if they reached a goal)
-void navigation_poptouchedgoals(entity this)
-{
-       vector org, m1, m2;
-       org = this.origin;
-       m1 = org + this.mins;
-       m2 = org + this.maxs;
-
-       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
-       {
-               if(this.lastteleporttime>0)
-               if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
-               {
-                       if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-                       if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
-                       {
-                               this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                               this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                       }
-                       navigation_poproute(this);
-                       return;
-               }
-       }
-
-       // If for some reason the bot is closer to the next goal, pop the current one
-       if(this.goalstack01)
-       if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin))
-       if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
-       if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode))
-       {
-               LOG_DEBUG(strcat("path optimized for ", this.netname, ", removed a goal from the queue\n"));
-               navigation_poproute(this);
-               // TODO this may also be a nice idea to do "early" (e.g. by
-               // manipulating the vlen() comparisons) to shorten paths in
-               // general - this would make bots walk more "on rails" than
-               // "zigzagging" which they currently do with sufficiently
-               // random-like waypoints, and thus can make a nice bot
-               // personality property
-       }
-
-       // HACK: remove players/bots as goals, they can lead a bot to unexpected places (cliffs, lava, etc)
-       // TODO: rate waypoints near the targetted player at that moment, instead of the player itthis
-       if(IS_PLAYER(this.goalcurrent))
-               navigation_poproute(this);
-
-       // aid for detecting jump pads better (distance based check fails sometimes)
-       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && this.jumppadcount > 0 )
-               navigation_poproute(this);
-
-       // Loose goal touching check when running
-       if(this.aistatus & AI_STATUS_RUNNING)
-       if(this.speed >= autocvar_sv_maxspeed) // if -really- running
-       if(this.goalcurrent.classname=="waypoint")
-       {
-               if(vdist(this.origin - this.goalcurrent.origin, <, 150))
-               {
-                       traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL);
-                       if(trace_fraction==1)
-                       {
-                               // Detect personal waypoints
-                               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-                               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
-                               {
-                                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-                               }
-
-                               navigation_poproute(this);
-                       }
-               }
-       }
-
-       while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
-       {
-               // Detect personal waypoints
-               if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
-               if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
-               {
-                       this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
-                       this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
-               }
-
-               navigation_poproute(this);
-       }
-}
-
-// begin a goal selection session (queries spawnfunc_waypoint network)
-void navigation_goalrating_start(entity this)
-{
-       if(this.aistatus & AI_STATUS_STUCK)
-               return;
-
-       this.navigation_jetpack_goal = NULL;
-       navigation_bestrating = -1;
-       this.navigation_hasgoals = false;
-       navigation_clearroute(this);
-       navigation_bestgoal = NULL;
-       navigation_markroutes(this, NULL);
-}
-
-// ends a goal selection session (updates goal stack to the best goal)
-void navigation_goalrating_end(entity this)
-{
-       if(this.aistatus & AI_STATUS_STUCK)
-               return;
-
-       navigation_routetogoal(this, navigation_bestgoal, this.origin);
-       LOG_DEBUG(strcat("best goal ", this.goalcurrent.classname , "\n"));
-
-       // If the bot got stuck then try to reach the farthest waypoint
-       if (!this.navigation_hasgoals)
-       if (autocvar_bot_wander_enable)
-       {
-               if (!(this.aistatus & AI_STATUS_STUCK))
-               {
-                       LOG_DEBUG(strcat(this.netname, " cannot walk to any goal\n"));
-                       this.aistatus |= AI_STATUS_STUCK;
-               }
-
-               this.navigation_hasgoals = false; // Reset this value
-       }
-}
-
-void botframe_updatedangerousobjects(float maxupdate)
-{
-       entity head, bot_dodgelist;
-       vector m1, m2, v, o;
-       float c, d, danger;
-       c = 0;
-       bot_dodgelist = findchainfloat(bot_dodge, true);
-       botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
-       while (botframe_dangerwaypoint != NULL)
-       {
-               danger = 0;
-               m1 = botframe_dangerwaypoint.mins;
-               m2 = botframe_dangerwaypoint.maxs;
-               head = bot_dodgelist;
-               while (head)
-               {
-                       v = head.origin;
-                       v.x = bound(m1_x, v.x, m2_x);
-                       v.y = bound(m1_y, v.y, m2_y);
-                       v.z = bound(m1_z, v.z, m2_z);
-                       o = (head.absmin + head.absmax) * 0.5;
-                       d = head.bot_dodgerating - vlen(o - v);
-                       if (d > 0)
-                       {
-                               traceline(o, v, true, NULL);
-                               if (trace_fraction == 1)
-                                       danger = danger + d;
-                       }
-                       head = head.chain;
-               }
-               botframe_dangerwaypoint.dmg = danger;
-               c = c + 1;
-               if (c >= maxupdate)
-                       break;
-               botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
-       }
-}
-
-void navigation_unstuck(entity this)
-{
-       float search_radius = 1000;
-
-       if (!autocvar_bot_wander_enable)
-               return;
-
-       if (!bot_waypoint_queue_owner)
-       {
-               LOG_DEBUG(strcat(this.netname, " sutck, taking over the waypoints queue\n"));
-               bot_waypoint_queue_owner = this;
-               bot_waypoint_queue_bestgoal = NULL;
-               bot_waypoint_queue_bestgoalrating = 0;
-       }
-
-       if(bot_waypoint_queue_owner!=this)
-               return;
-
-       if (bot_waypoint_queue_goal)
-       {
-               // evaluate the next goal on the queue
-               float d = vlen(this.origin - bot_waypoint_queue_goal.origin);
-               LOG_DEBUG(strcat(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d), "\n"));
-               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
-               {
-                       if( d > bot_waypoint_queue_bestgoalrating)
-                       {
-                               bot_waypoint_queue_bestgoalrating = d;
-                               bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal;
-                       }
-               }
-               bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal;
-
-               if (!bot_waypoint_queue_goal)
-               {
-                       if (bot_waypoint_queue_bestgoal)
-                       {
-                               LOG_DEBUG(strcat(this.netname, " stuck, reachable waypoint found, heading to it\n"));
-                               navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin);
-                               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-                               this.aistatus &= ~AI_STATUS_STUCK;
-                       }
-                       else
-                       {
-                               LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n"));
-                       }
-
-                       bot_waypoint_queue_owner = NULL;
-               }
-       }
-       else
-       {
-               if(bot_strategytoken!=this)
-                       return;
-
-               // build a new queue
-               LOG_DEBUG(strcat(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n"));
-
-               entity head, first;
-
-               first = NULL;
-               head = findradius(this.origin, search_radius);
-
-               while(head)
-               {
-                       if(head.classname=="waypoint")
-               //      if(!(head.wpflags & WAYPOINTFLAG_GENERATED))
-                       {
-                               if(bot_waypoint_queue_goal)
-                                       bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = head;
-                               else
-                                       first = head;
-
-                               bot_waypoint_queue_goal = head;
-                               bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
-                       }
-
-                       head = head.chain;
-               }
-
-               if (first)
-                       bot_waypoint_queue_goal = first;
-               else
-               {
-                       LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n"));
-                       bot_waypoint_queue_owner = NULL;
-               }
-       }
-}
-
-// Support for debugging tracewalk visually
-
-void debugresetnodes()
-{
-       debuglastnode = '0 0 0';
-}
-
-void debugnode(entity this, vector node)
-{
-       if (!IS_PLAYER(this))
-               return;
-
-       if(debuglastnode=='0 0 0')
-       {
-               debuglastnode = node;
-               return;
-       }
-
-       te_lightning2(NULL, node, debuglastnode);
-       debuglastnode = node;
-}
-
-void debugnodestatus(vector position, float status)
-{
-       vector c;
-
-       switch (status)
-       {
-               case DEBUG_NODE_SUCCESS:
-                       c = '0 15 0';
-                       break;
-               case DEBUG_NODE_WARNING:
-                       c = '15 15 0';
-                       break;
-               case DEBUG_NODE_FAIL:
-                       c = '15 0 0';
-                       break;
-               default:
-                       c = '15 15 15';
-       }
-
-       te_customflash(position, 40,  2, c);
-}
-
-// Support for debugging the goal stack visually
-
-.float goalcounter;
-.vector lastposition;
-
-// Debug the goal stack visually
-void debuggoalstack(entity this)
-{
-       entity goal;
-       vector org, go;
-
-       if(this.goalcounter==0)goal=this.goalcurrent;
-       else if(this.goalcounter==1)goal=this.goalstack01;
-       else if(this.goalcounter==2)goal=this.goalstack02;
-       else if(this.goalcounter==3)goal=this.goalstack03;
-       else if(this.goalcounter==4)goal=this.goalstack04;
-       else if(this.goalcounter==5)goal=this.goalstack05;
-       else if(this.goalcounter==6)goal=this.goalstack06;
-       else if(this.goalcounter==7)goal=this.goalstack07;
-       else if(this.goalcounter==8)goal=this.goalstack08;
-       else if(this.goalcounter==9)goal=this.goalstack09;
-       else if(this.goalcounter==10)goal=this.goalstack10;
-       else if(this.goalcounter==11)goal=this.goalstack11;
-       else if(this.goalcounter==12)goal=this.goalstack12;
-       else if(this.goalcounter==13)goal=this.goalstack13;
-       else if(this.goalcounter==14)goal=this.goalstack14;
-       else if(this.goalcounter==15)goal=this.goalstack15;
-       else if(this.goalcounter==16)goal=this.goalstack16;
-       else if(this.goalcounter==17)goal=this.goalstack17;
-       else if(this.goalcounter==18)goal=this.goalstack18;
-       else if(this.goalcounter==19)goal=this.goalstack19;
-       else if(this.goalcounter==20)goal=this.goalstack20;
-       else if(this.goalcounter==21)goal=this.goalstack21;
-       else if(this.goalcounter==22)goal=this.goalstack22;
-       else if(this.goalcounter==23)goal=this.goalstack23;
-       else if(this.goalcounter==24)goal=this.goalstack24;
-       else if(this.goalcounter==25)goal=this.goalstack25;
-       else if(this.goalcounter==26)goal=this.goalstack26;
-       else if(this.goalcounter==27)goal=this.goalstack27;
-       else if(this.goalcounter==28)goal=this.goalstack28;
-       else if(this.goalcounter==29)goal=this.goalstack29;
-       else if(this.goalcounter==30)goal=this.goalstack30;
-       else if(this.goalcounter==31)goal=this.goalstack31;
-       else goal=NULL;
-
-       if(goal==NULL)
-       {
-               this.goalcounter = 0;
-               this.lastposition='0 0 0';
-               return;
-       }
-
-       if(this.lastposition=='0 0 0')
-               org = this.origin;
-       else
-               org = this.lastposition;
-
-
-       go = ( goal.absmin + goal.absmax ) * 0.5;
-       te_lightning2(NULL, org, go);
-       this.lastposition = go;
-
-       this.goalcounter++;
-}
diff --git a/qcsrc/server/bot/navigation.qh b/qcsrc/server/bot/navigation.qh
deleted file mode 100644 (file)
index 7fef6af..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-float navigation_bestrating;
-float bot_navigation_movemode;
-float navigation_testtracewalk;
-
-vector jumpstepheightvec;
-vector stepheightvec;
-
-entity botframe_dangerwaypoint;
-entity navigation_bestgoal;
-
-// stack of current goals (the last one of which may be an item or other
-// desirable object, the rest are typically waypoints to reach it)
-.entity goalcurrent, goalstack01, goalstack02, goalstack03;
-.entity goalstack04, goalstack05, goalstack06, goalstack07;
-.entity goalstack08, goalstack09, goalstack10, goalstack11;
-.entity goalstack12, goalstack13, goalstack14, goalstack15;
-.entity goalstack16, goalstack17, goalstack18, goalstack19;
-.entity goalstack20, goalstack21, goalstack22, goalstack23;
-.entity goalstack24, goalstack25, goalstack26, goalstack27;
-.entity goalstack28, goalstack29, goalstack30, goalstack31;
-.entity nearestwaypoint;
-
-.float nearestwaypointtimeout;
-.float navigation_hasgoals;
-.float lastteleporttime;
-
-.float blacklisted;
-
-.entity navigation_jetpack_goal;
-.vector navigation_jetpack_point;
-
-const float DEBUG_NODE_SUCCESS        = 1;
-const float DEBUG_NODE_WARNING        = 2;
-const float DEBUG_NODE_FAIL           = 3;
-vector debuglastnode;
-
-entity bot_waypoint_queue_owner;       // Owner of the temporary list of goals
-entity bot_waypoint_queue_goal;                // Head of the temporary list of goals
-.entity bot_waypoint_queue_nextgoal;
-entity bot_waypoint_queue_bestgoal;
-float bot_waypoint_queue_bestgoalrating;
-
-/*
- * Functions
- */
-
-void debugresetnodes();
-void debugnode(entity this, vector node);
-void debugnodestatus(vector position, float status);
-
-void debuggoalstack(entity this);
-
-float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode);
-
-float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist);
-float navigation_routetogoal(entity this, entity e, vector startposition);
-
-void navigation_clearroute(entity this);
-void navigation_pushroute(entity this, entity e);
-void navigation_poproute(entity this);
-void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p);
-void navigation_markroutes(entity this, entity fixed_source_waypoint);
-void navigation_markroutes_inverted(entity fixed_source_waypoint);
-void navigation_routerating(entity this, entity e, float f, float rangebias);
-void navigation_poptouchedgoals(entity this);
-void navigation_goalrating_start(entity this);
-void navigation_goalrating_end(entity this);
-void navigation_unstuck(entity this);
-
-void botframe_updatedangerousobjects(float maxupdate);
-
-entity navigation_findnearestwaypoint(entity ent, float walkfromwp);
-float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist);
diff --git a/qcsrc/server/bot/null/_mod.inc b/qcsrc/server/bot/null/_mod.inc
new file mode 100644 (file)
index 0000000..d40f451
--- /dev/null
@@ -0,0 +1,2 @@
+// generated file; do not modify
+#include <server/bot/null/bot_null.qc>
diff --git a/qcsrc/server/bot/null/_mod.qh b/qcsrc/server/bot/null/_mod.qh
new file mode 100644 (file)
index 0000000..0449a1c
--- /dev/null
@@ -0,0 +1,2 @@
+// generated file; do not modify
+#include <server/bot/null/bot_null.qh>
diff --git a/qcsrc/server/bot/null/bot_null.qc b/qcsrc/server/bot/null/bot_null.qc
new file mode 100644 (file)
index 0000000..b4102cb
--- /dev/null
@@ -0,0 +1,42 @@
+#include "bot_null.qh"
+
+#if 0
+bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity) { return false; }
+void bot_clientconnect(entity this) { }
+void bot_clientdisconnect(entity this) { }
+void bot_cmdhelp(string scmd) { }
+void bot_endgame() { }
+bool bot_fixcount() { return true; }
+void bot_list_commands() { }
+void bot_queuecommand(entity bot, string cmdstring) { }
+void bot_relinkplayerlist() { }
+void bot_resetqueues() { }
+void bot_serverframe() { }
+bool bot_shouldattack(entity this, entity e) { return false; }
+void bot_think(entity this) { }
+
+entity find_bot_by_name(string name) { return NULL; }
+entity find_bot_by_number(float number) { return NULL; }
+
+void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) { }
+void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) { }
+
+entity navigation_findnearestwaypoint(entity ent, float walkfromwp) { return NULL; }
+void navigation_goalrating_end(entity this) { }
+void navigation_goalrating_start(entity this) { }
+void navigation_markroutes(entity this, entity fixed_source_waypoint) { }
+void navigation_markroutes_inverted(entity fixed_source_waypoint) { }
+void navigation_routerating(entity this, entity e, float f, float rangebias) { }
+
+bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) { return false; }
+
+void waypoint_remove(entity e) { }
+void waypoint_saveall() { }
+void waypoint_schedulerelinkall() { }
+void waypoint_schedulerelink(entity wp) { }
+void waypoint_spawnforitem(entity e) { }
+void waypoint_spawnforitem_force(entity e, vector org) { }
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken) { }
+void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken) { }
+entity waypoint_spawn(vector m1, vector m2, float f) { return NULL; }
+#endif
diff --git a/qcsrc/server/bot/null/bot_null.qh b/qcsrc/server/bot/null/bot_null.qh
new file mode 100644 (file)
index 0000000..28709a2
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "../api.qh"
diff --git a/qcsrc/server/bot/scripting.qc b/qcsrc/server/bot/scripting.qc
deleted file mode 100644 (file)
index 3734ad8..0000000
+++ /dev/null
@@ -1,1341 +0,0 @@
-#include "scripting.qh"
-
-#include <common/state.qh>
-#include <common/physics/player.qh>
-
-#include "bot.qh"
-
-.int state;
-
-.float bot_cmdqueuebuf_allocated;
-.float bot_cmdqueuebuf;
-.float bot_cmdqueuebuf_start;
-.float bot_cmdqueuebuf_end;
-
-void bot_clearqueue(entity bot)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               return;
-       buf_del(bot.bot_cmdqueuebuf);
-       bot.bot_cmdqueuebuf_allocated = false;
-       LOG_TRACE("bot ", bot.netname, " queue cleared\n");
-}
-
-void bot_queuecommand(entity bot, string cmdstring)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-       {
-               bot.bot_cmdqueuebuf = buf_create();
-               bot.bot_cmdqueuebuf_allocated = true;
-               bot.bot_cmdqueuebuf_start = 0;
-               bot.bot_cmdqueuebuf_end = 0;
-       }
-
-       bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
-
-       // if the command was a "sound" command, precache the sound NOW
-       // this prevents lagging!
-       {
-               float sp;
-               string parm;
-               string cmdstr;
-
-               sp = strstrofs(cmdstring, " ", 0);
-               if(sp >= 0)
-               {
-                       parm = substring(cmdstring, sp + 1, -1);
-                       cmdstr = substring(cmdstring, 0, sp);
-                       if(cmdstr == "sound")
-                       {
-                               // find the LAST word
-                               for (;;)
-                               {
-                                       sp = strstrofs(parm, " ", 0);
-                                       if(sp < 0)
-                                               break;
-                                       parm = substring(parm, sp + 1, -1);
-                               }
-                               precache_sound(parm);
-                       }
-               }
-       }
-
-       bot.bot_cmdqueuebuf_end += 1;
-}
-
-void bot_dequeuecommand(entity bot, float idx)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               error("dequeuecommand but no queue allocated");
-       if(idx < bot.bot_cmdqueuebuf_start)
-               error("dequeueing a command in the past");
-       if(idx >= bot.bot_cmdqueuebuf_end)
-               error("dequeueing a command in the future");
-       bufstr_set(bot.bot_cmdqueuebuf, idx, "");
-       if(idx == bot.bot_cmdqueuebuf_start)
-               bot.bot_cmdqueuebuf_start += 1;
-       if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
-               bot_clearqueue(bot);
-}
-
-string bot_readcommand(entity bot, float idx)
-{
-       if(!bot.bot_cmdqueuebuf_allocated)
-               error("readcommand but no queue allocated");
-       if(idx < bot.bot_cmdqueuebuf_start)
-               error("reading a command in the past");
-       if(idx >= bot.bot_cmdqueuebuf_end)
-               error("reading a command in the future");
-       return bufstr_get(bot.bot_cmdqueuebuf, idx);
-}
-
-bool bot_havecommand(entity this, int idx)
-{
-       if(!this.bot_cmdqueuebuf_allocated)
-               return false;
-       if(idx < this.bot_cmdqueuebuf_start)
-               return false;
-       if(idx >= this.bot_cmdqueuebuf_end)
-               return false;
-       return true;
-}
-
-const int MAX_BOT_PLACES = 4;
-.float bot_places_count;
-.entity bot_places[MAX_BOT_PLACES];
-.string bot_placenames[MAX_BOT_PLACES];
-entity bot_getplace(entity this, string placename)
-{
-       entity e;
-       if(substring(placename, 0, 1) == "@")
-       {
-               int i, p;
-               placename = substring(placename, 1, -1);
-               string s, s2;
-               for(i = 0; i < this.bot_places_count; ++i)
-                       if(this.(bot_placenames[i]) == placename)
-                               return this.(bot_places[i]);
-               // now: i == this.bot_places_count
-               s = s2 = cvar_string(placename);
-               p = strstrofs(s2, " ", 0);
-               if(p >= 0)
-               {
-                       s = substring(s2, 0, p);
-                       //print("places: ", placename, " -> ", cvar_string(placename), "\n");
-                       cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
-                       //print("places: ", placename, " := ", cvar_string(placename), "\n");
-               }
-               e = find(NULL, targetname, s);
-               if(!e)
-                       LOG_INFO("invalid place ", s, "\n");
-               if(i < MAX_BOT_PLACES)
-               {
-                       this.(bot_placenames[i]) = strzone(placename);
-                       this.(bot_places[i]) = e;
-                       this.bot_places_count += 1;
-               }
-               return e;
-       }
-       else
-       {
-               e = find(NULL, targetname, placename);
-               if(!e)
-                       LOG_INFO("invalid place ", placename, "\n");
-               return e;
-       }
-}
-
-
-// Initialize global commands list
-// NOTE: New commands should be initialized here
-void bot_commands_init()
-{
-       bot_cmd_string[BOT_CMD_NULL] = "";
-       bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_PAUSE] = "pause";
-       bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
-       bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_WAIT] = "wait";
-       bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_TURN] = "turn";
-       bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
-       bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
-
-       bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
-       bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
-       bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_CC] = "cc";
-       bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_IF] = "if";
-       bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_ELSE] = "else";
-       bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_FI] = "fi";
-       bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
-       bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_AIM] = "aim";
-       bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
-       bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
-       bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
-       bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
-       bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
-       bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
-       bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
-       bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
-
-       bot_cmd_string[BOT_CMD_CONSOLE] = "console";
-       bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_SOUND] = "sound";
-       bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
-
-       bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
-       bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
-
-       bot_cmds_initialized = true;
-}
-
-// Returns first bot with matching name
-entity find_bot_by_name(string name)
-{
-       entity bot;
-
-       bot = findchainflags(flags, FL_CLIENT);
-       while (bot)
-       {
-               if(IS_BOT_CLIENT(bot))
-               if(bot.netname==name)
-                       return bot;
-
-               bot = bot.chain;
-       }
-
-       return NULL;
-}
-
-// Returns a bot by number on list
-entity find_bot_by_number(float number)
-{
-       entity bot;
-       float c = 0;
-
-       if(!number)
-               return NULL;
-
-       bot = findchainflags(flags, FL_CLIENT);
-       while (bot)
-       {
-               if(IS_BOT_CLIENT(bot))
-               {
-                       if(++c==number)
-                               return bot;
-               }
-               bot = bot.chain;
-       }
-
-       return NULL;
-}
-
-float bot_decodecommand(string cmdstring)
-{
-       float cmd_parm_type;
-       float sp;
-       string parm;
-
-       sp = strstrofs(cmdstring, " ", 0);
-       if(sp < 0)
-       {
-               parm = "";
-       }
-       else
-       {
-               parm = substring(cmdstring, sp + 1, -1);
-               cmdstring = substring(cmdstring, 0, sp);
-       }
-
-       if(!bot_cmds_initialized)
-               bot_commands_init();
-
-       int i;
-       for(i=1;i<BOT_CMD_COUNTER;++i)
-       {
-               if(bot_cmd_string[i]!=cmdstring)
-                       continue;
-
-               cmd_parm_type = bot_cmd_parm_type[i];
-
-               if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
-               {
-                       LOG_INFO("ERROR: A parameter is required for this command\n");
-                       return 0;
-               }
-
-               // Load command into queue
-               bot_cmd.bot_cmd_type = i;
-
-               // Attach parameter
-               switch(cmd_parm_type)
-               {
-                       case BOT_CMD_PARAMETER_FLOAT:
-                               bot_cmd.bot_cmd_parm_float = stof(parm);
-                               break;
-                       case BOT_CMD_PARAMETER_STRING:
-                               if(bot_cmd.bot_cmd_parm_string)
-                                       strunzone(bot_cmd.bot_cmd_parm_string);
-                               bot_cmd.bot_cmd_parm_string = strzone(parm);
-                               break;
-                       case BOT_CMD_PARAMETER_VECTOR:
-                               bot_cmd.bot_cmd_parm_vector = stov(parm);
-                               break;
-                       default:
-                               break;
-               }
-               return 1;
-       }
-       LOG_INFO("ERROR: No such command '", cmdstring, "'\n");
-       return 0;
-}
-
-void bot_cmdhelp(string scmd)
-{
-       int i, ntype;
-       string stype;
-
-       if(!bot_cmds_initialized)
-               bot_commands_init();
-
-       for(i=1;i<BOT_CMD_COUNTER;++i)
-       {
-               if(bot_cmd_string[i]!=scmd)
-                       continue;
-
-               ntype = bot_cmd_parm_type[i];
-
-               switch(ntype)
-               {
-                       case BOT_CMD_PARAMETER_FLOAT:
-                               stype = "float number";
-                               break;
-                       case BOT_CMD_PARAMETER_STRING:
-                               stype = "string";
-                               break;
-                       case BOT_CMD_PARAMETER_VECTOR:
-                               stype = "vector";
-                               break;
-                       default:
-                               stype = "none";
-                               break;
-               }
-
-               LOG_INFO(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
-
-               LOG_INFO("Description: ");
-               switch(i)
-               {
-                       case BOT_CMD_PAUSE:
-                               LOG_INFO("Stops the bot completely. Any command other than 'continue' will be ignored.");
-                               break;
-                       case BOT_CMD_CONTINUE:
-                               LOG_INFO("Disable paused status");
-                               break;
-                       case BOT_CMD_WAIT:
-                               LOG_INFO("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
-                               break;
-                       case BOT_CMD_WAIT_UNTIL:
-                               LOG_INFO("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
-                               break;
-                       case BOT_CMD_BARRIER:
-                               LOG_INFO("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
-                               break;
-                       case BOT_CMD_TURN:
-                               LOG_INFO("Look to the right or left N degrees. For turning to the left use positive numbers.");
-                               break;
-                       case BOT_CMD_MOVETO:
-                               LOG_INFO("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
-                               break;
-                       case BOT_CMD_MOVETOTARGET:
-                               LOG_INFO("Walk to the specific target on the map");
-                               break;
-                       case BOT_CMD_RESETGOAL:
-                               LOG_INFO("Resets the goal stack");
-                               break;
-                       case BOT_CMD_CC:
-                               LOG_INFO("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
-                               break;
-                       case BOT_CMD_IF:
-                               LOG_INFO("Perform simple conditional execution.\n");
-                               LOG_INFO("Syntax: \n");
-                               LOG_INFO("        sv_cmd .. if \"condition\"\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if true>\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if true>\n");
-                               LOG_INFO("        sv_cmd .. else\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if false>\n");
-                               LOG_INFO("        sv_cmd ..     <instruction if false>\n");
-                               LOG_INFO("        sv_cmd .. fi\n");
-                               LOG_INFO("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
-                               LOG_INFO("            Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
-                               LOG_INFO("Fields: health, speed, flagcarrier\n");
-                               LOG_INFO("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
-                               break;
-                       case BOT_CMD_RESETAIM:
-                               LOG_INFO("Points the aim to the coordinates x,y 0,0");
-                               break;
-                       case BOT_CMD_AIM:
-                               LOG_INFO("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
-                               LOG_INFO("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
-                               LOG_INFO("Examples: aim \"90 0\"        // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
-                               LOG_INFO("          aim \"0 90 2\"      // Will gradually look to the sky in the next two seconds");
-                               break;
-                       case BOT_CMD_AIMTARGET:
-                               LOG_INFO("Points the aim to given target");
-                               break;
-                       case BOT_CMD_PRESSKEY:
-                               LOG_INFO("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
-                               LOG_INFO("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
-                               LOG_INFO("Note: The script will not return the control to the bot ai until all keys are released");
-                               break;
-                       case BOT_CMD_RELEASEKEY:
-                               LOG_INFO("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
-                               break;
-                       case BOT_CMD_SOUND:
-                               LOG_INFO("play sound file at bot location");
-                               break;
-                       case BOT_CMD_DEBUG_ASSERT_CANFIRE:
-                               LOG_INFO("verify the state of the weapon entity");
-                               break;
-                       default:
-                               LOG_INFO("This command has no description yet.");
-                               break;
-               }
-               LOG_INFO("\n");
-       }
-}
-
-void bot_list_commands()
-{
-       int i;
-       string ptype;
-
-       if(!bot_cmds_initialized)
-               bot_commands_init();
-
-       LOG_INFO("List of all available commands:\n");
-       LOG_INFO("  Command - Parameter Type\n");
-
-       for(i=1;i<BOT_CMD_COUNTER;++i)
-       {
-               switch(bot_cmd_parm_type[i])
-               {
-                       case BOT_CMD_PARAMETER_FLOAT:
-                               ptype = "float number";
-                               break;
-                       case BOT_CMD_PARAMETER_STRING:
-                               ptype = "string";
-                               break;
-                       case BOT_CMD_PARAMETER_VECTOR:
-                               ptype = "vector";
-                               break;
-                       default:
-                               ptype = "none";
-                               break;
-               }
-               LOG_INFO(strcat("  ",bot_cmd_string[i]," - <",ptype,"> \n"));
-       }
-}
-
-// Commands code
-.int bot_exec_status;
-
-void SV_ParseClientCommand(string s);
-float bot_cmd_cc(entity this)
-{
-       WITHSELF(this, SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string));
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_impulse(entity this)
-{
-       this.impulse = bot_cmd.bot_cmd_parm_float;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_continue(entity this)
-{
-       this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
-       return CMD_STATUS_FINISHED;
-}
-
-.float bot_cmd_wait_time;
-float bot_cmd_wait(entity this)
-{
-       if(this.bot_exec_status & BOT_EXEC_STATUS_WAITING)
-       {
-               if(time>=this.bot_cmd_wait_time)
-               {
-                       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
-                       return CMD_STATUS_FINISHED;
-               }
-               else
-                       return CMD_STATUS_EXECUTING;
-       }
-
-       this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
-       this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
-       return CMD_STATUS_EXECUTING;
-}
-
-float bot_cmd_wait_until(entity this)
-{
-       if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
-       {
-               this.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
-               return CMD_STATUS_EXECUTING;
-       }
-       this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_barrier(entity this)
-{
-       // 0 = no barrier, 1 = waiting, 2 = waiting finished
-
-       if(this.bot_barrier == 0) // initialization
-       {
-               this.bot_barrier = 1;
-
-               //this.colormod = '4 4 0';
-       }
-
-       if(this.bot_barrier == 1) // find other bots
-       {
-               FOREACH_CLIENT(it.isbot, LAMBDA(
-                       if(it.bot_cmdqueuebuf_allocated)
-                       if(it.bot_barrier != 1)
-                               return CMD_STATUS_EXECUTING; // not all are at the barrier yet
-               ));
-
-               // all bots hit the barrier!
-
-               // acknowledge barrier
-               FOREACH_CLIENT(it.isbot, LAMBDA(it.bot_barrier = 2));
-
-               bot_barriertime = time;
-       }
-
-       // if we get here, the barrier is finished
-       // so end it...
-       this.bot_barrier = 0;
-       //this.colormod = '0 0 0';
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_turn(entity this)
-{
-       this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float;
-       this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_select_weapon(entity this)
-{
-       float id = bot_cmd.bot_cmd_parm_float;
-
-       if(id < WEP_FIRST || id > WEP_LAST)
-               return CMD_STATUS_ERROR;
-
-       if(client_hasweapon(this, Weapons_from(id), true, false))
-               PS(this).m_switchweapon = Weapons_from(id);
-       else
-               return CMD_STATUS_ERROR;
-
-       return CMD_STATUS_FINISHED;
-}
-
-.int bot_cmd_condition_status;
-
-const int CMD_CONDITION_NONE = 0;
-const int CMD_CONDITION_true = 1;
-const int CMD_CONDITION_false = 2;
-const int CMD_CONDITION_true_BLOCK = 4;
-const int CMD_CONDITION_false_BLOCK = 8;
-
-float bot_cmd_eval(entity this, string expr)
-{
-       // Search for numbers
-       if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
-       {
-               return stof(expr);
-       }
-
-       // Search for cvars
-       if(substring(expr, 0, 5)=="cvar.")
-       {
-               return cvar(substring(expr, 5, strlen(expr)));
-       }
-
-       // Search for fields
-       switch(expr)
-       {
-               case "health":
-                       return this.health;
-               case "speed":
-                       return vlen(this.velocity);
-               case "flagcarrier":
-                       return ((this.flagcarried!=NULL));
-       }
-
-       LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
-       return 0;
-}
-
-float bot_cmd_if(entity this)
-{
-       string expr, val_a, val_b;
-       float cmpofs;
-
-       if(this.bot_cmd_condition_status != CMD_CONDITION_NONE)
-       {
-               // Only one "if" block is allowed at time
-               LOG_INFO("ERROR: Only one conditional block can be processed at time");
-               bot_clearqueue(this);
-               return CMD_STATUS_ERROR;
-       }
-
-       this.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK;
-
-       // search for operators
-       expr = bot_cmd.bot_cmd_parm_string;
-
-       cmpofs = strstrofs(expr,"=",0);
-
-       if(cmpofs>0)
-       {
-               val_a = substring(expr,0,cmpofs);
-               val_b = substring(expr,cmpofs+1,strlen(expr));
-
-               if(bot_cmd_eval(this, val_a)==bot_cmd_eval(this, val_b))
-                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       cmpofs = strstrofs(expr,">",0);
-
-       if(cmpofs>0)
-       {
-               val_a = substring(expr,0,cmpofs);
-               val_b = substring(expr,cmpofs+1,strlen(expr));
-
-               if(bot_cmd_eval(this, val_a)>bot_cmd_eval(this, val_b))
-                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       cmpofs = strstrofs(expr,"<",0);
-
-       if(cmpofs>0)
-       {
-               val_a = substring(expr,0,cmpofs);
-               val_b = substring(expr,cmpofs+1,strlen(expr));
-
-               if(bot_cmd_eval(this, val_a)<bot_cmd_eval(this, val_b))
-                       this.bot_cmd_condition_status |= CMD_CONDITION_true;
-               else
-                       this.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-               return CMD_STATUS_FINISHED;
-       }
-
-       if(bot_cmd_eval(this, expr))
-               this.bot_cmd_condition_status |= CMD_CONDITION_true;
-       else
-               this.bot_cmd_condition_status |= CMD_CONDITION_false;
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_else(entity this)
-{
-       this.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
-       this.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_fi(entity this)
-{
-       this.bot_cmd_condition_status = CMD_CONDITION_NONE;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_resetaim(entity this)
-{
-       this.v_angle = '0 0 0';
-       return CMD_STATUS_FINISHED;
-}
-
-.float bot_cmd_aim_begintime;
-.float bot_cmd_aim_endtime;
-.vector bot_cmd_aim_begin;
-.vector bot_cmd_aim_end;
-
-float bot_cmd_aim(entity this)
-{
-       // Current direction
-       if(this.bot_cmd_aim_endtime)
-       {
-               float progress;
-
-               progress = min(1 - (this.bot_cmd_aim_endtime - time) / (this.bot_cmd_aim_endtime - this.bot_cmd_aim_begintime),1);
-               this.v_angle = this.bot_cmd_aim_begin + ((this.bot_cmd_aim_end - this.bot_cmd_aim_begin) * progress);
-
-               if(time>=this.bot_cmd_aim_endtime)
-               {
-                       this.bot_cmd_aim_endtime = 0;
-                       return CMD_STATUS_FINISHED;
-               }
-               else
-                       return CMD_STATUS_EXECUTING;
-       }
-
-       // New aiming direction
-       string parms;
-       float tokens, step;
-
-       parms = bot_cmd.bot_cmd_parm_string;
-
-       tokens = tokenizebyseparator(parms, " ");
-
-       if(tokens<2||tokens>3)
-               return CMD_STATUS_ERROR;
-
-       step = (tokens == 3) ? stof(argv(2)) : 0;
-
-       if(step == 0)
-       {
-               this.v_angle_x -= stof(argv(1));
-               this.v_angle_y += stof(argv(0));
-               return CMD_STATUS_FINISHED;
-       }
-
-       this.bot_cmd_aim_begin = this.v_angle;
-
-       this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1));
-       this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0));
-       this.bot_cmd_aim_end_z = 0;
-
-       this.bot_cmd_aim_begintime = time;
-       this.bot_cmd_aim_endtime = time + step;
-
-       return CMD_STATUS_EXECUTING;
-}
-
-float bot_cmd_aimtarget(entity this)
-{
-       if(this.bot_cmd_aim_endtime)
-       {
-               return bot_cmd_aim(this);
-       }
-
-       entity e;
-       string parms;
-       vector v;
-       float tokens, step;
-
-       parms = bot_cmd.bot_cmd_parm_string;
-
-       tokens = tokenizebyseparator(parms, " ");
-
-       e = bot_getplace(this, argv(0));
-       if(!e)
-               return CMD_STATUS_ERROR;
-
-       v = e.origin + (e.mins + e.maxs) * 0.5;
-
-       if(tokens==1)
-       {
-               this.v_angle = vectoangles(v - (this.origin + this.view_ofs));
-               this.v_angle_x = -this.v_angle.x;
-               return CMD_STATUS_FINISHED;
-       }
-
-       if(tokens<1||tokens>2)
-               return CMD_STATUS_ERROR;
-
-       step = stof(argv(1));
-
-       this.bot_cmd_aim_begin = this.v_angle;
-       this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs));
-       this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x;
-
-       this.bot_cmd_aim_begintime = time;
-       this.bot_cmd_aim_endtime = time + step;
-
-       return CMD_STATUS_EXECUTING;
-}
-
-.int bot_cmd_keys;
-
-const int BOT_CMD_KEY_NONE             = 0;
-const int BOT_CMD_KEY_FORWARD  = BIT(0);
-const int BOT_CMD_KEY_BACKWARD         = BIT(1);
-const int BOT_CMD_KEY_RIGHT    = BIT(2);
-const int BOT_CMD_KEY_LEFT             = BIT(3);
-const int BOT_CMD_KEY_JUMP             = BIT(4);
-const int BOT_CMD_KEY_ATTACK1  = BIT(5);
-const int BOT_CMD_KEY_ATTACK2  = BIT(6);
-const int BOT_CMD_KEY_USE              = BIT(7);
-const int BOT_CMD_KEY_HOOK             = BIT(8);
-const int BOT_CMD_KEY_CROUCH   = BIT(9);
-const int BOT_CMD_KEY_CHAT             = BIT(10);
-
-bool bot_presskeys(entity this)
-{
-       this.movement = '0 0 0';
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
-       PHYS_INPUT_BUTTON_CROUCH(this) = false;
-       PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_ATCK2(this) = false;
-       PHYS_INPUT_BUTTON_USE(this) = false;
-       PHYS_INPUT_BUTTON_HOOK(this) = false;
-       PHYS_INPUT_BUTTON_CHAT(this) = false;
-
-       if(this.bot_cmd_keys == BOT_CMD_KEY_NONE)
-               return false;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
-               this.movement_x = autocvar_sv_maxspeed;
-       else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
-               this.movement_x = -autocvar_sv_maxspeed;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
-               this.movement_y = autocvar_sv_maxspeed;
-       else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT)
-               this.movement_y = -autocvar_sv_maxspeed;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_JUMP)
-               PHYS_INPUT_BUTTON_JUMP(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
-               PHYS_INPUT_BUTTON_CROUCH(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
-               PHYS_INPUT_BUTTON_ATCK(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
-               PHYS_INPUT_BUTTON_ATCK2(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_USE)
-               PHYS_INPUT_BUTTON_USE(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_HOOK)
-               PHYS_INPUT_BUTTON_HOOK(this) = true;
-
-       if(this.bot_cmd_keys & BOT_CMD_KEY_CHAT)
-               PHYS_INPUT_BUTTON_CHAT(this) = true;
-
-       return true;
-}
-
-
-float bot_cmd_keypress_handler(entity this, string key, float enabled)
-{
-       switch(key)
-       {
-               case "all":
-                       if(enabled)
-                               this.bot_cmd_keys = power2of(20) - 1; // >:)
-                       else
-                               this.bot_cmd_keys = BOT_CMD_KEY_NONE;
-               case "forward":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
-                       break;
-               case "backward":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
-                       break;
-               case "left":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
-                       break;
-               case "right":
-                       if(enabled)
-                       {
-                               this.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
-                       }
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
-                       break;
-               case "jump":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
-                       break;
-               case "crouch":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
-                       break;
-               case "attack1":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
-                       break;
-               case "attack2":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
-                       break;
-               case "use":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_USE;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
-                       break;
-               case "hook":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
-                       break;
-               case "chat":
-                       if(enabled)
-                               this.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
-                       else
-                               this.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
-                       break;
-               default:
-                       break;
-       }
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_presskey(entity this)
-{
-       string key;
-
-       key = bot_cmd.bot_cmd_parm_string;
-
-       bot_cmd_keypress_handler(this, key,true);
-
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_releasekey(entity this)
-{
-       string key;
-
-       key = bot_cmd.bot_cmd_parm_string;
-
-       return bot_cmd_keypress_handler(this, key,false);
-}
-
-float bot_cmd_pause(entity this)
-{
-       PHYS_INPUT_BUTTON_DRAG(this) = false;
-       PHYS_INPUT_BUTTON_USE(this) = false;
-       PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
-       PHYS_INPUT_BUTTON_HOOK(this) = false;
-       PHYS_INPUT_BUTTON_CHAT(this) = false;
-       PHYS_INPUT_BUTTON_ATCK2(this) = false;
-       PHYS_INPUT_BUTTON_CROUCH(this) = false;
-
-       this.movement = '0 0 0';
-       this.bot_cmd_keys = BOT_CMD_KEY_NONE;
-
-       this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
-       return CMD_STATUS_FINISHED;
-}
-
-float bot_cmd_moveto(entity this)
-{
-       return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector);
-}
-
-float bot_cmd_movetotarget(entity this)
-{
-       entity e;
-       e = bot_getplace(this, bot_cmd.bot_cmd_parm_string);
-       if(!e)
-               return CMD_STATUS_ERROR;
-       return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5);
-}
-
-float bot_cmd_resetgoal(entity this)
-{
-       return this.cmd_resetgoal(this);
-}
-
-
-float bot_cmd_sound(entity this)
-{
-       string f;
-       f = bot_cmd.bot_cmd_parm_string;
-
-       float n = tokenizebyseparator(f, " ");
-
-       string sample = f;
-       float chan = CH_WEAPON_B;
-       float vol = VOL_BASE;
-       float atten = ATTEN_MIN;
-
-       if(n >= 1)
-               sample = argv(n - 1);
-       if(n >= 2)
-               chan = stof(argv(0));
-       if(n >= 3)
-               vol = stof(argv(1));
-       if(n >= 4)
-               atten = stof(argv(2));
-
-       precache_sound(f);
-       _sound(this, chan, sample, vol, atten);
-
-       return CMD_STATUS_FINISHED;
-}
-
-.entity tuba_note;
-float bot_cmd_debug_assert_canfire(entity this)
-{
-       float f = bot_cmd.bot_cmd_parm_float;
-
-       int slot = 0;
-       .entity weaponentity = weaponentities[slot];
-       if(this.(weaponentity).state != WS_READY)
-       {
-               if(f)
-               {
-                       this.colormod = '0 8 8';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by weaponentity state\n");
-               }
-       }
-       else if(ATTACK_FINISHED(this, slot) > time)
-       {
-               if(f)
-               {
-                       this.colormod = '8 0 8';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)\n");
-               }
-       }
-       else if(this.tuba_note)
-       {
-               if(f)
-               {
-                       this.colormod = '8 0 0';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, bot still has an active tuba note\n");
-               }
-       }
-       else
-       {
-               if(!f)
-               {
-                       this.colormod = '8 8 0';
-                       LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left\n");
-               }
-       }
-
-       return CMD_STATUS_FINISHED;
-}
-
-//
-
-void bot_command_executed(entity this, bool rm)
-{
-       entity cmd;
-
-       cmd = bot_cmd;
-
-       if(rm)
-               bot_dequeuecommand(this, this.bot_cmd_execution_index);
-
-       this.bot_cmd_execution_index++;
-}
-
-void bot_setcurrentcommand(entity this)
-{
-       bot_cmd = NULL;
-
-       if(!this.bot_cmd_current)
-       {
-               this.bot_cmd_current = new_pure(bot_cmd);
-               this.bot_cmd_current.is_bot_cmd = true;
-       }
-
-       bot_cmd = this.bot_cmd_current;
-       if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0)
-       {
-               if(bot_havecommand(this, this.bot_cmd_execution_index))
-               {
-                       string cmdstring;
-                       cmdstring = bot_readcommand(this, this.bot_cmd_execution_index);
-                       if(bot_decodecommand(cmdstring))
-                       {
-                               bot_cmd.owner = this;
-                               bot_cmd.bot_cmd_index = this.bot_cmd_execution_index;
-                       }
-                       else
-                       {
-                               // Invalid command, remove from queue
-                               bot_cmd = NULL;
-                               bot_dequeuecommand(this, this.bot_cmd_execution_index);
-                               this.bot_cmd_execution_index++;
-                       }
-               }
-               else
-                       bot_cmd = NULL;
-       }
-}
-
-void bot_resetqueues()
-{
-       FOREACH_CLIENT(it.isbot, LAMBDA(
-               it.bot_cmd_execution_index = 0;
-               bot_clearqueue(it);
-               // also, cancel all barriers
-               it.bot_barrier = 0;
-               for(int i = 0; i < it.bot_places_count; ++i)
-               {
-                       strunzone(it.(bot_placenames[i]));
-                       it.(bot_placenames[i]) = string_null;
-               }
-               it.bot_places_count = 0;
-       ));
-
-       bot_barriertime = time;
-}
-
-// Here we map commands to functions and deal with complex interactions between commands and execution states
-// NOTE: Of course you need to include your commands here too :)
-float bot_execute_commands_once(entity this)
-{
-       float status, ispressingkey;
-
-       // Find command
-       bot_setcurrentcommand(this);
-
-       // if we have no bot command, better return
-       // old logic kept pressing previously pressed keys, but that has problems
-       // (namely, it means you cannot make a bot "normal" ever again)
-       // to keep a bot walking for a while, use the "wait" bot command
-       if(bot_cmd == NULL)
-               return false;
-
-       // Ignore all commands except continue when the bot is paused
-       if(this.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
-       if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
-       {
-               if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
-               {
-                       bot_command_executed(this, true);
-                       LOG_INFO( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
-               }
-               return 1;
-       }
-
-       // Keep pressing keys raised by the "presskey" command
-       ispressingkey = boolean(bot_presskeys(this));
-
-       // Handle conditions
-       if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
-       if(this.bot_cmd_condition_status & CMD_CONDITION_true && this.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK)
-       {
-               bot_command_executed(this, true);
-               return -1;
-       }
-       else if(this.bot_cmd_condition_status & CMD_CONDITION_false && this.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK)
-       {
-               bot_command_executed(this, true);
-               return -1;
-       }
-
-       // Map commands to functions
-       switch(bot_cmd.bot_cmd_type)
-       {
-               case BOT_CMD_NULL:
-                       return ispressingkey;
-                       //break;
-               case BOT_CMD_PAUSE:
-                       status = bot_cmd_pause(this);
-                       break;
-               case BOT_CMD_CONTINUE:
-                       status = bot_cmd_continue(this);
-                       break;
-               case BOT_CMD_WAIT:
-                       status = bot_cmd_wait(this);
-                       break;
-               case BOT_CMD_WAIT_UNTIL:
-                       status = bot_cmd_wait_until(this);
-                       break;
-               case BOT_CMD_TURN:
-                       status = bot_cmd_turn(this);
-                       break;
-               case BOT_CMD_MOVETO:
-                       status = bot_cmd_moveto(this);
-                       break;
-               case BOT_CMD_MOVETOTARGET:
-                       status = bot_cmd_movetotarget(this);
-                       break;
-               case BOT_CMD_RESETGOAL:
-                       status = bot_cmd_resetgoal(this);
-                       break;
-               case BOT_CMD_CC:
-                       status = bot_cmd_cc(this);
-                       break;
-               case BOT_CMD_IF:
-                       status = bot_cmd_if(this);
-                       break;
-               case BOT_CMD_ELSE:
-                       status = bot_cmd_else(this);
-                       break;
-               case BOT_CMD_FI:
-                       status = bot_cmd_fi(this);
-                       break;
-               case BOT_CMD_RESETAIM:
-                       status = bot_cmd_resetaim(this);
-                       break;
-               case BOT_CMD_AIM:
-                       status = bot_cmd_aim(this);
-                       break;
-               case BOT_CMD_AIMTARGET:
-                       status = bot_cmd_aimtarget(this);
-                       break;
-               case BOT_CMD_PRESSKEY:
-                       status = bot_cmd_presskey(this);
-                       break;
-               case BOT_CMD_RELEASEKEY:
-                       status = bot_cmd_releasekey(this);
-                       break;
-               case BOT_CMD_SELECTWEAPON:
-                       status = bot_cmd_select_weapon(this);
-                       break;
-               case BOT_CMD_IMPULSE:
-                       status = bot_cmd_impulse(this);
-                       break;
-               case BOT_CMD_BARRIER:
-                       status = bot_cmd_barrier(this);
-                       break;
-               case BOT_CMD_CONSOLE:
-                       localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
-                       status = CMD_STATUS_FINISHED;
-                       break;
-               case BOT_CMD_SOUND:
-                       status = bot_cmd_sound(this);
-                       break;
-               case BOT_CMD_DEBUG_ASSERT_CANFIRE:
-                       status = bot_cmd_debug_assert_canfire(this);
-                       break;
-               default:
-                       LOG_INFO(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
-                       return 0;
-       }
-
-       if (status==CMD_STATUS_ERROR)
-               LOG_INFO(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
-
-       // Move execution pointer
-       if(status==CMD_STATUS_EXECUTING)
-       {
-               return 1;
-       }
-       else
-       {
-               if(autocvar_g_debug_bot_commands)
-               {
-                       string parms;
-
-                       switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
-                       {
-                               case BOT_CMD_PARAMETER_FLOAT:
-                                       parms = ftos(bot_cmd.bot_cmd_parm_float);
-                                       break;
-                               case BOT_CMD_PARAMETER_STRING:
-                                       parms = bot_cmd.bot_cmd_parm_string;
-                                       break;
-                               case BOT_CMD_PARAMETER_VECTOR:
-                                       parms = vtos(bot_cmd.bot_cmd_parm_vector);
-                                       break;
-                               default:
-                                       parms = "";
-                                       break;
-                       }
-                       clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
-               }
-
-               bot_command_executed(this, true);
-       }
-
-       if(status == CMD_STATUS_FINISHED)
-               return -1;
-
-       return CMD_STATUS_ERROR;
-}
-
-// This function should be (the only) called directly from the bot ai loop
-int bot_execute_commands(entity this)
-{
-       int f;
-       do
-       {
-               f = bot_execute_commands_once(this);
-       }
-       while(f < 0);
-       return f;
-}
diff --git a/qcsrc/server/bot/scripting.qh b/qcsrc/server/bot/scripting.qh
deleted file mode 100644 (file)
index cb6cd87..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-#define BOT_EXEC_STATUS_IDLE 0
-#define BOT_EXEC_STATUS_PAUSED 1
-#define BOT_EXEC_STATUS_WAITING 2
-
-#define CMD_STATUS_EXECUTING 0
-#define CMD_STATUS_FINISHED 1
-#define CMD_STATUS_ERROR 2
-
-
-// NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
-const int BOT_CMD_NULL                         = 0;
-const int BOT_CMD_PAUSE                = 1;
-const int BOT_CMD_CONTINUE             = 2;
-const int BOT_CMD_WAIT                         = 3;
-const int BOT_CMD_TURN                         = 4;
-const int BOT_CMD_MOVETO               = 5;
-const int BOT_CMD_RESETGOAL    = 6;    // Not implemented yet
-const int BOT_CMD_CC                   = 7;
-const int BOT_CMD_IF                   = 8;
-const int BOT_CMD_ELSE                         = 9;
-const int BOT_CMD_FI                   = 10;
-const int BOT_CMD_RESETAIM             = 11;
-const int BOT_CMD_AIM                  = 12;
-const int BOT_CMD_PRESSKEY             = 13;
-const int BOT_CMD_RELEASEKEY   = 14;
-const int BOT_CMD_SELECTWEAPON         = 15;
-const int BOT_CMD_IMPULSE              = 16;
-const int BOT_CMD_WAIT_UNTIL   = 17;
-const int BOT_CMD_MOVETOTARGET         = 18;
-const int BOT_CMD_AIMTARGET    = 19;
-const int BOT_CMD_BARRIER              = 20;
-const int BOT_CMD_CONSOLE              = 21;
-const int BOT_CMD_SOUND                = 22;
-const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23;
-const int BOT_CMD_WHILE                = 24;   // TODO: Not implemented yet
-const int BOT_CMD_WEND                         = 25;   // TODO: Not implemented yet
-const int BOT_CMD_CHASE                = 26;   // TODO: Not implemented yet
-
-const int BOT_CMD_COUNTER              = 24;   // Update this value if you add/remove a command
-
-// NOTE: Following commands should be implemented on the bot ai
-//              If a new command should be handled by the target ai(s) please declare it here
-.float(entity, vector) cmd_moveto;
-.float(entity) cmd_resetgoal;
-
-//
-const int BOT_CMD_PARAMETER_NONE = 0;
-const int BOT_CMD_PARAMETER_FLOAT = 1;
-const int BOT_CMD_PARAMETER_STRING = 2;
-const int BOT_CMD_PARAMETER_VECTOR = 3;
-
-float bot_cmds_initialized;
-int bot_cmd_parm_type[BOT_CMD_COUNTER];
-string bot_cmd_string[BOT_CMD_COUNTER];
-
-// Bots command queue
-entity bot_cmd;        // global current command
-.entity bot_cmd_current; // current command of this bot
-
-.float is_bot_cmd;                     // Tells if the entity is a bot command
-.float bot_cmd_index;                  // Position of the command in the queue
-.int bot_cmd_type;                     // If of command (see the BOT_CMD_* defines)
-.float bot_cmd_parm_float;             // Field to store a float parameter
-.string bot_cmd_parm_string;           // Field to store a string parameter
-.vector bot_cmd_parm_vector;           // Field to store a vector parameter
-
-float bot_barriertime;
-.float bot_barrier;
-
-.float bot_cmd_execution_index;                // Position in the queue of the command to be executed
-
-
-void bot_resetqueues();
-void bot_queuecommand(entity bot, string cmdstring);
-void bot_cmdhelp(string scmd);
-void bot_list_commands();
-float bot_execute_commands(entity this);
-entity find_bot_by_name(string name);
-entity find_bot_by_number(float number);
diff --git a/qcsrc/server/bot/waypoints.qc b/qcsrc/server/bot/waypoints.qc
deleted file mode 100644 (file)
index e8322c2..0000000
+++ /dev/null
@@ -1,1146 +0,0 @@
-#include "waypoints.qh"
-
-#include "bot.qh"
-#include "navigation.qh"
-
-#include <common/state.qh>
-
-#include "../antilag.qh"
-
-#include <common/constants.qh>
-
-#include <lib/warpzone/common.qh>
-#include <lib/warpzone/util_server.qh>
-
-// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
-// them back to it as well
-// (suitable for spawnfunc_waypoint editor)
-entity waypoint_spawn(vector m1, vector m2, float f)
-{
-       entity w;
-       w = find(NULL, classname, "waypoint");
-
-       if (!(f & WAYPOINTFLAG_PERSONAL))
-       while (w)
-       {
-               // if a matching spawnfunc_waypoint already exists, don't add a duplicate
-               if (boxesoverlap(m1, m2, w.absmin, w.absmax))
-                       return w;
-               w = find(w, classname, "waypoint");
-       }
-
-       w = new(waypoint);
-       w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
-       w.wpflags = f;
-       w.solid = SOLID_TRIGGER;
-       setorigin(w, (m1 + m2) * 0.5);
-       setsize(w, m1 - w.origin, m2 - w.origin);
-       if (vlen(w.size) > 0)
-               w.wpisbox = true;
-
-       if(!w.wpisbox)
-       {
-               setsize(w, STAT(PL_MIN, NULL) - '1 1 0', STAT(PL_MAX, NULL) + '1 1 0');
-               if(!move_out_of_solid(w))
-               {
-                       if(!(f & WAYPOINTFLAG_GENERATED))
-                       {
-                               LOG_TRACE("Killed a waypoint that was stuck in solid at ", vtos(w.origin), "\n");
-                               remove(w);
-                               return NULL;
-                       }
-                       else
-                       {
-                               if(autocvar_developer)
-                               {
-                                       LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n");
-                                       backtrace("Waypoint stuck");
-                               }
-                       }
-               }
-               setsize(w, '0 0 0', '0 0 0');
-       }
-
-       waypoint_clearlinks(w);
-       //waypoint_schedulerelink(w);
-
-       if (autocvar_g_waypointeditor)
-       {
-               m1 = w.mins;
-               m2 = w.maxs;
-               setmodel(w, MDL_WAYPOINT); w.effects = EF_LOWPRECISION;
-               setsize(w, m1, m2);
-               if (w.wpflags & WAYPOINTFLAG_ITEM)
-                       w.colormod = '1 0 0';
-               else if (w.wpflags & WAYPOINTFLAG_GENERATED)
-                       w.colormod = '1 1 0';
-               else
-                       w.colormod = '1 1 1';
-       }
-       else
-               w.model = "";
-
-       return w;
-}
-
-// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has
-void waypoint_addlink(entity from, entity to)
-{
-       float c;
-
-       if (from == to)
-               return;
-       if (from.wpflags & WAYPOINTFLAG_NORELINK)
-               return;
-
-       if (from.wp00 == to) return;if (from.wp01 == to) return;if (from.wp02 == to) return;if (from.wp03 == to) return;
-       if (from.wp04 == to) return;if (from.wp05 == to) return;if (from.wp06 == to) return;if (from.wp07 == to) return;
-       if (from.wp08 == to) return;if (from.wp09 == to) return;if (from.wp10 == to) return;if (from.wp11 == to) return;
-       if (from.wp12 == to) return;if (from.wp13 == to) return;if (from.wp14 == to) return;if (from.wp15 == to) return;
-       if (from.wp16 == to) return;if (from.wp17 == to) return;if (from.wp18 == to) return;if (from.wp19 == to) return;
-       if (from.wp20 == to) return;if (from.wp21 == to) return;if (from.wp22 == to) return;if (from.wp23 == to) return;
-       if (from.wp24 == to) return;if (from.wp25 == to) return;if (from.wp26 == to) return;if (from.wp27 == to) return;
-       if (from.wp28 == to) return;if (from.wp29 == to) return;if (from.wp30 == to) return;if (from.wp31 == to) return;
-
-       if (to.wpisbox || from.wpisbox)
-       {
-               // if either is a box we have to find the nearest points on them to
-               // calculate the distance properly
-               vector v1, v2, m1, m2;
-               v1 = from.origin;
-               m1 = to.absmin;
-               m2 = to.absmax;
-               v1_x = bound(m1_x, v1_x, m2_x);
-               v1_y = bound(m1_y, v1_y, m2_y);
-               v1_z = bound(m1_z, v1_z, m2_z);
-               v2 = to.origin;
-               m1 = from.absmin;
-               m2 = from.absmax;
-               v2_x = bound(m1_x, v2_x, m2_x);
-               v2_y = bound(m1_y, v2_y, m2_y);
-               v2_z = bound(m1_z, v2_z, m2_z);
-               v2 = to.origin;
-               c = vlen(v2 - v1);
-       }
-       else
-               c = vlen(to.origin - from.origin);
-
-       if (from.wp31mincost < c) return;
-       if (from.wp30mincost < c) {from.wp31 = to;from.wp31mincost = c;return;} from.wp31 = from.wp30;from.wp31mincost = from.wp30mincost;
-       if (from.wp29mincost < c) {from.wp30 = to;from.wp30mincost = c;return;} from.wp30 = from.wp29;from.wp30mincost = from.wp29mincost;
-       if (from.wp28mincost < c) {from.wp29 = to;from.wp29mincost = c;return;} from.wp29 = from.wp28;from.wp29mincost = from.wp28mincost;
-       if (from.wp27mincost < c) {from.wp28 = to;from.wp28mincost = c;return;} from.wp28 = from.wp27;from.wp28mincost = from.wp27mincost;
-       if (from.wp26mincost < c) {from.wp27 = to;from.wp27mincost = c;return;} from.wp27 = from.wp26;from.wp27mincost = from.wp26mincost;
-       if (from.wp25mincost < c) {from.wp26 = to;from.wp26mincost = c;return;} from.wp26 = from.wp25;from.wp26mincost = from.wp25mincost;
-       if (from.wp24mincost < c) {from.wp25 = to;from.wp25mincost = c;return;} from.wp25 = from.wp24;from.wp25mincost = from.wp24mincost;
-       if (from.wp23mincost < c) {from.wp24 = to;from.wp24mincost = c;return;} from.wp24 = from.wp23;from.wp24mincost = from.wp23mincost;
-       if (from.wp22mincost < c) {from.wp23 = to;from.wp23mincost = c;return;} from.wp23 = from.wp22;from.wp23mincost = from.wp22mincost;
-       if (from.wp21mincost < c) {from.wp22 = to;from.wp22mincost = c;return;} from.wp22 = from.wp21;from.wp22mincost = from.wp21mincost;
-       if (from.wp20mincost < c) {from.wp21 = to;from.wp21mincost = c;return;} from.wp21 = from.wp20;from.wp21mincost = from.wp20mincost;
-       if (from.wp19mincost < c) {from.wp20 = to;from.wp20mincost = c;return;} from.wp20 = from.wp19;from.wp20mincost = from.wp19mincost;
-       if (from.wp18mincost < c) {from.wp19 = to;from.wp19mincost = c;return;} from.wp19 = from.wp18;from.wp19mincost = from.wp18mincost;
-       if (from.wp17mincost < c) {from.wp18 = to;from.wp18mincost = c;return;} from.wp18 = from.wp17;from.wp18mincost = from.wp17mincost;
-       if (from.wp16mincost < c) {from.wp17 = to;from.wp17mincost = c;return;} from.wp17 = from.wp16;from.wp17mincost = from.wp16mincost;
-       if (from.wp15mincost < c) {from.wp16 = to;from.wp16mincost = c;return;} from.wp16 = from.wp15;from.wp16mincost = from.wp15mincost;
-       if (from.wp14mincost < c) {from.wp15 = to;from.wp15mincost = c;return;} from.wp15 = from.wp14;from.wp15mincost = from.wp14mincost;
-       if (from.wp13mincost < c) {from.wp14 = to;from.wp14mincost = c;return;} from.wp14 = from.wp13;from.wp14mincost = from.wp13mincost;
-       if (from.wp12mincost < c) {from.wp13 = to;from.wp13mincost = c;return;} from.wp13 = from.wp12;from.wp13mincost = from.wp12mincost;
-       if (from.wp11mincost < c) {from.wp12 = to;from.wp12mincost = c;return;} from.wp12 = from.wp11;from.wp12mincost = from.wp11mincost;
-       if (from.wp10mincost < c) {from.wp11 = to;from.wp11mincost = c;return;} from.wp11 = from.wp10;from.wp11mincost = from.wp10mincost;
-       if (from.wp09mincost < c) {from.wp10 = to;from.wp10mincost = c;return;} from.wp10 = from.wp09;from.wp10mincost = from.wp09mincost;
-       if (from.wp08mincost < c) {from.wp09 = to;from.wp09mincost = c;return;} from.wp09 = from.wp08;from.wp09mincost = from.wp08mincost;
-       if (from.wp07mincost < c) {from.wp08 = to;from.wp08mincost = c;return;} from.wp08 = from.wp07;from.wp08mincost = from.wp07mincost;
-       if (from.wp06mincost < c) {from.wp07 = to;from.wp07mincost = c;return;} from.wp07 = from.wp06;from.wp07mincost = from.wp06mincost;
-       if (from.wp05mincost < c) {from.wp06 = to;from.wp06mincost = c;return;} from.wp06 = from.wp05;from.wp06mincost = from.wp05mincost;
-       if (from.wp04mincost < c) {from.wp05 = to;from.wp05mincost = c;return;} from.wp05 = from.wp04;from.wp05mincost = from.wp04mincost;
-       if (from.wp03mincost < c) {from.wp04 = to;from.wp04mincost = c;return;} from.wp04 = from.wp03;from.wp04mincost = from.wp03mincost;
-       if (from.wp02mincost < c) {from.wp03 = to;from.wp03mincost = c;return;} from.wp03 = from.wp02;from.wp03mincost = from.wp02mincost;
-       if (from.wp01mincost < c) {from.wp02 = to;from.wp02mincost = c;return;} from.wp02 = from.wp01;from.wp02mincost = from.wp01mincost;
-       if (from.wp00mincost < c) {from.wp01 = to;from.wp01mincost = c;return;} from.wp01 = from.wp00;from.wp01mincost = from.wp00mincost;
-       from.wp00 = to;from.wp00mincost = c;return;
-}
-
-// relink this spawnfunc_waypoint
-// (precompile a list of all reachable waypoints from this spawnfunc_waypoint)
-// (SLOW!)
-void waypoint_think(entity this)
-{
-       entity e;
-       vector sv, sm1, sm2, ev, em1, em2, dv;
-
-       bot_calculate_stepheightvec();
-
-       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
-
-       //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
-       sm1 = this.origin + this.mins;
-       sm2 = this.origin + this.maxs;
-       for(e = NULL; (e = find(e, classname, "waypoint")); )
-       {
-               if (boxesoverlap(this.absmin, this.absmax, e.absmin, e.absmax))
-               {
-                       waypoint_addlink(this, e);
-                       waypoint_addlink(e, this);
-               }
-               else
-               {
-                       ++relink_total;
-                       if(!checkpvs(this.origin, e))
-                       {
-                               ++relink_pvsculled;
-                               continue;
-                       }
-                       sv = e.origin;
-                       sv.x = bound(sm1_x, sv.x, sm2_x);
-                       sv.y = bound(sm1_y, sv.y, sm2_y);
-                       sv.z = bound(sm1_z, sv.z, sm2_z);
-                       ev = this.origin;
-                       em1 = e.origin + e.mins;
-                       em2 = e.origin + e.maxs;
-                       ev.x = bound(em1_x, ev.x, em2_x);
-                       ev.y = bound(em1_y, ev.y, em2_y);
-                       ev.z = bound(em1_z, ev.z, em2_z);
-                       dv = ev - sv;
-                       dv.z = 0;
-                       if(vdist(dv, >=, 1050)) // max search distance in XY
-                       {
-                               ++relink_lengthculled;
-                               continue;
-                       }
-                       navigation_testtracewalk = 0;
-                       if (!this.wpisbox)
-                       {
-                               tracebox(sv - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, false, this);
-                               if (!trace_startsolid)
-                               {
-                                       //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
-                                       sv = trace_endpos + '0 0 1';
-                               }
-                       }
-                       if (!e.wpisbox)
-                       {
-                               tracebox(ev - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, false, e);
-                               if (!trace_startsolid)
-                               {
-                                       //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
-                                       ev = trace_endpos + '0 0 1';
-                               }
-                       }
-                       //traceline(this.origin, e.origin, false, NULL);
-                       //if (trace_fraction == 1)
-                       if (!this.wpisbox && tracewalk(this, sv, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, MOVE_NOMONSTERS))
-                               waypoint_addlink(this, e);
-                       else
-                               relink_walkculled += 0.5;
-                       if (!e.wpisbox && tracewalk(e, ev, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, MOVE_NOMONSTERS))
-                               waypoint_addlink(e, this);
-                       else
-                               relink_walkculled += 0.5;
-               }
-       }
-       navigation_testtracewalk = 0;
-       this.wplinked = true;
-}
-
-void waypoint_clearlinks(entity wp)
-{
-       // clear links to other waypoints
-       float f;
-       f = 10000000;
-       wp.wp00 = wp.wp01 = wp.wp02 = wp.wp03 = wp.wp04 = wp.wp05 = wp.wp06 = wp.wp07 = NULL;
-       wp.wp08 = wp.wp09 = wp.wp10 = wp.wp11 = wp.wp12 = wp.wp13 = wp.wp14 = wp.wp15 = NULL;
-       wp.wp16 = wp.wp17 = wp.wp18 = wp.wp19 = wp.wp20 = wp.wp21 = wp.wp22 = wp.wp23 = NULL;
-       wp.wp24 = wp.wp25 = wp.wp26 = wp.wp27 = wp.wp28 = wp.wp29 = wp.wp30 = wp.wp31 = NULL;
-
-       wp.wp00mincost = wp.wp01mincost = wp.wp02mincost = wp.wp03mincost = wp.wp04mincost = wp.wp05mincost = wp.wp06mincost = wp.wp07mincost = f;
-       wp.wp08mincost = wp.wp09mincost = wp.wp10mincost = wp.wp11mincost = wp.wp12mincost = wp.wp13mincost = wp.wp14mincost = wp.wp15mincost = f;
-       wp.wp16mincost = wp.wp17mincost = wp.wp18mincost = wp.wp19mincost = wp.wp20mincost = wp.wp21mincost = wp.wp22mincost = wp.wp23mincost = f;
-       wp.wp24mincost = wp.wp25mincost = wp.wp26mincost = wp.wp27mincost = wp.wp28mincost = wp.wp29mincost = wp.wp30mincost = wp.wp31mincost = f;
-
-       wp.wplinked = false;
-}
-
-// tell a spawnfunc_waypoint to relink
-void waypoint_schedulerelink(entity wp)
-{
-       if (wp == NULL)
-               return;
-       // TODO: add some sort of visible box in edit mode for box waypoints
-       if (autocvar_g_waypointeditor)
-       {
-               vector m1, m2;
-               m1 = wp.mins;
-               m2 = wp.maxs;
-               setmodel(wp, MDL_WAYPOINT); wp.effects = EF_LOWPRECISION;
-               setsize(wp, m1, m2);
-               if (wp.wpflags & WAYPOINTFLAG_ITEM)
-                       wp.colormod = '1 0 0';
-               else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
-                       wp.colormod = '1 1 0';
-               else
-                       wp.colormod = '1 1 1';
-       }
-       else
-               wp.model = "";
-       wp.wpisbox = vlen(wp.size) > 0;
-       wp.enemy = NULL;
-       if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL))
-               wp.owner = NULL;
-       if (!(wp.wpflags & WAYPOINTFLAG_NORELINK))
-               waypoint_clearlinks(wp);
-       // schedule an actual relink on next frame
-       setthink(wp, waypoint_think);
-       wp.nextthink = time;
-       wp.effects = EF_LOWPRECISION;
-}
-
-// spawnfunc_waypoint map entity
-spawnfunc(waypoint)
-{
-       setorigin(this, this.origin);
-       // schedule a relink after other waypoints have had a chance to spawn
-       waypoint_clearlinks(this);
-       //waypoint_schedulerelink(this);
-}
-
-// remove a spawnfunc_waypoint, and schedule all neighbors to relink
-void waypoint_remove(entity e)
-{
-       // tell all linked waypoints that they need to relink
-       waypoint_schedulerelink(e.wp00);
-       waypoint_schedulerelink(e.wp01);
-       waypoint_schedulerelink(e.wp02);
-       waypoint_schedulerelink(e.wp03);
-       waypoint_schedulerelink(e.wp04);
-       waypoint_schedulerelink(e.wp05);
-       waypoint_schedulerelink(e.wp06);
-       waypoint_schedulerelink(e.wp07);
-       waypoint_schedulerelink(e.wp08);
-       waypoint_schedulerelink(e.wp09);
-       waypoint_schedulerelink(e.wp10);
-       waypoint_schedulerelink(e.wp11);
-       waypoint_schedulerelink(e.wp12);
-       waypoint_schedulerelink(e.wp13);
-       waypoint_schedulerelink(e.wp14);
-       waypoint_schedulerelink(e.wp15);
-       waypoint_schedulerelink(e.wp16);
-       waypoint_schedulerelink(e.wp17);
-       waypoint_schedulerelink(e.wp18);
-       waypoint_schedulerelink(e.wp19);
-       waypoint_schedulerelink(e.wp20);
-       waypoint_schedulerelink(e.wp21);
-       waypoint_schedulerelink(e.wp22);
-       waypoint_schedulerelink(e.wp23);
-       waypoint_schedulerelink(e.wp24);
-       waypoint_schedulerelink(e.wp25);
-       waypoint_schedulerelink(e.wp26);
-       waypoint_schedulerelink(e.wp27);
-       waypoint_schedulerelink(e.wp28);
-       waypoint_schedulerelink(e.wp29);
-       waypoint_schedulerelink(e.wp30);
-       waypoint_schedulerelink(e.wp31);
-       // and now remove the spawnfunc_waypoint
-       remove(e);
-}
-
-// empties the map of waypoints
-void waypoint_removeall()
-{
-       entity head, next;
-       head = findchain(classname, "waypoint");
-       while (head)
-       {
-               next = head.chain;
-               remove(head);
-               head = next;
-       }
-}
-
-// tell all waypoints to relink
-// (is this useful at all?)
-void waypoint_schedulerelinkall()
-{
-       entity head;
-       relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0;
-       head = findchain(classname, "waypoint");
-       while (head)
-       {
-               waypoint_schedulerelink(head);
-               head = head.chain;
-       }
-}
-
-// Load waypoint links from file
-float waypoint_load_links()
-{
-       string filename, s;
-       float file, tokens, c = 0, found;
-       entity wp_from = NULL, wp_to;
-       vector wp_to_pos, wp_from_pos;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.cache");
-       file = fopen(filename, FILE_READ);
-
-       if (file < 0)
-       {
-               LOG_TRACE("waypoint links load from ");
-               LOG_TRACE(filename);
-               LOG_TRACE(" failed\n");
-               return false;
-       }
-
-       while ((s = fgets(file)))
-       {
-               tokens = tokenizebyseparator(s, "*");
-
-               if (tokens!=2)
-               {
-                       // bad file format
-                       fclose(file);
-                       return false;
-               }
-
-               wp_from_pos     = stov(argv(0));
-               wp_to_pos       = stov(argv(1));
-
-               // Search "from" waypoint
-               if(!wp_from || wp_from.origin!=wp_from_pos)
-               {
-                       wp_from = findradius(wp_from_pos, 1);
-                       found = false;
-                       while(wp_from)
-                       {
-                               if(vdist(wp_from.origin - wp_from_pos, <, 1))
-                               if(wp_from.classname == "waypoint")
-                               {
-                                       found = true;
-                                       break;
-                               }
-                               wp_from = wp_from.chain;
-                       }
-
-                       if(!found)
-                       {
-                               LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from.origin),"\n");
-                               continue;
-                       }
-
-               }
-
-               // Search "to" waypoint
-               wp_to = findradius(wp_to_pos, 1);
-               found = false;
-               while(wp_to)
-               {
-                       if(vdist(wp_to.origin - wp_to_pos, <, 1))
-                       if(wp_to.classname == "waypoint")
-                       {
-                               found = true;
-                               break;
-                       }
-                       wp_to = wp_to.chain;
-               }
-
-               if(!found)
-               {
-                       LOG_TRACE("waypoint_load_links: couldn't find 'to' waypoint at ", vtos(wp_to.origin),"\n");
-                       continue;
-               }
-
-               ++c;
-               waypoint_addlink(wp_from, wp_to);
-       }
-
-       fclose(file);
-
-       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache\n");
-
-       botframe_cachedwaypointlinks = true;
-       return true;
-}
-
-void waypoint_load_links_hardwired()
-{
-       string filename, s;
-       float file, tokens, c = 0, found;
-       entity wp_from = NULL, wp_to;
-       vector wp_to_pos, wp_from_pos;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.hardwired");
-       file = fopen(filename, FILE_READ);
-
-       botframe_loadedforcedlinks = true;
-
-       if (file < 0)
-       {
-               LOG_TRACE("waypoint links load from ", filename, " failed\n");
-               return;
-       }
-
-       while ((s = fgets(file)))
-       {
-               if(substring(s, 0, 2)=="//")
-                       continue;
-
-               if(substring(s, 0, 1)=="#")
-                       continue;
-
-               tokens = tokenizebyseparator(s, "*");
-
-               if (tokens!=2)
-                       continue;
-
-               wp_from_pos     = stov(argv(0));
-               wp_to_pos       = stov(argv(1));
-
-               // Search "from" waypoint
-               if(!wp_from || wp_from.origin!=wp_from_pos)
-               {
-                       wp_from = findradius(wp_from_pos, 5);
-                       found = false;
-                       while(wp_from)
-                       {
-                               if(vdist(wp_from.origin - wp_from_pos, <, 5))
-                               if(wp_from.classname == "waypoint")
-                               {
-                                       found = true;
-                                       break;
-                               }
-                               wp_from = wp_from.chain;
-                       }
-
-                       if(!found)
-                       {
-                               LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n"));
-                               continue;
-                       }
-               }
-
-               // Search "to" waypoint
-               wp_to = findradius(wp_to_pos, 5);
-               found = false;
-               while(wp_to)
-               {
-                       if(vdist(wp_to.origin - wp_to_pos, <, 5))
-                       if(wp_to.classname == "waypoint")
-                       {
-                               found = true;
-                               break;
-                       }
-                       wp_to = wp_to.chain;
-               }
-
-               if(!found)
-               {
-                       LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n"));
-                       continue;
-               }
-
-               ++c;
-               waypoint_addlink(wp_from, wp_to);
-               wp_from.wphardwired = true;
-               wp_to.wphardwired = true;
-       }
-
-       fclose(file);
-
-       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired\n");
-}
-
-entity waypoint_get_link(entity w, float i)
-{
-       switch(i)
-       {
-               case  0:return w.wp00;
-               case  1:return w.wp01;
-               case  2:return w.wp02;
-               case  3:return w.wp03;
-               case  4:return w.wp04;
-               case  5:return w.wp05;
-               case  6:return w.wp06;
-               case  7:return w.wp07;
-               case  8:return w.wp08;
-               case  9:return w.wp09;
-               case 10:return w.wp10;
-               case 11:return w.wp11;
-               case 12:return w.wp12;
-               case 13:return w.wp13;
-               case 14:return w.wp14;
-               case 15:return w.wp15;
-               case 16:return w.wp16;
-               case 17:return w.wp17;
-               case 18:return w.wp18;
-               case 19:return w.wp19;
-               case 20:return w.wp20;
-               case 21:return w.wp21;
-               case 22:return w.wp22;
-               case 23:return w.wp23;
-               case 24:return w.wp24;
-               case 25:return w.wp25;
-               case 26:return w.wp26;
-               case 27:return w.wp27;
-               case 28:return w.wp28;
-               case 29:return w.wp29;
-               case 30:return w.wp30;
-               case 31:return w.wp31;
-               default:return NULL;
-       }
-}
-
-// Save all waypoint links to a file
-void waypoint_save_links()
-{
-       string filename, s;
-       float file, c, i;
-       entity w, link;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.cache");
-       file = fopen(filename, FILE_WRITE);
-       if (file < 0)
-       {
-               LOG_INFO("waypoint links save to ");
-               LOG_INFO(filename);
-               LOG_INFO(" failed\n");
-       }
-       c = 0;
-       w = findchain(classname, "waypoint");
-       while (w)
-       {
-               for(i=0;i<32;++i)
-               {
-                       // :S
-                       link = waypoint_get_link(w, i);
-                       if(link==NULL)
-                               continue;
-
-                       s = strcat(vtos(w.origin), "*", vtos(link.origin), "\n");
-                       fputs(file, s);
-                       ++c;
-               }
-               w = w.chain;
-       }
-       fclose(file);
-       botframe_cachedwaypointlinks = true;
-
-       LOG_INFO("saved ");
-       LOG_INFO(ftos(c));
-       LOG_INFO(" waypoints links to maps/");
-       LOG_INFO(mapname);
-       LOG_INFO(".waypoints.cache\n");
-}
-
-// save waypoints to gamedir/data/maps/mapname.waypoints
-void waypoint_saveall()
-{
-       string filename, s;
-       float file, c;
-       entity w;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints");
-       file = fopen(filename, FILE_WRITE);
-       if (file >= 0)
-       {
-               c = 0;
-               w = findchain(classname, "waypoint");
-               while (w)
-               {
-                       if (!(w.wpflags & WAYPOINTFLAG_GENERATED))
-                       {
-                               s = strcat(vtos(w.origin + w.mins), "\n");
-                               s = strcat(s, vtos(w.origin + w.maxs));
-                               s = strcat(s, "\n");
-                               s = strcat(s, ftos(w.wpflags));
-                               s = strcat(s, "\n");
-                               fputs(file, s);
-                               c = c + 1;
-                       }
-                       w = w.chain;
-               }
-               fclose(file);
-               bprint("saved ");
-               bprint(ftos(c));
-               bprint(" waypoints to maps/");
-               bprint(mapname);
-               bprint(".waypoints\n");
-       }
-       else
-       {
-               bprint("waypoint save to ");
-               bprint(filename);
-               bprint(" failed\n");
-       }
-       waypoint_save_links();
-       botframe_loadedforcedlinks = false;
-}
-
-// load waypoints from file
-float waypoint_loadall()
-{
-       string filename, s;
-       float file, cwp, cwb, fl;
-       vector m1, m2;
-       cwp = 0;
-       cwb = 0;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints");
-       file = fopen(filename, FILE_READ);
-       if (file >= 0)
-       {
-               while ((s = fgets(file)))
-               {
-                       m1 = stov(s);
-                       s = fgets(file);
-                       if (!s)
-                               break;
-                       m2 = stov(s);
-                       s = fgets(file);
-                       if (!s)
-                               break;
-                       fl = stof(s);
-                       waypoint_spawn(m1, m2, fl);
-                       if (m1 == m2)
-                               cwp = cwp + 1;
-                       else
-                               cwb = cwb + 1;
-               }
-               fclose(file);
-               LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints\n");
-       }
-       else
-       {
-               LOG_TRACE("waypoint load from ", filename, " failed\n");
-       }
-       return cwp + cwb;
-}
-
-vector waypoint_fixorigin(vector position)
-{
-       tracebox(position + '0 0 1' * (1 - STAT(PL_MIN, NULL).z), STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), position + '0 0 -512', MOVE_NOMONSTERS, NULL);
-       if(trace_fraction < 1)
-               position = trace_endpos;
-       //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
-       //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n");
-       return position;
-}
-
-void waypoint_spawnforitem_force(entity e, vector org)
-{
-       entity w;
-
-       // Fix the waypoint altitude if necessary
-       org = waypoint_fixorigin(org);
-
-       // don't spawn an item spawnfunc_waypoint if it already exists
-       w = findchain(classname, "waypoint");
-       while (w)
-       {
-               if (w.wpisbox)
-               {
-                       if (boxesoverlap(org, org, w.absmin, w.absmax))
-                       {
-                               e.nearestwaypoint = w;
-                               return;
-                       }
-               }
-               else
-               {
-                       if (vlen(w.origin - org) < 16)
-                       {
-                               e.nearestwaypoint = w;
-                               return;
-                       }
-               }
-               w = w.chain;
-       }
-       e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM);
-}
-
-void waypoint_spawnforitem(entity e)
-{
-       if(!bot_waypoints_for_items)
-               return;
-
-       waypoint_spawnforitem_force(e, e.origin);
-}
-
-void waypoint_spawnforteleporter_boxes(entity e, vector org1, vector org2, vector destination1, vector destination2, float timetaken)
-{
-       entity w;
-       entity dw;
-       w = waypoint_spawn(org1, org2, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK);
-       dw = waypoint_spawn(destination1, destination2, WAYPOINTFLAG_GENERATED);
-       // one way link to the destination
-       w.wp00 = dw;
-       w.wp00mincost = timetaken; // this is just for jump pads
-       // the teleporter's nearest spawnfunc_waypoint is this one
-       // (teleporters are not goals, so this is probably useless)
-       e.nearestwaypoint = w;
-       e.nearestwaypointtimeout = time + 1000000000;
-}
-
-void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken)
-{
-       org = waypoint_fixorigin(org);
-       destination = waypoint_fixorigin(destination);
-       waypoint_spawnforteleporter_boxes(e, org, org, destination, destination, timetaken);
-}
-
-void waypoint_spawnforteleporter(entity e, vector destination, float timetaken)
-{
-       destination = waypoint_fixorigin(destination);
-       waypoint_spawnforteleporter_boxes(e, e.absmin, e.absmax, destination, destination, timetaken);
-}
-
-entity waypoint_spawnpersonal(entity this, vector position)
-{
-       entity w;
-
-       // drop the waypoint to a proper location:
-       //   first move it up by a player height
-       //   then move it down to hit the floor with player bbox size
-       position = waypoint_fixorigin(position);
-
-       w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL);
-       w.nearestwaypoint = NULL;
-       w.nearestwaypointtimeout = 0;
-       w.owner = this;
-
-       waypoint_schedulerelink(w);
-
-       return w;
-}
-
-void botframe_showwaypointlinks()
-{
-       entity head, w;
-       if (time < botframe_waypointeditorlightningtime)
-               return;
-       botframe_waypointeditorlightningtime = time + 0.5;
-       FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot, LAMBDA(
-               if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
-               {
-                       //navigation_testtracewalk = true;
-                       head = navigation_findnearestwaypoint(it, false);
-               //      print("currently selected WP is ", etos(head), "\n");
-                       //navigation_testtracewalk = false;
-                       if (head)
-                       {
-                               w = head     ;if (w) te_lightning2(NULL, w.origin, it.origin);
-                               w = head.wp00;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp01;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp02;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp03;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp04;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp05;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp06;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp07;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp08;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp09;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp10;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp11;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp12;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp13;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp14;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp15;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp16;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp17;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp18;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp19;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp20;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp21;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp22;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp23;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp24;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp25;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp26;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp27;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp28;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp29;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp30;if (w) te_lightning2(NULL, w.origin, head.origin);
-                               w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin);
-                       }
-               }
-       ));
-}
-
-float botframe_autowaypoints_fixdown(vector v)
-{
-       tracebox(v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v + '0 0 -64', MOVE_NOMONSTERS, NULL);
-       if(trace_fraction >= 1)
-               return 0;
-       return 1;
-}
-
-float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f)
-{
-       entity w;
-
-       w = find(NULL, classname, "waypoint");
-       while (w)
-       {
-               // if a matching spawnfunc_waypoint already exists, don't add a duplicate
-               if (boxesoverlap(v - '32 32 32', v + '32 32 32', w.absmin, w.absmax))
-               //if (boxesoverlap(v - '4 4 4', v + '4 4 4', w.absmin, w.absmax))
-                       return 0;
-               w = find(w, classname, "waypoint");
-       }
-
-       waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f));
-       return 1;
-}
-
-// return value:
-//    1 = WP created
-//    0 = no action needed
-//   -1 = temp fail, try from world too
-//   -2 = permanent fail, do not retry
-float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .entity fld)
-{
-       // make it possible to go from p to wp, if we can
-       // if wp is NULL, nearest is chosen
-
-       entity w;
-       vector porg;
-       float t, tmin, tmax;
-       vector o;
-       vector save;
-
-       if(!botframe_autowaypoints_fixdown(p.origin))
-               return -2;
-       porg = trace_endpos;
-
-       if(wp)
-       {
-               // if any WP w fulfills wp -> w -> porg and w is closer than wp, then switch from wp to w
-
-               // if wp -> porg, then OK
-               float maxdist;
-               if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050))
-               {
-                       // we may find a better one
-                       maxdist = vlen(wp.origin - porg);
-               }
-               else
-               {
-                       // accept any "good"
-                       maxdist = 2100;
-               }
-
-               float bestdist;
-               bestdist = maxdist;
-               w = find(NULL, classname, "waypoint");
-               while (w)
-               {
-                       if(w != wp && !(w.wpflags & WAYPOINTFLAG_NORELINK))
-                       {
-                               float d;
-                               d = vlen(wp.origin - w.origin) + vlen(w.origin - porg);
-                               if(d < bestdist)
-                                       if(navigation_waypoint_will_link(wp.origin, w.origin, p, walkfromwp, 1050))
-                                               if(navigation_waypoint_will_link(w.origin, porg, p, walkfromwp, 1050))
-                                               {
-                                                       bestdist = d;
-                                                       p.(fld) = w;
-                                               }
-                       }
-                       w = find(w, classname, "waypoint");
-               }
-               if(bestdist < maxdist)
-               {
-                       LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
-                       return 0;
-               }
-
-               if(bestdist < 2100)
-               {
-                       // we know maxdist < 2100
-                       // so wp -> porg is still valid
-                       // all is good
-                       p.(fld) = wp;
-                       return 0;
-               }
-
-               // otherwise, no existing WP can fix our issues
-       }
-       else
-       {
-               save = p.origin;
-               setorigin(p, porg);
-               w = navigation_findnearestwaypoint(p, walkfromwp);
-               setorigin(p, save);
-               if(w)
-               {
-                       p.(fld) = w;
-                       return 0;
-               }
-       }
-
-       tmin = 0;
-       tmax = 1;
-       for (;;)
-       {
-               if(tmax - tmin < 0.001)
-               {
-                       // did not get a good candidate
-                       return -1;
-               }
-
-               t = (tmin + tmax) * 0.5;
-               o = antilag_takebackorigin(p, CS(p), time - t);
-               if(!botframe_autowaypoints_fixdown(o))
-                       return -2;
-               o = trace_endpos;
-
-               if(wp)
-               {
-                       if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050))
-                       {
-                               // we cannot walk from wp.origin to o
-                               // get closer to tmax
-                               tmin = t;
-                               continue;
-                       }
-               }
-               else
-               {
-                       save = p.origin;
-                       setorigin(p, o);
-                       w = navigation_findnearestwaypoint(p, walkfromwp);
-                       setorigin(p, save);
-                       if(!w)
-                       {
-                               // we cannot walk from any WP to o
-                               // get closer to tmax
-                               tmin = t;
-                               continue;
-                       }
-               }
-
-               // if we get here, o is valid regarding waypoints
-               // check if o is connected right to the player
-               // we break if it succeeds, as that means o is a good waypoint location
-               if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050))
-                       break;
-
-               // o is no good, we need to get closer to the player
-               tmax = t;
-       }
-
-       LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n");
-       botframe_autowaypoints_createwp(o, p, fld, 0);
-       return 1;
-}
-
-// automatically create missing waypoints
-.entity botframe_autowaypoints_lastwp0, botframe_autowaypoints_lastwp1;
-void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld)
-{
-       float r = botframe_autowaypoints_fix_from(p, walkfromwp, p.(fld), fld);
-       if(r != -1)
-               return;
-       r = botframe_autowaypoints_fix_from(p, walkfromwp, NULL, fld);
-       if(r != -1)
-               return;
-
-       LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n");
-       if(!botframe_autowaypoints_fixdown(p.origin))
-               return; // shouldn't happen, caught above
-       botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED);
-}
-
-void botframe_deleteuselesswaypoints()
-{
-       entity w, w1, w2;
-       float i, j, k;
-       for (w = NULL; (w = findfloat(w, bot_pickup, true)); )
-       {
-               // NOTE: this protects waypoints if they're the ONLY nearest
-               // waypoint. That's the intention.
-               navigation_findnearestwaypoint(w, false);  // Walk TO item.
-               navigation_findnearestwaypoint(w, true);  // Walk FROM item.
-       }
-       for (w = NULL; (w = find(w, classname, "waypoint")); )
-       {
-               w.wpflags |= WAYPOINTFLAG_DEAD_END;
-               w.wpflags &= ~WAYPOINTFLAG_USEFUL;
-               // WP is useful if:
-               if (w.wpflags & WAYPOINTFLAG_ITEM)
-                       w.wpflags |= WAYPOINTFLAG_USEFUL;
-               if (w.wpflags & WAYPOINTFLAG_TELEPORT)
-                       w.wpflags |= WAYPOINTFLAG_USEFUL;
-               if (w.wpflags & WAYPOINTFLAG_PROTECTED)
-                       w.wpflags |= WAYPOINTFLAG_USEFUL;
-               // b) WP is closest WP for an item/spawnpoint/other entity
-               //    This has been done above by protecting these WPs.
-       }
-       // c) There are w1, w, w2 so that w1 -> w, w -> w2 and not w1 -> w2.
-       for (w1 = NULL; (w1 = find(w1, classname, "waypoint")); )
-       {
-               if (w1.wpflags & WAYPOINTFLAG_PERSONAL)
-                       continue;
-               for (i = 0; i < 32; ++i)
-               {
-                       w = waypoint_get_link(w1, i);
-                       if (!w)
-                               break;
-                       if (w.wpflags & WAYPOINTFLAG_PERSONAL)
-                               continue;
-                       if (w.wpflags & WAYPOINTFLAG_USEFUL)
-                               continue;
-                       for (j = 0; j < 32; ++j)
-                       {
-                               w2 = waypoint_get_link(w, j);
-                               if (!w2)
-                                       break;
-                               if (w1 == w2)
-                                       continue;
-                               if (w2.wpflags & WAYPOINTFLAG_PERSONAL)
-                                       continue;
-                               // If we got here, w1 != w2 exist with w1 -> w
-                               // and w -> w2. That means the waypoint is not
-                               // a dead end.
-                               w.wpflags &= ~WAYPOINTFLAG_DEAD_END;
-                               for (k = 0; k < 32; ++k)
-                               {
-                                       if (waypoint_get_link(w1, k) == w2)
-                                               continue;
-                                       // IF WE GET HERE, w is proven useful
-                                       // to get from w1 to w2!
-                                       w.wpflags |= WAYPOINTFLAG_USEFUL;
-                                       goto next;
-                               }
-                       }
-LABEL(next)
-               }
-       }
-       // d) The waypoint is a dead end. Dead end waypoints must be kept as
-       //     they are needed to complete routes while autowaypointing.
-
-       for (w = NULL; (w = find(w, classname, "waypoint")); )
-       {
-               if (!(w.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)))
-               {
-                       LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", w.origin);
-                       te_explosion(w.origin);
-                       waypoint_remove(w);
-                       break;
-               }
-       }
-       for (w = NULL; (w = find(w, classname, "waypoint")); )
-               w.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag
-}
-
-void botframe_autowaypoints()
-{
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA(
-               // going back is broken, so only fix waypoints to walk TO the player
-               //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0);
-               botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1);
-               //te_explosion(p.botframe_autowaypoints_lastwp0.origin);
-       ));
-
-       if (autocvar_g_waypointeditor_auto >= 2) {
-               botframe_deleteuselesswaypoints();
-       }
-}
-
diff --git a/qcsrc/server/bot/waypoints.qh b/qcsrc/server/bot/waypoints.qh
deleted file mode 100644 (file)
index 23c0fa6..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-/*
- * Globals and Fields
- */
-
-const int WAYPOINTFLAG_GENERATED = BIT(23);
-const int WAYPOINTFLAG_ITEM = BIT(22);
-const int WAYPOINTFLAG_TELEPORT = BIT(21);
-const int WAYPOINTFLAG_NORELINK = BIT(20);
-const int WAYPOINTFLAG_PERSONAL = BIT(19);
-const int WAYPOINTFLAG_PROTECTED = BIT(18);  // Useless WP detection never kills these.
-const int WAYPOINTFLAG_USEFUL = BIT(17);  // Useless WP detection temporary flag.
-const int WAYPOINTFLAG_DEAD_END = BIT(16);  // Useless WP detection temporary flag.
-
-// fields you can query using prvm_global server to get some statistics about waypoint linking culling
-float relink_total, relink_walkculled, relink_pvsculled, relink_lengthculled;
-
-float botframe_waypointeditorlightningtime;
-float botframe_loadedforcedlinks;
-float botframe_cachedwaypointlinks;
-
-.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15;
-.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31;
-
-// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance
-.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost;
-.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost;
-.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost;
-.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost;
-
-.float wpfire, wpcost, wpconsidered, wpisbox, wplinked, wphardwired;
-.int wpflags;
-
-.vector wpnearestpoint;
-
-/*
- * Functions
- */
-
-spawnfunc(waypoint);
-void waypoint_addlink(entity from, entity to);
-void waypoint_think(entity this);
-void waypoint_clearlinks(entity wp);
-void waypoint_schedulerelink(entity wp);
-
-void waypoint_remove(entity e);
-void waypoint_removeall();
-void waypoint_schedulerelinkall();
-void waypoint_load_links_hardwired();
-void waypoint_save_links();
-void waypoint_saveall();
-
-void waypoint_spawnforitem_force(entity e, vector org);
-void waypoint_spawnforitem(entity e);
-void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
-void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
-void botframe_showwaypointlinks();
-
-float waypoint_loadall();
-float waypoint_load_links();
-
-entity waypoint_spawn(vector m1, vector m2, float f);
-entity waypoint_spawnpersonal(entity this, vector position);
-
-vector waypoint_fixorigin(vector position);
-
-void botframe_autowaypoints();
index 5dcd0ad9359d353687409b097c3654a1f30cd65e..5097bd8a145bf3254beb73c947e9d8ccba9f0f81 100644 (file)
@@ -73,21 +73,25 @@ void CampaignPreInit()
 {
        float baseskill;
        string title;
-       if(autocvar_sv_cheats)
-       {
-               CampaignBailout("JOLLY CHEATS AHAHAHAHAHAHAH))");
-               return;
-       }
        campaign_level = autocvar__campaign_index;
        campaign_name = strzone(autocvar__campaign_name);
        campaign_index_var = strzone(strcat("g_campaign", campaign_name, "_index"));
        CampaignFile_Load(campaign_level, 2);
+
        if(campaign_entries < 1)
        {
                CampaignBailout("unknown map");
                return;
        }
 
+       if(autocvar_sv_cheats)
+       {
+               MapInfo_SwitchGameType(MapInfo_Type_FromString(campaign_gametype[0]));
+               CampaignFile_Unload();
+               CampaignBailout("JOLLY CHEATS AHAHAHAHAHAHAH))");
+               return;
+       }
+
        baseskill = autocvar_g_campaign_skill;
        baseskill = baseskill + campaign_botskill[0];
        if(baseskill < 0)
@@ -256,7 +260,7 @@ void CampaignPostIntermission()
        if(campaign_won && campaign_entries < 2)
        {
                // last map won!
-               LOG_DEBUG("^2test run: campaign looks GOOD\n");
+               LOG_DEBUG("^2test run: campaign looks GOOD");
                localcmd("togglemenu 1\n");
                CampaignFile_Unload();
                return;
index 9706a2f1598820d972650d73fc14950d87f3ab55..3d569222cd25d29238562501d6ae78a9633d7058 100644 (file)
@@ -4,7 +4,7 @@
 #include "race.qh"
 #include "../common/triggers/teleporters.qh"
 
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 
 #include "weapons/tracing.qh"
 
@@ -14,9 +14,9 @@
 
 #include <common/physics/player.qh>
 
-#include "../common/monsters/all.qh"
+#include "../common/monsters/_mod.qh"
 
-#include "../common/weapons/all.qh"
+#include <common/weapons/_all.qh>
 
 #include "../common/triggers/subs.qh"
 
@@ -164,7 +164,7 @@ float CheatImpulse(entity this, int imp)
                        this.personal.strength_finished = this.strength_finished;
                        this.personal.invincible_finished = this.invincible_finished;
                        this.personal.teleport_time = time;
-                       break; // this part itthis doesn't cheat, so let's not count this
+                       break; // this part itself doesn't cheat, so let's not count this
                case CHIMPULSE_CLONE_MOVING.impulse:
                        IS_CHEAT(this, imp, 0, 0);
                        makevectors (this.v_angle);
@@ -232,7 +232,7 @@ float CheatImpulse(entity this, int imp)
                        break;
                case CHIMPULSE_TELEPORT.impulse:
                        IS_CHEAT(this, imp, 0, 0);
-                       if(this.movetype == MOVETYPE_NOCLIP)
+                       if(this.move_movetype == MOVETYPE_NOCLIP)
                        {
                                e = find(NULL, classname, "info_autoscreenshot");
                                if(e)
@@ -240,7 +240,7 @@ float CheatImpulse(entity this, int imp)
                                        sprint(this, "Emergency teleport used info_autoscreenshot location\n");
                                        setorigin(this, e.origin);
                                        this.angles = e.angles;
-                                       remove(e);
+                                       delete(e);
                                        // should we? this.angles_x = -this.angles_x;
                                        this.fixangle = true;
                                        this.velocity = '0 0 0';
@@ -262,7 +262,7 @@ float CheatImpulse(entity this, int imp)
                case CHIMPULSE_R00T.impulse:
                        IS_CHEAT(this, imp, 0, 0);
                        RandomSelection_Init();
-                       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it) && DIFF_TEAM(it, this), LAMBDA(RandomSelection_Add(it, 0, string_null, 1, 1)));
+                       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it) && DIFF_TEAM(it, this), LAMBDA(RandomSelection_AddEnt(it, 1, 1)));
                        if(RandomSelection_chosen_ent)
                                e = RandomSelection_chosen_ent;
                        else
@@ -274,7 +274,7 @@ float CheatImpulse(entity this, int imp)
                        e2 = spawn();
                        setorigin(e2, e.origin);
                        RadiusDamage(e2, this, 1000, 0, 128, NULL, NULL, 500, DEATH_CHEAT.m_id, e);
-                       remove(e2);
+                       delete(e2);
 
                        LOG_INFO("404 Sportsmanship not found.\n");
                        DID_CHEAT();
@@ -323,7 +323,7 @@ float CheatCommand(entity this, int argc)
                                // arguments:
                                //   effectname
                                effectnum = _particleeffectnum(argv(1));
-                               W_SetupShot(this, false, false, SND_Null, CH_WEAPON_A, 0);
+                               W_SetupShot(this, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0);
                                traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, this);
                                __trailparticles(this, effectnum, w_shotorg, trace_endpos);
                                DID_CHEAT();
@@ -338,7 +338,7 @@ float CheatCommand(entity this, int argc)
                                // arguments:
                                //   modelname mode
                                f = stof(argv(2));
-                               W_SetupShot(this, false, false, SND_Null, CH_WEAPON_A, 0);
+                               W_SetupShot(this, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0);
                                traceline(w_shotorg, w_shotorg + w_shotdir * 2048, MOVE_NORMAL, this);
                                if((trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) || trace_fraction == 1)
                                {
@@ -364,7 +364,7 @@ float CheatCommand(entity this, int argc)
                                                tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_NORMAL, e);
                                                if(trace_startsolid)
                                                {
-                                                       remove(e);
+                                                       delete(e);
                                                        sprint(this, "cannot make stuff there (no space)\n");
                                                }
                                                else
@@ -444,7 +444,7 @@ float CheatCommand(entity this, int argc)
                        e.nextthink = time;
                        e.solid = 0; // nothing special
                        setmodel(e, MDL_MARKER);
-                       setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+                       setsize(e, STAT(PL_MIN, this), STAT(PL_MAX, this));
                        e.skin = 2;
                        if(argc == 3)
                                e.cnt = stof(argv(1));
@@ -475,18 +475,18 @@ float CheatCommand(entity this, int argc)
                        RandomSelection_Init();
                        crosshair_trace(this);
                        for(entity e = NULL; (e = find(e, classname, "dragbox_box")); )
-                               RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
+                               RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
                        for(entity e = NULL; (e = find(e, classname, "dragpoint")); )
-                               RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
+                               RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
                        if(RandomSelection_chosen_ent)
                        {
-                               remove(RandomSelection_chosen_ent.killindicator.killindicator);
-                               remove(RandomSelection_chosen_ent.killindicator);
+                               delete(RandomSelection_chosen_ent.killindicator.killindicator);
+                               delete(RandomSelection_chosen_ent.killindicator);
                                if(RandomSelection_chosen_ent.aiment)
-                                       remove(RandomSelection_chosen_ent.aiment);
+                                       delete(RandomSelection_chosen_ent.aiment);
                                if(RandomSelection_chosen_ent.enemy)
-                                       remove(RandomSelection_chosen_ent.enemy);
-                               remove(RandomSelection_chosen_ent);
+                                       delete(RandomSelection_chosen_ent.enemy);
+                               delete(RandomSelection_chosen_ent);
                        }
                        DID_CHEAT();
                        break;
@@ -497,9 +497,9 @@ float CheatCommand(entity this, int argc)
                                RandomSelection_Init();
                                crosshair_trace(this);
                                for(entity e = NULL; (e = find(e, classname, "dragbox_box")); )
-                                       RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
+                                       RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
                                for(entity e = NULL; (e = find(e, classname, "dragpoint")); )
-                                       RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
+                                       RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
                                if(RandomSelection_chosen_ent)
                                {
                                        if(substring(argv(1), 0, 1) == "*")
@@ -604,15 +604,15 @@ float CheatCommand(entity this, int argc)
                case "drag_clear":
                        IS_CHEAT(this, 0, argc, 0);
                        for(entity e = NULL; (e = find(e, classname, "dragbox_box")); )
-                               remove(e);
+                               delete(e);
                        for(entity e = NULL; (e = find(e, classname, "dragbox_corner_1")); )
-                               remove(e);
+                               delete(e);
                        for(entity e = NULL; (e = find(e, classname, "dragbox_corner_2")); )
-                               remove(e);
+                               delete(e);
                        for(entity e = NULL; (e = find(e, classname, "dragpoint")); )
-                               remove(e);
+                               delete(e);
                        for(entity e = NULL; (e = find(e, classname, "drag_digit")); )
-                               remove(e);
+                               delete(e);
                        DID_CHEAT();
                        break;
                case "god":
@@ -639,29 +639,29 @@ float CheatCommand(entity this, int argc)
                        break;
                case "noclip":
                        IS_CHEAT(this, 0, argc, 0);
-                       if(this.movetype != MOVETYPE_NOCLIP)
+                       if(this.move_movetype != MOVETYPE_NOCLIP)
                        {
-                               this.movetype = MOVETYPE_NOCLIP;
+                               set_movetype(this, MOVETYPE_NOCLIP);
                                sprint(this, "noclip ON\n");
                                DID_CHEAT();
                        }
                        else
                        {
-                               this.movetype = MOVETYPE_WALK;
+                               set_movetype(this, MOVETYPE_WALK);
                                sprint(this, "noclip OFF\n");
                        }
                        break;
                case "fly":
                        IS_CHEAT(this, 0, argc, 0);
-                       if(this.movetype != MOVETYPE_FLY)
+                       if(this.move_movetype != MOVETYPE_FLY)
                        {
-                               this.movetype = MOVETYPE_FLY;
+                               set_movetype(this, MOVETYPE_FLY);
                                sprint(this, "flymode ON\n");
                                DID_CHEAT();
                        }
                        else
                        {
-                               this.movetype = MOVETYPE_WALK;
+                               set_movetype(this, MOVETYPE_WALK);
                                sprint(this, "flymode OFF\n");
                        }
                        break;
@@ -675,7 +675,7 @@ float CheatCommand(entity this, int argc)
                        entity e = spawn();
                        e.target = argv(1);
                        SUB_UseTargets(e, this, NULL);
-                       remove(e);
+                       delete(e);
                        DID_CHEAT();
                        break;
                case "killtarget":
@@ -683,7 +683,7 @@ float CheatCommand(entity this, int argc)
                        entity e2 = spawn();
                        e2.killtarget = argv(1);
                        SUB_UseTargets(e2, this, NULL);
-                       remove(e2);
+                       delete(e2);
                        DID_CHEAT();
                        break;
                case "teleporttotarget":
@@ -695,7 +695,7 @@ float CheatCommand(entity this, int argc)
                        if(!wasfreed(ent))
                        {
                                Simple_TeleportPlayer(ent, this);
-                               remove(ent);
+                               delete(ent);
                                DID_CHEAT();
                        }
                        break;
@@ -733,7 +733,8 @@ float CheatFrame(entity this)
                        if(this.maycheat || (gamestart_sv_cheats && autocvar_sv_cheats))
                        {
                                // use cheat dragging if cheats are enabled
-                               crosshair_trace_plusvisibletriggers(this);
+                               //if(Drag_IsDragging(this))
+                                       //crosshair_trace_plusvisibletriggers(this);
                                Drag(this, true, true);
                        }
                        else
@@ -867,9 +868,9 @@ void Drag_Begin(entity dragger, entity draggee, vector touchpoint)
 {
        float tagscale;
 
-       draggee.dragmovetype = draggee.movetype;
+       draggee.dragmovetype = draggee.move_movetype;
        draggee.draggravity = draggee.gravity;
-       draggee.movetype = MOVETYPE_WALK;
+       set_movetype(draggee, MOVETYPE_WALK);
        draggee.gravity = 0.00001;
        UNSET_ONGROUND(draggee);
        draggee.draggedby = dragger;
@@ -894,10 +895,10 @@ void Drag_Finish(entity dragger)
        if(dragger)
                dragger.dragentity = NULL;
        draggee.draggedby = NULL;
-       draggee.movetype = draggee.dragmovetype;
+       set_movetype(draggee, draggee.dragmovetype);
        draggee.gravity = draggee.draggravity;
 
-       switch(draggee.movetype)
+       switch(draggee.move_movetype)
        {
                case MOVETYPE_TOSS:
                case MOVETYPE_WALK:
@@ -912,7 +913,7 @@ void Drag_Finish(entity dragger)
                        break;
        }
 
-       if((draggee.flags & FL_ITEM) && (vlen(draggee.velocity) < 32))
+       if((draggee.flags & FL_ITEM) && (vdist(draggee.velocity, <, 32)))
        {
                draggee.velocity = '0 0 0';
                SET_ONGROUND(draggee); // floating items are FUN
diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc
deleted file mode 100644 (file)
index 28d9c6c..0000000
+++ /dev/null
@@ -1,2475 +0,0 @@
-#include "cl_client.qh"
-
-#include "anticheat.qh"
-#include "cl_impulse.qh"
-#include "cl_player.qh"
-#include "ipban.qh"
-#include "miscfunctions.qh"
-#include "portals.qh"
-#include "teamplay.qh"
-#include "playerdemo.qh"
-#include "spawnpoints.qh"
-#include "g_damage.qh"
-#include "g_hook.qh"
-#include "command/common.qh"
-#include "cheats.qh"
-#include "g_world.qh"
-#include "race.qh"
-#include "antilag.qh"
-#include "campaign.qh"
-#include "command/common.qh"
-
-#include "bot/bot.qh"
-#include "bot/navigation.qh"
-
-#include "../common/ent_cs.qh"
-#include <common/state.qh>
-
-#include <common/effects/qc/globalsound.qh>
-
-#include "../common/triggers/teleporters.qh"
-
-#include "../common/vehicles/all.qh"
-
-#include "weapons/hitplot.qh"
-#include "weapons/weaponsystem.qh"
-
-#include "../common/net_notice.qh"
-#include "../common/physics/player.qh"
-
-#include "../common/items/all.qc"
-
-#include "../common/mutators/mutator/waypoints/all.qh"
-
-#include "../common/triggers/subs.qh"
-#include "../common/triggers/triggers.qh"
-#include "../common/triggers/trigger/secret.qh"
-
-#include "../common/minigames/sv_minigames.qh"
-
-#include "../common/items/inventory.qh"
-
-#include "../common/monsters/sv_monsters.qh"
-
-#include "../lib/warpzone/server.qh"
-
-STATIC_METHOD(Client, Add, void(Client this, int _team))
-{
-    WITHSELF(this, ClientConnect());
-    TRANSMUTE(Player, this);
-    this.frame = 12; // 7
-    this.team = _team;
-    WITHSELF(this, PutClientInServer());
-}
-
-void PutObserverInServer(entity this);
-void ClientDisconnect();
-
-STATIC_METHOD(Client, Remove, void(Client this))
-{
-    TRANSMUTE(Observer, this);
-    WITHSELF(this, PutClientInServer());
-    WITHSELF(this, ClientDisconnect());
-}
-
-void send_CSQC_teamnagger() {
-       WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
-}
-
-bool ClientData_Send(entity this, entity to, int sf)
-{
-       assert(to == this.owner, return false);
-
-       entity e = to;
-       if (IS_SPEC(e)) e = e.enemy;
-
-       sf = 0;
-       if (e.race_completed)       sf |= 1; // forced scoreboard
-       if (to.spectatee_status)    sf |= 2; // spectator ent number follows
-       if (e.zoomstate)            sf |= 4; // zoomed
-       if (e.porto_v_angle_held)   sf |= 8; // angles held
-
-       WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
-       WriteByte(MSG_ENTITY, sf);
-
-       if (sf & 2)
-       {
-               WriteByte(MSG_ENTITY, to.spectatee_status);
-       }
-       if (sf & 8)
-       {
-               WriteAngle(MSG_ENTITY, e.v_angle.x);
-               WriteAngle(MSG_ENTITY, e.v_angle.y);
-       }
-       return true;
-}
-
-void ClientData_Attach(entity this)
-{
-       Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send);
-       this.clientdata.drawonlytoclient = this;
-       this.clientdata.owner = this;
-}
-
-void ClientData_Detach(entity this)
-{
-       remove(this.clientdata);
-       this.clientdata = NULL;
-}
-
-void ClientData_Touch(entity e)
-{
-       e.clientdata.SendFlags = 1;
-
-       // make it spectatable
-       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1));
-}
-
-.string netname_previous;
-
-void SetSpectatee(entity player, entity spectatee);
-
-
-/*
-=============
-CheckPlayerModel
-
-Checks if the argument string can be a valid playermodel.
-Returns a valid one in doubt.
-=============
-*/
-string FallbackPlayerModel;
-string CheckPlayerModel(string plyermodel) {
-       if(FallbackPlayerModel != cvar_defstring("_cl_playermodel"))
-       {
-               // note: we cannot summon Don Strunzone here, some player may
-               // still have the model string set. In case anyone manages how
-               // to change a cvar default, we'll have a small leak here.
-               FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
-       }
-       // only in right path
-       if( substring(plyermodel,0,14) != "models/player/")
-               return FallbackPlayerModel;
-       // only good file extensions
-       if(substring(plyermodel,-4,4) != ".zym")
-       if(substring(plyermodel,-4,4) != ".dpm")
-       if(substring(plyermodel,-4,4) != ".iqm")
-       if(substring(plyermodel,-4,4) != ".md3")
-       if(substring(plyermodel,-4,4) != ".psk")
-               return FallbackPlayerModel;
-       // forbid the LOD models
-       if(substring(plyermodel, -9,5) == "_lod1")
-               return FallbackPlayerModel;
-       if(substring(plyermodel, -9,5) == "_lod2")
-               return FallbackPlayerModel;
-       if(plyermodel != strtolower(plyermodel))
-               return FallbackPlayerModel;
-       // also, restrict to server models
-       if(autocvar_sv_servermodelsonly)
-       {
-               if(!fexists(plyermodel))
-                       return FallbackPlayerModel;
-       }
-       return plyermodel;
-}
-
-void setplayermodel(entity e, string modelname)
-{
-       precache_model(modelname);
-       _setmodel(e, modelname);
-       player_setupanimsformodel(e);
-       if(!autocvar_g_debug_globalsounds)
-               UpdatePlayerSounds(e);
-}
-
-void FixPlayermodel(entity player);
-/** putting a client as observer in the server */
-void PutObserverInServer(entity this)
-{
-    bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
-       PlayerState_detach(this);
-
-       if (IS_PLAYER(this) && this.health >= 1) {
-        // despawn effect
-               Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
-    }
-
-    {
-        entity spot = SelectSpawnPoint(this, true);
-        if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
-        this.angles = spot.angles;
-        this.angles_z = 0;
-        this.fixangle = true;
-        // offset it so that the spectator spawns higher off the ground, looks better this way
-        setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL));
-        this.prevorigin = this.origin;
-        if (IS_REAL_CLIENT(this))
-        {
-            msg_entity = this;
-            WriteByte(MSG_ONE, SVC_SETVIEW);
-            WriteEntity(MSG_ONE, this);
-        }
-        // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
-        // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
-        if(!autocvar_g_debug_globalsounds)
-        {
-               // needed for player sounds
-               this.model = "";
-               FixPlayermodel(this);
-        } 
-        setmodel(this, MDL_Null);
-        setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL));
-        this.view_ofs = '0 0 0';
-    }
-
-    RemoveGrapplingHook(this);
-       Portal_ClearAll(this);
-       Unfreeze(this);
-
-       if (this.alivetime)
-       {
-               if (!warmup_stage)
-                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
-               this.alivetime = 0;
-       }
-
-       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
-
-       WaypointSprite_PlayerDead(this);
-
-       if (mutator_returnvalue) {
-           // mutator prevents resetting teams+score
-       } else {
-               this.team = -1;  // move this as it is needed to log the player spectating in eventlog
-        this.frags = FRAGS_SPECTATOR;
-        PlayerScore_Clear(this);  // clear scores when needed
-    }
-
-       if (this.killcount != FRAGS_SPECTATOR)
-       {
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
-               if(!intermission_running)
-               if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
-
-               if(this.just_joined == false) {
-                       LogTeamchange(this.playerid, -1, 4);
-               } else
-                       this.just_joined = false;
-       }
-
-       accuracy_resend(this);
-
-       this.spectatortime = time;
-       this.bot_attack = false;
-    this.hud = HUD_NORMAL;
-       TRANSMUTE(Observer, this);
-       this.iscreature = false;
-       this.teleportable = TELEPORT_SIMPLE;
-       this.damagedbycontents = false;
-       this.health = FRAGS_SPECTATOR;
-       this.takedamage = DAMAGE_NO;
-       this.solid = SOLID_NOT;
-       this.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
-       this.flags = FL_CLIENT | FL_NOTARGET;
-       this.armorvalue = 666;
-       this.effects = 0;
-       this.armorvalue = autocvar_g_balance_armor_start;
-       this.pauserotarmor_finished = 0;
-       this.pauserothealth_finished = 0;
-       this.pauseregen_finished = 0;
-       this.damageforcescale = 0;
-       this.death_time = 0;
-       this.respawn_flags = 0;
-       this.respawn_time = 0;
-       this.stat_respawn_time = 0;
-       this.alpha = 0;
-       this.scale = 0;
-       this.fade_time = 0;
-       this.pain_frame = 0;
-       this.pain_finished = 0;
-       this.strength_finished = 0;
-       this.invincible_finished = 0;
-       this.superweapons_finished = 0;
-       this.pushltime = 0;
-       this.istypefrag = 0;
-       setthink(this, func_null);
-       this.nextthink = 0;
-       this.hook_time = 0;
-       this.deadflag = DEAD_NO;
-       this.crouch = false;
-       this.revival_time = 0;
-
-       this.items = 0;
-       this.weapons = '0 0 0';
-       this.drawonlytoclient = this;
-
-       this.weaponname = "";
-       this.weaponmodel = "";
-       for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-       {
-               this.weaponentities[slot] = NULL;
-       }
-       this.exteriorweaponentity = NULL;
-       this.killcount = FRAGS_SPECTATOR;
-       this.velocity = '0 0 0';
-       this.avelocity = '0 0 0';
-       this.punchangle = '0 0 0';
-       this.punchvector = '0 0 0';
-       this.oldvelocity = this.velocity;
-       this.fire_endtime = -1;
-       this.event_damage = func_null;
-}
-
-int player_getspecies(entity this)
-{
-       get_model_parameters(this.model, this.skin);
-       int s = get_model_parameters_species;
-       get_model_parameters(string_null, 0);
-       if (s < 0) return SPECIES_HUMAN;
-       return s;
-}
-
-.float model_randomizer;
-void FixPlayermodel(entity player)
-{
-       string defaultmodel = "";
-       int defaultskin = 0;
-       if(autocvar_sv_defaultcharacter)
-       {
-               if(teamplay)
-               {
-                       string s = Static_Team_ColorName_Lower(player.team);
-                       if (s != "neutral")
-                       {
-                               defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s));
-                               defaultskin = cvar(strcat("sv_defaultplayerskin_", s));
-                       }
-               }
-
-               if(defaultmodel == "")
-               {
-                       defaultmodel = autocvar_sv_defaultplayermodel;
-                       defaultskin = autocvar_sv_defaultplayerskin;
-               }
-
-               int n = tokenize_console(defaultmodel);
-               if(n > 0)
-               {
-                       defaultmodel = argv(floor(n * player.model_randomizer));
-                       // However, do NOT randomize if the player-selected model is in the list.
-                       for (int i = 0; i < n; ++i)
-                               if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin))
-                                       defaultmodel = argv(i);
-               }
-
-               int i = strstrofs(defaultmodel, ":", 0);
-               if(i >= 0)
-               {
-                       defaultskin = stof(substring(defaultmodel, i+1, -1));
-                       defaultmodel = substring(defaultmodel, 0, i);
-               }
-       }
-       if(autocvar_sv_defaultcharacterskin && !defaultskin)
-       {
-               if(teamplay)
-               {
-                       string s = Static_Team_ColorName_Lower(player.team);
-                       if (s != "neutral")
-                               defaultskin = cvar(strcat("sv_defaultplayerskin_", s));
-               }
-
-               if(!defaultskin)
-                       defaultskin = autocvar_sv_defaultplayerskin;
-       }
-
-       MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player);
-       defaultmodel = M_ARGV(0, string);
-       defaultskin = M_ARGV(1, int);
-
-       bool chmdl = false;
-       int oldskin;
-       if(defaultmodel != "")
-       {
-               if (defaultmodel != player.model)
-               {
-                       vector m1 = player.mins;
-                       vector m2 = player.maxs;
-                       setplayermodel (player, defaultmodel);
-                       setsize (player, m1, m2);
-                       chmdl = true;
-               }
-
-               oldskin = player.skin;
-               player.skin = defaultskin;
-       } else {
-               if (player.playermodel != player.model || player.playermodel == "")
-               {
-                       player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop
-                       vector m1 = player.mins;
-                       vector m2 = player.maxs;
-                       setplayermodel (player, player.playermodel);
-                       setsize (player, m1, m2);
-                       chmdl = true;
-               }
-
-               if(!autocvar_sv_defaultcharacterskin)
-               {
-                       oldskin = player.skin;
-                       player.skin = stof(player.playerskin);
-               }
-               else
-               {
-                       oldskin = player.skin;
-                       player.skin = defaultskin;
-               }
-       }
-
-       if(chmdl || oldskin != player.skin) // model or skin has changed
-       {
-               player.species = player_getspecies(player); // update species
-               if(!autocvar_g_debug_globalsounds)
-                       UpdatePlayerSounds(player); // update skin sounds
-       }
-
-       if(!teamplay)
-               if(strlen(autocvar_sv_defaultplayercolors))
-                       if(player.clientcolors != stof(autocvar_sv_defaultplayercolors))
-                               setcolor(player, stof(autocvar_sv_defaultplayercolors));
-}
-
-
-/** Called when a client spawns in the server */
-void PutClientInServer()
-{ENGINE_EVENT();
-       if (IS_BOT_CLIENT(this)) {
-               TRANSMUTE(Player, this);
-       } else if (IS_REAL_CLIENT(this)) {
-               msg_entity = this;
-               WriteByte(MSG_ONE, SVC_SETVIEW);
-               WriteEntity(MSG_ONE, this);
-       }
-       if (gameover) {
-               TRANSMUTE(Observer, this);
-       }
-
-       SetSpectatee(this, NULL);
-
-       // reset player keys
-       this.itemkeys = 0;
-
-       MUTATOR_CALLHOOK(PutClientInServer, this);
-
-       if (IS_OBSERVER(this)) {
-               PutObserverInServer(this);
-       } else if (IS_PLAYER(this)) {
-               PlayerState_attach(this);
-               accuracy_resend(this);
-
-               if (this.team < 0)
-                       JoinBestTeam(this, false, true);
-
-               entity spot = SelectSpawnPoint(this, false);
-               if (!spot) {
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
-                       return; // spawn failed
-               }
-
-               TRANSMUTE(Player, this);
-               this.wasplayer = true;
-               this.iscreature = true;
-               this.teleportable = TELEPORT_NORMAL;
-               this.damagedbycontents = true;
-               this.movetype = MOVETYPE_WALK;
-               this.solid = SOLID_SLIDEBOX;
-               this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
-               if (autocvar_g_playerclip_collisions)
-                       this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
-               if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions)
-                       this.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
-               this.frags = FRAGS_PLAYER;
-               if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this);
-               this.flags = FL_CLIENT | FL_PICKUPITEMS;
-               if (autocvar__notarget)
-                       this.flags |= FL_NOTARGET;
-               this.takedamage = DAMAGE_AIM;
-               this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
-               this.dmg = 2; // WTF
-
-               if (warmup_stage) {
-                       this.ammo_shells = warmup_start_ammo_shells;
-                       this.ammo_nails = warmup_start_ammo_nails;
-                       this.ammo_rockets = warmup_start_ammo_rockets;
-                       this.ammo_cells = warmup_start_ammo_cells;
-                       this.ammo_plasma = warmup_start_ammo_plasma;
-                       this.ammo_fuel = warmup_start_ammo_fuel;
-                       this.health = warmup_start_health;
-                       this.armorvalue = warmup_start_armorvalue;
-                       this.weapons = WARMUP_START_WEAPONS;
-               } else {
-                       this.ammo_shells = start_ammo_shells;
-                       this.ammo_nails = start_ammo_nails;
-                       this.ammo_rockets = start_ammo_rockets;
-                       this.ammo_cells = start_ammo_cells;
-                       this.ammo_plasma = start_ammo_plasma;
-                       this.ammo_fuel = start_ammo_fuel;
-                       this.health = start_health;
-                       this.armorvalue = start_armorvalue;
-                       this.weapons = start_weapons;
-               }
-
-               this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
-
-               this.items = start_items;
-
-               this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
-               this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
-               this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
-               this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
-               this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
-               // extend the pause of rotting if client was reset at the beginning of the countdown
-               if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted?
-                       float f = game_starttime - time;
-                       this.spawnshieldtime += f;
-                       this.pauserotarmor_finished += f;
-                       this.pauserothealth_finished += f;
-                       this.pauseregen_finished += f;
-               }
-               this.damageforcescale = 2;
-               this.death_time = 0;
-               this.respawn_flags = 0;
-               this.respawn_time = 0;
-               this.stat_respawn_time = 0;
-               this.scale = autocvar_sv_player_scale;
-               this.fade_time = 0;
-               this.pain_frame = 0;
-               this.pain_finished = 0;
-               this.pushltime = 0;
-               setthink(this, func_null); // players have no think function
-               this.nextthink = 0;
-               this.dmg_team = 0;
-               this.ballistics_density = autocvar_g_ballistics_density_player;
-
-               this.deadflag = DEAD_NO;
-
-               this.angles = spot.angles;
-               this.angles_z = 0; // never spawn tilted even if the spot says to
-               if (IS_BOT_CLIENT(this))
-                       this.v_angle = this.angles;
-               this.fixangle = true; // turn this way immediately
-               this.oldvelocity = this.velocity = '0 0 0';
-               this.avelocity = '0 0 0';
-               this.punchangle = '0 0 0';
-               this.punchvector = '0 0 0';
-
-               this.strength_finished = 0;
-               this.invincible_finished = 0;
-               this.fire_endtime = -1;
-               this.revival_time = 0;
-               this.air_finished = time + 12;
-
-               entity spawnevent = new_pure(spawnevent);
-               spawnevent.owner = this;
-               Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
-
-               // Cut off any still running player sounds.
-               stopsound(this, CH_PLAYER_SINGLE);
-
-               this.model = "";
-               FixPlayermodel(this);
-               this.drawonlytoclient = NULL;
-
-               this.crouch = false;
-               this.view_ofs = STAT(PL_VIEW_OFS, NULL);
-               setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
-               this.spawnorigin = spot.origin;
-               setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
-               // don't reset back to last position, even if new position is stuck in solid
-               this.oldorigin = this.origin;
-               this.prevorigin = this.origin;
-               this.lastteleporttime = time; // prevent insane speeds due to changing origin
-               this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
-               this.hud = HUD_NORMAL;
-
-               this.event_damage = PlayerDamage;
-
-               this.bot_attack = true;
-               this.monster_attack = true;
-
-               PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
-
-               if (this.killcount == FRAGS_SPECTATOR) {
-                       PlayerScore_Clear(this);
-                       this.killcount = 0;
-               }
-
-               for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       CL_SpawnWeaponentity(this, weaponentities[slot]);
-               }
-               this.alpha = default_player_alpha;
-               this.colormod = '1 1 1' * autocvar_g_player_brightness;
-               this.exteriorweaponentity.alpha = default_weapon_alpha;
-
-               this.speedrunning = false;
-
-               target_voicescript_clear(this);
-
-               // reset fields the weapons may use
-               FOREACH(Weapons, true, LAMBDA(
-                       it.wr_resetplayer(it, this);
-                       // reload all reloadable weapons
-                       if (it.spawnflags & WEP_FLAG_RELOADABLE) {
-                               this.weapon_load[it.m_id] = it.reloading_ammo;
-                       }
-               ));
-
-               {
-                       string s = spot.target;
-                       spot.target = string_null;
-                       SUB_UseTargets(spot, this, NULL);
-                       spot.target = s;
-               }
-
-               Unfreeze(this);
-
-               MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
-
-               if (autocvar_spawn_debug)
-               {
-                       sprint(this, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
-                       remove(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
-               }
-
-               PS(this).m_switchweapon = w_getbestweapon(this);
-               this.cnt = -1; // W_LastWeapon will not complain
-               PS(this).m_weapon = WEP_Null;
-               this.weaponname = "";
-               PS(this).m_switchingweapon = WEP_Null;
-
-               if (!warmup_stage && !this.alivetime)
-                       this.alivetime = time;
-
-               antilag_clear(this, CS(this));
-       }
-}
-
-void ClientInit_misc(entity this);
-
-.float ebouncefactor, ebouncestop; // electro's values
-// TODO do we need all these fields, or should we stop autodetecting runtime
-// changes and just have a console command to update this?
-bool ClientInit_SendEntity(entity this, entity to, int sf)
-{
-       WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
-       return = true;
-       msg_entity = to;
-       // MSG_INIT replacement
-       // TODO: make easier to use
-       Registry_send_all();
-       W_PROP_reload(MSG_ONE, to);
-       ClientInit_misc(this);
-       MUTATOR_CALLHOOK(Ent_Init);
-}
-void ClientInit_misc(entity this)
-{
-       int channel = MSG_ONE;
-       WriteHeader(channel, ENT_CLIENT_INIT);
-       WriteByte(channel, g_nexball_meter_period * 32);
-       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
-       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
-       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
-       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
-       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
-       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
-       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
-       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
-
-       if(sv_foginterval && world.fog != "")
-               WriteString(channel, world.fog);
-       else
-               WriteString(channel, "");
-       WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
-       WriteByte(channel, serverflags); // client has to know if it should zoom or not
-       WriteCoord(channel, autocvar_g_trueaim_minrange);
-}
-
-void ClientInit_CheckUpdate(entity this)
-{
-       this.nextthink = time;
-       if(this.count != autocvar_g_balance_armor_blockpercent)
-       {
-               this.count = autocvar_g_balance_armor_blockpercent;
-               this.SendFlags |= 1;
-       }
-}
-
-void ClientInit_Spawn()
-{
-       entity e = new_pure(clientinit);
-       setthink(e, ClientInit_CheckUpdate);
-       Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
-
-       ClientInit_CheckUpdate(e);
-}
-
-/*
-=============
-SetNewParms
-=============
-*/
-void SetNewParms ()
-{
-       // initialize parms for a new player
-       parm1 = -(86400 * 366);
-
-       MUTATOR_CALLHOOK(SetNewParms);
-}
-
-/*
-=============
-SetChangeParms
-=============
-*/
-void SetChangeParms ()
-{ENGINE_EVENT();
-       // save parms for level change
-       parm1 = this.parm_idlesince - time;
-
-       MUTATOR_CALLHOOK(SetChangeParms);
-}
-
-/*
-=============
-DecodeLevelParms
-=============
-*/
-void DecodeLevelParms(entity this)
-{
-       // load parms
-       this.parm_idlesince = parm1;
-       if (this.parm_idlesince == -(86400 * 366))
-               this.parm_idlesince = time;
-
-       // whatever happens, allow 60 seconds of idling directly after connect for map loading
-       this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60);
-
-       MUTATOR_CALLHOOK(DecodeLevelParms);
-}
-
-/*
-=============
-ClientKill
-
-Called when a client types 'kill' in the console
-=============
-*/
-
-.float clientkill_nexttime;
-void ClientKill_Now_TeamChange(entity this)
-{
-       if(this.killindicator_teamchange == -1)
-       {
-               JoinBestTeam( this, false, true );
-       }
-       else if(this.killindicator_teamchange == -2)
-       {
-               if(blockSpectators)
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
-               PutObserverInServer(this);
-       }
-       else
-               WITHSELF(this, SV_ChangeTeam(this.killindicator_teamchange - 1));
-       this.killindicator_teamchange = 0;
-}
-
-void ClientKill_Now(entity this)
-{
-       if(this.vehicle)
-       {
-           vehicles_exit(this.vehicle, VHEF_RELEASE);
-           if(!this.killindicator_teamchange)
-           {
-            this.vehicle_health = -1;
-            Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0');
-           }
-       }
-
-       if(this.killindicator && !wasfreed(this.killindicator))
-               remove(this.killindicator);
-
-       this.killindicator = NULL;
-
-       if(this.killindicator_teamchange)
-               ClientKill_Now_TeamChange(this);
-
-       if(IS_PLAYER(this))
-               Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0');
-
-       // now I am sure the player IS dead
-}
-void KillIndicator_Think(entity this)
-{
-       if (gameover)
-       {
-               this.owner.killindicator = NULL;
-               remove(this);
-               return;
-       }
-
-       if (this.owner.alpha < 0 && !this.owner.vehicle)
-       {
-               this.owner.killindicator = NULL;
-               remove(this);
-               return;
-       }
-
-       if(this.cnt <= 0)
-       {
-               ClientKill_Now(this.owner);
-               return;
-       }
-    else if(g_cts && this.health == 1) // health == 1 means that it's silent
-    {
-        this.nextthink = time + 1;
-        this.cnt -= 1;
-    }
-       else
-       {
-               if(this.cnt <= 10)
-                       setmodel(this, MDL_NUM(this.cnt));
-               if(IS_REAL_CLIENT(this.owner))
-               {
-                       if(this.cnt <= 10)
-                               { Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); }
-               }
-               this.nextthink = time + 1;
-               this.cnt -= 1;
-       }
-}
-
-float clientkilltime;
-void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec
-{
-       float killtime;
-       float starttime;
-       entity e;
-
-       if (gameover)
-               return;
-
-       killtime = autocvar_g_balance_kill_delay;
-
-       if(g_race_qualifying || g_cts)
-               killtime = 0;
-
-    if(MUTATOR_CALLHOOK(ClientKill, this, killtime))
-       return;
-
-       this.killindicator_teamchange = targetteam;
-
-    if(!this.killindicator)
-       {
-               if(!IS_DEAD(this))
-               {
-                       killtime = max(killtime, this.clientkill_nexttime - time);
-                       this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam;
-               }
-
-               if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this))
-               {
-                       ClientKill_Now(this);
-               }
-               else
-               {
-                       starttime = max(time, clientkilltime);
-
-                       this.killindicator = spawn();
-                       this.killindicator.owner = this;
-                       this.killindicator.scale = 0.5;
-                       setattachment(this.killindicator, this, "");
-                       setorigin(this.killindicator, '0 0 52');
-                       setthink(this.killindicator, KillIndicator_Think);
-                       this.killindicator.nextthink = starttime + (this.lip) * 0.05;
-                       clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05);
-                       this.killindicator.cnt = ceil(killtime);
-                       this.killindicator.count = bound(0, ceil(killtime), 10);
-                       //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n"));
-
-                       for(e = NULL; (e = find(e, classname, "body")) != NULL; )
-                       {
-                               if(e.enemy != this)
-                                       continue;
-                               e.killindicator = spawn();
-                               e.killindicator.owner = e;
-                               e.killindicator.scale = 0.5;
-                               setattachment(e.killindicator, e, "");
-                               setorigin(e.killindicator, '0 0 52');
-                               setthink(e.killindicator, KillIndicator_Think);
-                               e.killindicator.nextthink = starttime + (e.lip) * 0.05;
-                               clientkilltime = max(clientkilltime, e.killindicator.nextthink + 0.05);
-                               e.killindicator.cnt = ceil(killtime);
-                       }
-                       this.lip = 0;
-               }
-       }
-       if(this.killindicator)
-       {
-               if(targetteam == 0) // just die
-               {
-                       this.killindicator.colormod = '0 0 0';
-                       if(IS_REAL_CLIENT(this))
-                       if(this.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, this.killindicator.cnt);
-               }
-               else if(targetteam == -1) // auto
-               {
-                       this.killindicator.colormod = '0 1 0';
-                       if(IS_REAL_CLIENT(this))
-                       if(this.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, this.killindicator.cnt);
-               }
-               else if(targetteam == -2) // spectate
-               {
-                       this.killindicator.colormod = '0.5 0.5 0.5';
-                       if(IS_REAL_CLIENT(this))
-                       if(this.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, this.killindicator.cnt);
-               }
-               else
-               {
-                       this.killindicator.colormod = Team_ColorRGB(targetteam);
-                       if(IS_REAL_CLIENT(this))
-                       if(this.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE), this.killindicator.cnt);
-               }
-       }
-
-}
-
-void ClientKill ()
-{ENGINE_EVENT();
-       if(gameover) return;
-       if(this.player_blocked) return;
-       if(STAT(FROZEN, this)) return;
-
-       ClientKill_TeamChange(this, 0);
-}
-
-void FixClientCvars(entity e)
-{
-       // send prediction settings to the client
-       stuffcmd(e, "\nin_bindmap 0 0\n");
-       if(autocvar_g_antilag == 3) // client side hitscan
-               stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
-       if(autocvar_sv_gentle)
-               stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
-
-       MUTATOR_CALLHOOK(FixClientCvars, e);
-}
-
-float PlayerInIDList(entity p, string idlist)
-{
-       float n, i;
-       string s;
-
-       // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this
-       if (!p.crypto_idfp)
-               return 0;
-
-       // this function allows abbreviated player IDs too!
-       n = tokenize_console(idlist);
-       for(i = 0; i < n; ++i)
-       {
-               s = argv(i);
-               if(s == substring(p.crypto_idfp, 0, strlen(s)))
-                       return 1;
-       }
-
-       return 0;
-}
-
-#ifdef DP_EXT_PRECONNECT
-/*
-=============
-ClientPreConnect
-
-Called once (not at each match start) when a client begins a connection to the server
-=============
-*/
-void ClientPreConnect ()
-{ENGINE_EVENT();
-       if(autocvar_sv_eventlog)
-       {
-               GameLogEcho(sprintf(":connect:%d:%d:%s",
-                       this.playerid,
-                       etof(this),
-                       ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot")
-               ));
-       }
-}
-#endif
-
-/**
-=============
-ClientConnect
-
-Called when a client connects to the server
-=============
-*/
-void ClientConnect()
-{ENGINE_EVENT();
-       if (Ban_MaybeEnforceBanOnce(this)) return;
-       assert(!IS_CLIENT(this), return);
-       this.flags |= FL_CLIENT;
-       assert(player_count >= 0, player_count = 0);
-
-#ifdef WATERMARK
-       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK);
-#endif
-       this.version_nagtime = time + 10 + random() * 10;
-       TRANSMUTE(Client, this);
-
-       // identify the right forced team
-       if (autocvar_g_campaign)
-       {
-               if (IS_REAL_CLIENT(this)) // only players, not bots
-               {
-                       switch (autocvar_g_campaign_forceteam)
-                       {
-                               case 1: this.team_forced = NUM_TEAM_1; break;
-                               case 2: this.team_forced = NUM_TEAM_2; break;
-                               case 3: this.team_forced = NUM_TEAM_3; break;
-                               case 4: this.team_forced = NUM_TEAM_4; break;
-                               default: this.team_forced = 0;
-                       }
-               }
-       }
-       else if (PlayerInIDList(this, autocvar_g_forced_team_red))    this.team_forced = NUM_TEAM_1;
-       else if (PlayerInIDList(this, autocvar_g_forced_team_blue))   this.team_forced = NUM_TEAM_2;
-       else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3;
-       else if (PlayerInIDList(this, autocvar_g_forced_team_pink))   this.team_forced = NUM_TEAM_4;
-       else switch (autocvar_g_forced_team_otherwise)
-       {
-               default: this.team_forced = 0; break;
-               case "red": this.team_forced = NUM_TEAM_1; break;
-               case "blue": this.team_forced = NUM_TEAM_2; break;
-               case "yellow": this.team_forced = NUM_TEAM_3; break;
-               case "pink": this.team_forced = NUM_TEAM_4; break;
-               case "spectate":
-               case "spectator":
-                       this.team_forced = -1;
-                       break;
-       }
-       if (!teamplay && this.team_forced > 0) this.team_forced = 0;
-
-    {
-        int id = this.playerid;
-        this.playerid = 0; // silent
-           JoinBestTeam(this, false, false); // if the team number is valid, keep it
-           this.playerid = id;
-    }
-
-       if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) {
-               TRANSMUTE(Observer, this);
-       } else {
-               if (!teamplay || autocvar_g_balance_teams) {
-                       TRANSMUTE(Player, this);
-                       campaign_bots_may_start = true;
-               } else {
-                       TRANSMUTE(Observer, this); // do it anyway
-               }
-       }
-
-       PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid));
-
-       // always track bots, don't ask for cl_allow_uidtracking
-    if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this);
-
-       if (autocvar_sv_eventlog)
-               GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname));
-
-       LogTeamchange(this.playerid, this.team, 1);
-
-       this.just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
-
-       this.netname_previous = strzone(this.netname);
-
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname);
-
-       stuffcmd(this, clientstuff, "\n");
-       stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
-
-       FixClientCvars(this);
-
-       // get version info from player
-       stuffcmd(this, "cmd clientversion $gameversion\n");
-
-       // notify about available teams
-       if (teamplay)
-       {
-               CheckAllowedTeams(this);
-               int t = 0;
-               if (c1 >= 0) t |= BIT(0);
-               if (c2 >= 0) t |= BIT(1);
-               if (c3 >= 0) t |= BIT(2);
-               if (c4 >= 0) t |= BIT(3);
-               stuffcmd(this, sprintf("set _teams_available %d\n", t));
-       }
-       else
-       {
-               stuffcmd(this, "set _teams_available 0\n");
-       }
-
-       bot_relinkplayerlist();
-
-       this.spectatortime = time;
-       if (blockSpectators)
-       {
-               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
-       }
-
-       this.jointime = time;
-       this.allowed_timeouts = autocvar_sv_timeout_number;
-
-       if (IS_REAL_CLIENT(this))
-       {
-               if (!autocvar_g_campaign)
-               {
-                       this.motd_actived_time = -1;
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
-               }
-
-               if (g_weaponarena_weapons == WEPSET(TUBA))
-                       stuffcmd(this, "cl_cmd settemp chase_active 1\n");
-       }
-
-       if (!sv_foginterval && world.fog != "")
-               stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
-
-       if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)))
-               if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
-                       send_CSQC_teamnagger();
-
-       CSQCMODEL_AUTOINIT(this);
-
-       this.model_randomizer = random();
-
-       if (IS_REAL_CLIENT(this))
-               sv_notice_join(this);
-
-       FOREACH_ENTITY_FLOAT(init_for_player_needed, true, {
-               it.init_for_player(it, this);
-       });
-
-       MUTATOR_CALLHOOK(ClientConnect, this);
-}
-/*
-=============
-ClientDisconnect
-
-Called when a client disconnects from the server
-=============
-*/
-.entity chatbubbleentity;
-void ReadyCount();
-void ClientDisconnect()
-{ENGINE_EVENT();
-       assert(IS_CLIENT(this), return);
-
-       PlayerStats_GameReport_FinalizePlayer(this);
-       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
-       if (this.active_minigame) part_minigame(this);
-       if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
-
-       if (autocvar_sv_eventlog)
-               GameLogEcho(strcat(":part:", ftos(this.playerid)));
-
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
-
-    MUTATOR_CALLHOOK(ClientDisconnect, this);
-
-       ClientState_detach(this);
-
-       Portal_ClearAll(this);
-
-       Unfreeze(this);
-
-       RemoveGrapplingHook(this);
-
-       // Here, everything has been done that requires this player to be a client.
-
-       this.flags &= ~FL_CLIENT;
-
-       if (this.chatbubbleentity) remove(this.chatbubbleentity);
-       if (this.killindicator) remove(this.killindicator);
-
-       WaypointSprite_PlayerGone(this);
-
-       bot_relinkplayerlist();
-
-       if (this.netname_previous) strunzone(this.netname_previous);
-       if (this.clientstatus) strunzone(this.clientstatus);
-       if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse);
-       if (this.personal) remove(this.personal);
-
-       this.playerid = 0;
-       ReadyCount();
-       if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
-}
-
-void ChatBubbleThink(entity this)
-{
-       this.nextthink = time;
-       if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this)
-       {
-               if(this.owner) // but why can that ever be NULL?
-                       this.owner.chatbubbleentity = NULL;
-               remove(this);
-               return;
-       }
-
-       this.mdl = "";
-
-       if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) )
-       {
-               if ( this.owner.active_minigame )
-                       this.mdl = "models/sprites/minigame_busy.iqm";
-               else if (PHYS_INPUT_BUTTON_CHAT(this.owner))
-                       this.mdl = "models/misc/chatbubble.spr";
-       }
-
-       if ( this.model != this.mdl )
-               _setmodel(this, this.mdl);
-
-}
-
-void UpdateChatBubble(entity this)
-{
-       if (this.alpha < 0)
-               return;
-       // spawn a chatbubble entity if needed
-       if (!this.chatbubbleentity)
-       {
-               this.chatbubbleentity = new(chatbubbleentity);
-               this.chatbubbleentity.owner = this;
-               this.chatbubbleentity.exteriormodeltoclient = this;
-               setthink(this.chatbubbleentity, ChatBubbleThink);
-               this.chatbubbleentity.nextthink = time;
-               setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
-               //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
-               setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1');
-               setattachment(this.chatbubbleentity, this, "");  // sticks to moving player better, also conserves bandwidth
-               this.chatbubbleentity.mdl = this.chatbubbleentity.model;
-               //this.chatbubbleentity.model = "";
-               this.chatbubbleentity.effects = EF_LOWPRECISION;
-       }
-}
-
-
-// LordHavoc: this hack will be removed when proper _pants/_shirt layers are
-// added to the model skins
-/*void UpdateColorModHack()
-{
-       float c;
-       c = this.clientcolors & 15;
-       // LordHavoc: only bothering to support white, green, red, yellow, blue
-            if (!teamplay) this.colormod = '0 0 0';
-       else if (c ==  0) this.colormod = '1.00 1.00 1.00';
-       else if (c ==  3) this.colormod = '0.10 1.73 0.10';
-       else if (c ==  4) this.colormod = '1.73 0.10 0.10';
-       else if (c == 12) this.colormod = '1.22 1.22 0.10';
-       else if (c == 13) this.colormod = '0.10 0.10 1.73';
-       else this.colormod = '1 1 1';
-}*/
-
-void respawn(entity this)
-{
-       if(this.alpha >= 0 && autocvar_g_respawn_ghosts)
-       {
-               this.solid = SOLID_NOT;
-               this.takedamage = DAMAGE_NO;
-               this.movetype = MOVETYPE_FLY;
-               this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
-               this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
-               this.effects |= CSQCMODEL_EF_RESPAWNGHOST;
-               Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1);
-               if(autocvar_g_respawn_ghosts_maxtime)
-                       SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
-       }
-
-       CopyBody(this, 1);
-
-       this.effects |= EF_NODRAW; // prevent another CopyBody
-       WITHSELF(this, PutClientInServer());
-}
-
-void play_countdown(entity this, float finished, Sound samp)
-{
-    TC(Sound, samp);
-       if(IS_REAL_CLIENT(this))
-               if(floor(finished - time - frametime) != floor(finished - time))
-                       if(finished - time < 6)
-                               sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
-}
-
-void player_powerups(entity this)
-{
-       // add a way to see what the items were BEFORE all of these checks for the mutator hook
-       int items_prev = this.items;
-
-       if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover)
-               this.modelflags |= MF_ROCKET;
-       else
-               this.modelflags &= ~MF_ROCKET;
-
-       this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
-
-       if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
-               return;
-
-       Fire_ApplyDamage(this);
-       Fire_ApplyEffect(this);
-
-       if (!g_instagib)
-       {
-               if (this.items & ITEM_Strength.m_itemid)
-               {
-                       play_countdown(this, this.strength_finished, SND_POWEROFF);
-                       this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > this.strength_finished)
-                       {
-                               this.items = this.items - (this.items & ITEM_Strength.m_itemid);
-                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
-                       }
-               }
-               else
-               {
-                       if (time < this.strength_finished)
-                       {
-                               this.items = this.items | ITEM_Strength.m_itemid;
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH);
-                       }
-               }
-               if (this.items & ITEM_Shield.m_itemid)
-               {
-                       play_countdown(this, this.invincible_finished, SND_POWEROFF);
-                       this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > this.invincible_finished)
-                       {
-                               this.items = this.items - (this.items & ITEM_Shield.m_itemid);
-                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
-                       }
-               }
-               else
-               {
-                       if (time < this.invincible_finished)
-                       {
-                               this.items = this.items | ITEM_Shield.m_itemid;
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD);
-                       }
-               }
-               if (this.items & IT_SUPERWEAPON)
-               {
-                       if (!(this.weapons & WEPSET_SUPERWEAPONS))
-                       {
-                               this.superweapons_finished = 0;
-                               this.items = this.items - (this.items & IT_SUPERWEAPON);
-                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
-                       }
-                       else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
-                       {
-                               // don't let them run out
-                       }
-                       else
-                       {
-                               play_countdown(this, this.superweapons_finished, SND_POWEROFF);
-                               if (time > this.superweapons_finished)
-                               {
-                                       this.items = this.items - (this.items & IT_SUPERWEAPON);
-                                       this.weapons &= ~WEPSET_SUPERWEAPONS;
-                                       //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
-                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
-                               }
-                       }
-               }
-               else if(this.weapons & WEPSET_SUPERWEAPONS)
-               {
-                       if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS))
-                       {
-                               this.items = this.items | IT_SUPERWEAPON;
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
-                       }
-                       else
-                       {
-                               this.superweapons_finished = 0;
-                               this.weapons &= ~WEPSET_SUPERWEAPONS;
-                       }
-               }
-               else
-               {
-                       this.superweapons_finished = 0;
-               }
-       }
-
-       if(autocvar_g_nodepthtestplayers)
-               this.effects = this.effects | EF_NODEPTHTEST;
-
-       if(autocvar_g_fullbrightplayers)
-               this.effects = this.effects | EF_FULLBRIGHT;
-
-       if (time >= game_starttime)
-       if (time < this.spawnshieldtime)
-               this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
-
-       MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
-}
-
-float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
-{
-       if(current > stable)
-               return current;
-       else if(current > stable - 0.25) // when close enough, "snap"
-               return stable;
-       else
-               return min(stable, current + (stable - current) * regenfactor * regenframetime);
-}
-
-float CalcRot(float current, float stable, float rotfactor, float rotframetime)
-{
-       if(current < stable)
-               return current;
-       else if(current < stable + 0.25) // when close enough, "snap"
-               return stable;
-       else
-               return max(stable, current + (stable - current) * rotfactor * rotframetime);
-}
-
-float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit)
-{
-       if(current > rotstable)
-       {
-               if(rotframetime > 0)
-               {
-                       current = CalcRot(current, rotstable, rotfactor, rotframetime);
-                       current = max(rotstable, current - rotlinear * rotframetime);
-               }
-       }
-       else if(current < regenstable)
-       {
-               if(regenframetime > 0)
-               {
-                       current = CalcRegen(current, regenstable, regenfactor, regenframetime);
-                       current = min(regenstable, current + regenlinear * regenframetime);
-               }
-       }
-
-       if(current > limit)
-               current = limit;
-
-       return current;
-}
-
-void player_regen(entity this)
-{
-       float max_mod, regen_mod, rot_mod, limit_mod;
-       max_mod = regen_mod = rot_mod = limit_mod = 1;
-
-       float regen_health = autocvar_g_balance_health_regen;
-       float regen_health_linear = autocvar_g_balance_health_regenlinear;
-       float regen_health_rot = autocvar_g_balance_health_rot;
-       float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
-       float regen_health_stable = autocvar_g_balance_health_regenstable;
-       float regen_health_rotstable = autocvar_g_balance_health_rotstable;
-       bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
-               regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
-       max_mod = M_ARGV(1, float);
-       regen_mod = M_ARGV(2, float);
-       rot_mod = M_ARGV(3, float);
-       limit_mod = M_ARGV(4, float);
-       regen_health = M_ARGV(5, float);
-       regen_health_linear = M_ARGV(6, float);
-       regen_health_rot = M_ARGV(7, float);
-       regen_health_rotlinear = M_ARGV(8, float);
-       regen_health_stable = M_ARGV(9, float);
-       regen_health_rotstable = M_ARGV(10, float);
-
-
-       if(!mutator_returnvalue)
-       if(!STAT(FROZEN, this))
-       {
-               float mina, maxa, limith, limita;
-               maxa = autocvar_g_balance_armor_rotstable;
-               mina = autocvar_g_balance_armor_regenstable;
-               limith = autocvar_g_balance_health_limit;
-               limita = autocvar_g_balance_armor_limit;
-
-               regen_health_rotstable = regen_health_rotstable * max_mod;
-               regen_health_stable = regen_health_stable * max_mod;
-               limith = limith * limit_mod;
-               limita = limita * limit_mod;
-
-               this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita);
-               this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith);
-       }
-
-       // if player rotted to death...  die!
-       // check this outside above checks, as player may still be able to rot to death
-       if(this.health < 1)
-       {
-               if(this.vehicle)
-                       vehicles_exit(this.vehicle, VHEF_RELEASE);
-               if(this.event_damage)
-                       this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0');
-       }
-
-       if (!(this.items & IT_UNLIMITED_WEAPON_AMMO))
-       {
-               float minf, maxf, limitf;
-
-               maxf = autocvar_g_balance_fuel_rotstable;
-               minf = autocvar_g_balance_fuel_regenstable;
-               limitf = autocvar_g_balance_fuel_limit;
-
-               this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf);
-       }
-}
-
-bool zoomstate_set;
-void SetZoomState(entity this, float z)
-{
-       if(z != this.zoomstate)
-       {
-               this.zoomstate = z;
-               ClientData_Touch(this);
-       }
-       zoomstate_set = true;
-}
-
-void GetPressedKeys(entity this)
-{
-       MUTATOR_CALLHOOK(GetPressedKeys, this);
-       int keys = this.pressedkeys;
-       keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
-       keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
-       keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
-       keys = BITSET(keys, KEY_LEFT,           this.movement.y < 0);
-
-       keys = BITSET(keys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(this));
-       keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
-       keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
-       keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
-       this.pressedkeys = keys;
-}
-
-/*
-======================
-spectate mode routines
-======================
-*/
-
-void SpectateCopy(entity this, entity spectatee)
-{
-    TC(Client, this); TC(Client, spectatee);
-
-       MUTATOR_CALLHOOK(SpectateCopy, spectatee, this);
-       PS(this) = PS(spectatee);
-       this.armortype = spectatee.armortype;
-       this.armorvalue = spectatee.armorvalue;
-       this.ammo_cells = spectatee.ammo_cells;
-       this.ammo_plasma = spectatee.ammo_plasma;
-       this.ammo_shells = spectatee.ammo_shells;
-       this.ammo_nails = spectatee.ammo_nails;
-       this.ammo_rockets = spectatee.ammo_rockets;
-       this.ammo_fuel = spectatee.ammo_fuel;
-       this.clip_load = spectatee.clip_load;
-       this.clip_size = spectatee.clip_size;
-       this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
-       this.health = spectatee.health;
-       this.impulse = 0;
-       this.items = spectatee.items;
-       this.last_pickup = spectatee.last_pickup;
-       this.hit_time = spectatee.hit_time;
-       this.strength_finished = spectatee.strength_finished;
-       this.invincible_finished = spectatee.invincible_finished;
-       this.pressedkeys = spectatee.pressedkeys;
-       this.weapons = spectatee.weapons;
-       this.vortex_charge = spectatee.vortex_charge;
-       this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
-       this.hagar_load = spectatee.hagar_load;
-       this.arc_heat_percent = spectatee.arc_heat_percent;
-       this.minelayer_mines = spectatee.minelayer_mines;
-       this.punchangle = spectatee.punchangle;
-       this.view_ofs = spectatee.view_ofs;
-       this.velocity = spectatee.velocity;
-       this.dmg_take = spectatee.dmg_take;
-       this.dmg_save = spectatee.dmg_save;
-       this.dmg_inflictor = spectatee.dmg_inflictor;
-       this.v_angle = spectatee.v_angle;
-       this.angles = spectatee.v_angle;
-       STAT(FROZEN, this) = STAT(FROZEN, spectatee);
-       this.revive_progress = spectatee.revive_progress;
-       if(!PHYS_INPUT_BUTTON_USE(this))
-               this.fixangle = true;
-       setorigin(this, spectatee.origin);
-       setsize(this, spectatee.mins, spectatee.maxs);
-       SetZoomState(this, spectatee.zoomstate);
-
-    anticheat_spectatecopy(this, spectatee);
-       this.hud = spectatee.hud;
-       if(spectatee.vehicle)
-    {
-        this.fixangle = false;
-        //this.velocity = spectatee.vehicle.velocity;
-        this.vehicle_health = spectatee.vehicle_health;
-        this.vehicle_shield = spectatee.vehicle_shield;
-        this.vehicle_energy = spectatee.vehicle_energy;
-        this.vehicle_ammo1 = spectatee.vehicle_ammo1;
-        this.vehicle_ammo2 = spectatee.vehicle_ammo2;
-        this.vehicle_reload1 = spectatee.vehicle_reload1;
-        this.vehicle_reload2 = spectatee.vehicle_reload2;
-
-        msg_entity = this;
-
-        WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
-            WriteAngle(MSG_ONE,  spectatee.v_angle.x);
-            WriteAngle(MSG_ONE,  spectatee.v_angle.y);
-            WriteAngle(MSG_ONE,  spectatee.v_angle.z);
-
-        //WriteByte (MSG_ONE, SVC_SETVIEW);
-        //    WriteEntity(MSG_ONE, this);
-        //makevectors(spectatee.v_angle);
-        //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/
-    }
-}
-
-bool SpectateUpdate(entity this)
-{
-       if(!this.enemy)
-           return false;
-
-       if(!IS_PLAYER(this.enemy) || this == this.enemy)
-       {
-               SetSpectatee(this, NULL);
-               return false;
-       }
-
-       SpectateCopy(this, this.enemy);
-
-       return true;
-}
-
-bool SpectateSet(entity this)
-{
-       if(!IS_PLAYER(this.enemy))
-               return false;
-
-       msg_entity = this;
-       WriteByte(MSG_ONE, SVC_SETVIEW);
-       WriteEntity(MSG_ONE, this.enemy);
-       this.movetype = MOVETYPE_NONE;
-       accuracy_resend(this);
-
-       if(!SpectateUpdate(this))
-               PutObserverInServer(this);
-
-       return true;
-}
-
-void SetSpectatee(entity this, entity spectatee)
-{
-       entity old_spectatee = this.enemy;
-
-       this.enemy = spectatee;
-
-       // WEAPONTODO
-       // these are required to fix the spectator bug with arc
-       if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
-       if(this.enemy && this.enemy.arc_beam) { this.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
-}
-
-bool Spectate(entity this, entity pl)
-{
-       if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
-               return false;
-       pl = M_ARGV(1, entity);
-
-       SetSpectatee(this, pl);
-       return SpectateSet(this);
-}
-
-bool SpectateNext(entity this)
-{
-       other = find(this.enemy, classname, STR_PLAYER);
-
-       if (MUTATOR_CALLHOOK(SpectateNext, this, other))
-               other = M_ARGV(1, entity);
-       else if (!other)
-               other = find(other, classname, STR_PLAYER);
-
-       if(other) { SetSpectatee(this, other); }
-
-       return SpectateSet(this);
-}
-
-bool SpectatePrev(entity this)
-{
-       // NOTE: chain order is from the highest to the lower entnum (unlike find)
-       other = findchain(classname, STR_PLAYER);
-       if (!other) // no player
-               return false;
-
-       entity first = other;
-       // skip players until current spectated player
-       if(this.enemy)
-       while(other && other != this.enemy)
-               other = other.chain;
-
-       switch (MUTATOR_CALLHOOK(SpectatePrev, this, other, first))
-       {
-               case MUT_SPECPREV_FOUND:
-                   other = M_ARGV(1, entity);
-                   break;
-               case MUT_SPECPREV_RETURN:
-                   other = M_ARGV(1, entity);
-                   return true;
-               case MUT_SPECPREV_CONTINUE:
-               default:
-               {
-                       if(other.chain)
-                               other = other.chain;
-                       else
-                               other = first;
-                       break;
-               }
-       }
-
-       SetSpectatee(this, other);
-       return SpectateSet(this);
-}
-
-/*
-=============
-ShowRespawnCountdown()
-
-Update a respawn countdown display.
-=============
-*/
-void ShowRespawnCountdown(entity this)
-{
-       float number;
-       if(!IS_DEAD(this)) // just respawned?
-               return;
-       else
-       {
-               number = ceil(this.respawn_time - time);
-               if(number <= 0)
-                       return;
-               if(number <= this.respawn_countdown)
-               {
-                       this.respawn_countdown = number - 1;
-                       if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
-                               { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); }
-               }
-       }
-}
-
-void LeaveSpectatorMode(entity this)
-{
-       if(this.caplayer)
-               return;
-       if(nJoinAllowed(this, this))
-       {
-               if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
-               {
-                       TRANSMUTE(Player, this);
-
-                       if(autocvar_g_campaign || autocvar_g_balance_teams)
-                               { JoinBestTeam(this, false, true); }
-
-                       if(autocvar_g_campaign)
-                               { campaign_bots_may_start = true; }
-
-                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
-
-                       WITHSELF(this, PutClientInServer());
-
-                       if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
-               }
-               else
-                       stuffcmd(this, "menu_showteamselect\n");
-       }
-       else
-       {
-               // Player may not join because g_maxplayers is set
-               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
-       }
-}
-
-/**
- * Determines whether the player is allowed to join. This depends on cvar
- * g_maxplayers, if it isn't used this function always return true, otherwise
- * it checks whether the number of currently playing players exceeds g_maxplayers.
- * @return int number of free slots for players, 0 if none
- */
-bool nJoinAllowed(entity this, entity ignore)
-{
-       if(!ignore)
-       // this is called that way when checking if anyone may be able to join (to build qcstatus)
-       // so report 0 free slots if restricted
-       {
-               if(autocvar_g_forced_team_otherwise == "spectate")
-                       return false;
-               if(autocvar_g_forced_team_otherwise == "spectator")
-                       return false;
-       }
-
-       if(this.team_forced < 0)
-               return false; // forced spectators can never join
-
-       // TODO simplify this
-       int totalClients = 0;
-       int currentlyPlaying = 0;
-       FOREACH_CLIENT(true, LAMBDA(
-               if(it != ignore)
-                       ++totalClients;
-               if(IS_REAL_CLIENT(it))
-               if(IS_PLAYER(it) || it.caplayer)
-                       ++currentlyPlaying;
-       ));
-
-       if (!autocvar_g_maxplayers)
-               return maxclients - totalClients;
-
-       if(currentlyPlaying < autocvar_g_maxplayers)
-               return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
-
-       return false;
-}
-
-/**
- * Checks whether the client is an observer or spectator, if so, he will get kicked after
- * g_maxplayers_spectator_blocktime seconds
- */
-void checkSpectatorBlock(entity this)
-{
-       if(IS_SPEC(this) || IS_OBSERVER(this))
-       if(!this.caplayer)
-       if(IS_REAL_CLIENT(this))
-       {
-               if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
-                       dropclient(this);
-               }
-       }
-}
-
-void PrintWelcomeMessage(entity this)
-{
-       if(this.motd_actived_time == 0)
-       {
-               if (autocvar_g_campaign) {
-                       if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) {
-                               this.motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message);
-                       }
-               } else {
-                       if (PHYS_INPUT_BUTTON_INFO(this)) {
-                               this.motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
-                       }
-               }
-       }
-       else if(this.motd_actived_time > 0) // showing MOTD or campaign message
-       {
-               if (autocvar_g_campaign) {
-                       if (PHYS_INPUT_BUTTON_INFO(this))
-                               this.motd_actived_time = time;
-                       else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
-                               this.motd_actived_time = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
-                       }
-               } else {
-                       if (PHYS_INPUT_BUTTON_INFO(this))
-                               this.motd_actived_time = time;
-                       else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
-                               this.motd_actived_time = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
-                       }
-               }
-       }
-       else //if(this.motd_actived_time < 0) // just connected, motd is active
-       {
-               if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD
-                       this.motd_actived_time = -2; // wait until BUTTON_INFO gets released
-               else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this))
-               {
-                       // instanctly hide MOTD
-                       this.motd_actived_time = 0;
-                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
-               }
-       }
-}
-
-void ObserverThink(entity this)
-{
-       if ( this.impulse )
-       {
-               MinigameImpulse(this, this.impulse);
-               this.impulse = 0;
-       }
-       float prefered_movetype;
-       if (this.flags & FL_JUMPRELEASED) {
-               if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
-                       this.flags &= ~FL_JUMPRELEASED;
-                       this.flags |= FL_SPAWNING;
-               } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
-                       this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext(this)) {
-                               TRANSMUTE(Spectator, this);
-                       }
-               } else {
-                       prefered_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
-                       if (this.movetype != prefered_movetype)
-                               this.movetype = prefered_movetype;
-               }
-       } else {
-               if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) {
-                       this.flags |= FL_JUMPRELEASED;
-                       if(this.flags & FL_SPAWNING)
-                       {
-                               this.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode(this);
-                               return;
-                       }
-               }
-       }
-}
-
-void SpectatorThink(entity this)
-{
-       if ( this.impulse )
-       {
-               if(MinigameImpulse(this, this.impulse))
-                       this.impulse = 0;
-       }
-       if (this.flags & FL_JUMPRELEASED) {
-               if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
-                       this.flags &= ~FL_JUMPRELEASED;
-                       this.flags |= FL_SPAWNING;
-               } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
-                       this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext(this)) {
-                               TRANSMUTE(Spectator, this);
-                       } else {
-                               TRANSMUTE(Observer, this);
-                               WITHSELF(this, PutClientInServer());
-                       }
-                       this.impulse = 0;
-               } else if(this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) {
-                       this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectatePrev(this)) {
-                               TRANSMUTE(Spectator, this);
-                       } else {
-                               TRANSMUTE(Observer, this);
-                               WITHSELF(this, PutClientInServer());
-                       }
-                       this.impulse = 0;
-               } else if (PHYS_INPUT_BUTTON_ATCK2(this)) {
-                       this.flags &= ~FL_JUMPRELEASED;
-                       TRANSMUTE(Observer, this);
-                       WITHSELF(this, PutClientInServer());
-               } else {
-                       if(!SpectateUpdate(this))
-                               PutObserverInServer(this);
-               }
-       } else {
-               if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) {
-                       this.flags |= FL_JUMPRELEASED;
-                       if(this.flags & FL_SPAWNING)
-                       {
-                               this.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode(this);
-                               return;
-                       }
-               }
-               if(!SpectateUpdate(this))
-                       PutObserverInServer(this);
-       }
-
-       this.flags |= FL_CLIENT | FL_NOTARGET;
-}
-
-void vehicles_enter (entity pl, entity veh);
-void PlayerUseKey(entity this)
-{
-       if (!IS_PLAYER(this))
-               return;
-
-       if(this.vehicle)
-       {
-               if(!gameover)
-               {
-                       vehicles_exit(this.vehicle, VHEF_NORMAL);
-                       return;
-               }
-       }
-       else if(autocvar_g_vehicles_enter)
-       {
-               if(!STAT(FROZEN, this))
-               if(!IS_DEAD(this))
-               if(!gameover)
-               {
-                       entity head, closest_target = NULL;
-                       head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true);
-
-                       while(head) // find the closest acceptable target to enter
-                       {
-                               if(head.vehicle_flags & VHF_ISVEHICLE)
-                               if(!IS_DEAD(head))
-                               if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this)))
-                               if(head.takedamage != DAMAGE_NO)
-                               {
-                                       if(closest_target)
-                                       {
-                                               if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin))
-                                               { closest_target = head; }
-                                       }
-                                       else { closest_target = head; }
-                               }
-
-                               head = head.chain;
-                       }
-
-                       if(closest_target) { vehicles_enter(this, closest_target); return; }
-               }
-       }
-
-       // a use key was pressed; call handlers
-       MUTATOR_CALLHOOK(PlayerUseKey, this);
-}
-
-
-/*
-=============
-PlayerPreThink
-
-Called every frame for each client before the physics are run
-=============
-*/
-.float usekeypressed;
-.float last_vehiclecheck;
-.int items_added;
-void PlayerPreThink ()
-{ENGINE_EVENT();
-       WarpZone_PlayerPhysics_FixVAngle(this);
-
-    STAT(GAMESTARTTIME, this) = game_starttime;
-       STAT(ROUNDSTARTTIME, this) = round_starttime;
-       STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam;
-       STAT(LEADLIMIT, this) = autocvar_leadlimit;
-
-       STAT(WEAPONSINMAP, this) = weaponsInMap;
-
-       if (frametime) {
-               // physics frames: update anticheat stuff
-               anticheat_prethink(this);
-       }
-
-       if (blockSpectators && frametime) {
-               // WORKAROUND: only use dropclient in server frames (frametime set).
-               // Never use it in cl_movement frames (frametime zero).
-               checkSpectatorBlock(this);
-    }
-
-       zoomstate_set = false;
-
-       // Check for nameless players
-       if (isInvisibleString(this.netname)) {
-               this.netname = strzone(sprintf("Player#%d", this.playerid));
-               // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
-       }
-       if (this.netname != this.netname_previous) {
-               if (autocvar_sv_eventlog) {
-                       GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname));
-        }
-               if (this.netname_previous) strunzone(this.netname_previous);
-               this.netname_previous = strzone(this.netname);
-       }
-
-       // version nagging
-       if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) {
-        this.version_nagtime = 0;
-        if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) {
-            // git client
-        } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
-            // git server
-            Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
-        } else {
-            int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion);
-            if (r < 0) { // old client
-                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
-            } else if (r > 0) { // old server
-                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
-            }
-        }
-    }
-
-       // GOD MODE info
-       if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
-       {
-               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
-               this.max_armorvalue = 0;
-       }
-
-       if (STAT(FROZEN, this) == 2)
-       {
-               this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1);
-               this.health = max(1, this.revive_progress * start_health);
-               this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1);
-
-               if (this.revive_progress >= 1)
-                       Unfreeze(this);
-       }
-       else if (STAT(FROZEN, this) == 3)
-       {
-               this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1);
-               this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress );
-
-               if (this.health < 1)
-               {
-                       if (this.vehicle)
-                               vehicles_exit(this.vehicle, VHEF_RELEASE);
-                       if(this.event_damage)
-                               this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
-               }
-               else if (this.revive_progress <= 0)
-                       Unfreeze(this);
-       }
-
-       MUTATOR_CALLHOOK(PlayerPreThink, this);
-
-       if(autocvar_g_vehicles_enter)
-       if(time > this.last_vehiclecheck)
-       if(IS_PLAYER(this))
-       if(!gameover)
-       if(!STAT(FROZEN, this))
-       if(!this.vehicle)
-       if(!IS_DEAD(this))
-       {
-               entity veh;
-               for(veh = NULL; (veh = findflags(veh, vehicle_flags, VHF_ISVEHICLE)); )
-               if(vdist(veh.origin - this.origin, <, autocvar_g_vehicles_enter_radius))
-               if(!IS_DEAD(veh))
-               if(veh.takedamage != DAMAGE_NO)
-               if((veh.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(veh.owner, this))
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
-               else if(!veh.owner)
-               if(!veh.team || SAME_TEAM(this, veh))
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
-               else if(autocvar_g_vehicles_steal)
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
-
-               this.last_vehiclecheck = time + 1;
-       }
-
-       if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
-       {
-               if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed)
-                       PlayerUseKey(this);
-               this.usekeypressed = PHYS_INPUT_BUTTON_USE(this);
-       }
-
-       if (IS_REAL_CLIENT(this))
-               PrintWelcomeMessage(this);
-
-       if (IS_PLAYER(this)) {
-               CheckRules_Player(this);
-
-               if (intermission_running) {
-                       IntermissionThink(this);
-                       return;
-               }
-
-               if (timeout_status == TIMEOUT_ACTIVE) {
-            // don't allow the player to turn around while game is paused
-                       // FIXME turn this into CSQC stuff
-                       this.v_angle = this.lastV_angle;
-                       this.angles = this.lastV_angle;
-                       this.fixangle = true;
-               }
-
-               if (frametime) player_powerups(this);
-
-               if (IS_DEAD(this)) {
-                       if (this.personal && g_race_qualifying) {
-                               if (time > this.respawn_time) {
-                                       STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
-                                       respawn(this);
-                                       this.impulse = CHIMPULSE_SPEEDRUN.impulse;
-                               }
-                       } else {
-                               if (frametime) player_anim(this);
-                               bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
-
-                               if (this.deadflag == DEAD_DYING) {
-                                       if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) {
-                                               this.deadflag = DEAD_RESPAWNING;
-                                       } else if (!button_pressed) {
-                                               this.deadflag = DEAD_DEAD;
-                    }
-                               } else if (this.deadflag == DEAD_DEAD) {
-                                       if (button_pressed) {
-                                               this.deadflag = DEAD_RESPAWNABLE;
-                                       } else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) {
-                                               this.deadflag = DEAD_RESPAWNING;
-                    }
-                               } else if (this.deadflag == DEAD_RESPAWNABLE) {
-                                       if (!button_pressed) {
-                                               this.deadflag = DEAD_RESPAWNING;
-                    }
-                               } else if (this.deadflag == DEAD_RESPAWNING) {
-                                       if (time > this.respawn_time) {
-                                               this.respawn_time = time + 1; // only retry once a second
-                                               this.respawn_time_max = this.respawn_time;
-                                               respawn(this);
-                                       }
-                               }
-
-                               ShowRespawnCountdown(this);
-
-                               if (this.respawn_flags & RESPAWN_SILENT)
-                                       STAT(RESPAWN_TIME, this) = 0;
-                               else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
-                               {
-                                       if (time < this.respawn_time)
-                                               STAT(RESPAWN_TIME, this) = this.respawn_time;
-                                       else if (this.deadflag != DEAD_RESPAWNING)
-                                               STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
-                               }
-                               else
-                                       STAT(RESPAWN_TIME, this) = this.respawn_time;
-                       }
-
-                       // if respawning, invert stat_respawn_time to indicate this, the client translates it
-                       if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
-                               STAT(RESPAWN_TIME, this) *= -1;
-
-                       return;
-               }
-
-               this.prevorigin = this.origin;
-
-               bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
-        .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-               if (this.hook.state) {
-                       do_crouch = false;
-               } else if (this.vehicle) {
-                       do_crouch = false;
-               } else if (STAT(FROZEN, this)) {
-                       do_crouch = false;
-        } else if ((PS(this).m_weapon == WEP_SHOTGUN || PS(this).m_weapon == WEP_SHOCKWAVE) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
-                   // WEAPONTODO: predict
-                       do_crouch = false;
-        }
-
-               if (do_crouch) {
-                       if (!this.crouch) {
-                               this.crouch = true;
-                               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
-                               setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
-                               // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway
-                       }
-               } else if (this.crouch) {
-            tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this);
-            if (!trace_startsolid) {
-                this.crouch = false;
-                this.view_ofs = STAT(PL_VIEW_OFS, this);
-                setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
-            }
-               }
-
-               FixPlayermodel(this);
-
-               // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
-               //if(frametime)
-               {
-                       this.items &= ~this.items_added;
-
-                       W_WeaponFrame(this);
-
-                       this.items_added = 0;
-                       if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01))
-                this.items_added |= IT_FUEL;
-
-                       this.items |= this.items_added;
-               }
-
-               player_regen(this);
-
-               // WEAPONTODO: Add a weapon request for this
-               // rot vortex charge to the charge limit
-               if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time)
-                       this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
-
-               if (frametime) player_anim(this);
-
-               // secret status
-               secrets_setstatus(this);
-
-               // monsters status
-               monsters_setstatus(this);
-
-               this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
-       }
-       else if (gameover) {
-               if (intermission_running) IntermissionThink(this);
-               return;
-       }
-       else if (IS_OBSERVER(this)) {
-               ObserverThink(this);
-       }
-       else if (IS_SPEC(this)) {
-               SpectatorThink(this);
-       }
-
-       // WEAPONTODO: Add weapon request for this
-       if (!zoomstate_set) {
-               SetZoomState(this,
-                       PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this)
-                       || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX)
-                       || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)
-               );
-    }
-
-       int oldspectatee_status = this.spectatee_status;
-       if (IS_SPEC(this)) {
-               this.spectatee_status = etof(this.enemy);
-       } else if (IS_OBSERVER(this)) {
-               this.spectatee_status = etof(this);
-       } else {
-               this.spectatee_status = 0;
-    }
-       if (this.spectatee_status != oldspectatee_status) {
-               ClientData_Touch(this);
-               if (g_race || g_cts) race_InitSpectator();
-       }
-
-       if (this.teamkill_soundtime && time > this.teamkill_soundtime)
-       {
-               this.teamkill_soundtime = 0;
-
-               entity e = this.teamkill_soundsource;
-               entity oldpusher = e.pusher;
-               e.pusher = this;
-               PlayerSound(e, playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY);
-               e.pusher = oldpusher;
-       }
-
-       if (this.taunt_soundtime && time > this.taunt_soundtime) {
-               this.taunt_soundtime = 0;
-               PlayerSound(this, playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT);
-       }
-
-       target_voicescript_next(this);
-
-       // WEAPONTODO: Move into weaponsystem somehow
-       // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
-       if (PS(this).m_weapon == WEP_Null)
-               this.clip_load = this.clip_size = 0;
-}
-
-void DrownPlayer(entity this)
-{
-       if(IS_DEAD(this))
-               return;
-
-       if (this.waterlevel != WATERLEVEL_SUBMERGED)
-       {
-               if(this.air_finished < time)
-                       PlayerSound(this, playersound_gasp, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-               this.air_finished = time + autocvar_g_balance_contents_drowndelay;
-               this.dmg = 2;
-       }
-       else if (this.air_finished < time)
-       {       // drown!
-               if (this.pain_finished < time)
-               {
-                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0');
-                       this.pain_finished = time + 0.5;
-               }
-       }
-}
-
-/*
-=============
-PlayerPostThink
-
-Called every frame for each client after the physics are run
-=============
-*/
-.float idlekick_lasttimeleft;
-void PlayerPostThink ()
-{ENGINE_EVENT();
-       if (sv_maxidle > 0)
-       if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
-       if (IS_REAL_CLIENT(this))
-       if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle)
-       {
-               if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
-               {
-                       if (this.idlekick_lasttimeleft)
-                       {
-                               this.idlekick_lasttimeleft = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
-                       }
-               }
-               else
-               {
-                       float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince));
-                       if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10
-                               if (!this.idlekick_lasttimeleft)
-                                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
-                       }
-                       if (timeleft <= 0) {
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
-                               dropclient(this);
-                               return;
-                       }
-                       else if (timeleft <= 10) {
-                               if (timeleft != this.idlekick_lasttimeleft) {
-                                   Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft));
-                }
-                               this.idlekick_lasttimeleft = timeleft;
-                       }
-               }
-       }
-
-       CheatFrame(this);
-
-       //CheckPlayerJump();
-
-       if (IS_PLAYER(this)) {
-               DrownPlayer(this);
-               CheckRules_Player(this);
-               UpdateChatBubble(this);
-               if (this.impulse) ImpulseCommands(this);
-               if (intermission_running) return; // intermission or finale
-               GetPressedKeys(this);
-       }
-
-       if (this.waypointsprite_attachedforcarrier) {
-           vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id);
-               WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v);
-    }
-
-       playerdemo_write(this);
-
-       CSQCMODEL_AUTOUPDATE(this);
-}
diff --git a/qcsrc/server/cl_client.qh b/qcsrc/server/cl_client.qh
deleted file mode 100644 (file)
index d540bb1..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#pragma once
-
-void ClientState_attach(entity this);
-
-CLASS(Client, Object)
-    /** Client name */
-    ATTRIB(Client, netname, string, this.netname)
-    ATTRIB(Client, colormap, int, this.colormap)
-    ATTRIB(Client, team, int, this.team)
-    ATTRIB(Client, clientcolors, int, this.clientcolors)
-    /** Client IP */
-    ATTRIB(Client, netaddress, string, this.netaddress)
-    ATTRIB(Client, playermodel, string, this.playermodel)
-    ATTRIB(Client, playerskin, int, this.playerskin)
-
-    /** fingerprint of CA key the player used to authenticate */
-    ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp)
-    /** fingerprint of CA key the server used to authenticate to the player */
-    ATTRIB(Client, crypto_mykeyfp, string, this.crypto_mykeyfp)
-    /** fingerprint of ID used by the player entity, or string_null if not identified */
-    ATTRIB(Client, crypto_idfp, string, this.crypto_idfp)
-    /** set if the player's ID has been signed */
-    ATTRIB(Client, crypto_idfp_signed, bool, this.crypto_idfp_signed)
-    /** the string "AES128" if encrypting, and string_null if plaintext */
-    ATTRIB(Client, crypto_encryptmethod, string, this.crypto_encryptmethod)
-    /** the string "HMAC-SHA256" if signing, and string_null if plaintext */
-    ATTRIB(Client, crypto_signmethod, string, this.crypto_signmethod)
-
-    // custom
-
-    ATTRIB(Client, playerid, int, this.playerid)
-
-    METHOD(Client, m_unwind, bool(Client this));
-
-    STATIC_METHOD(Client, Add, void(Client this, int _team));
-    STATIC_METHOD(Client, Remove, void(Client this));
-
-    INIT(Client) {
-        if (this.m_unwind(this)) return this;
-        make_impure(this);
-        this.classname = "player_joining";
-        static int playerid_last;
-        this.playerid = ++playerid_last;
-        ClientState_attach(this);
-    }
-    DESTRUCTOR(Client) {
-        Client_Remove(this);
-    }
-    CONSTRUCTOR(Client, string name) {
-        CONSTRUCT(Client);
-        this.netname = name;
-        this.netaddress = "local";
-        this.playermodel = "models/player/megaerebus.iqm";
-    }
-ENDCLASS(Client)
-
-CLASS(Observer, Client)
-    INIT(Observer) {
-        this.classname = STR_OBSERVER;
-    }
-    DESTRUCTOR(Observer) { }
-ENDCLASS(Observer)
-
-CLASS(Spectator, Client)
-    INIT(Spectator) {
-        this.classname = STR_SPECTATOR;
-    }
-    DESTRUCTOR(Spectator) { }
-ENDCLASS(Spectator)
-
-CLASS(Player, Client)
-    INIT(Player) {
-        this.classname = STR_PLAYER;
-    }
-    DESTRUCTOR(Player) { }
-ENDCLASS(Player)
-
-METHOD(Client, m_unwind, bool(Client this))
-{
-    TC(Client, this);
-    #define UNWIND(class) MACRO_BEGIN if (this.instanceOf##class) { METHOD_REFERENCE(class, dtorimpl)(this); } MACRO_END
-    switch (this.classname) {
-        case "Observer":
-            UNWIND(Spectator);
-            UNWIND(Player);
-            return true;
-        case "Spectator":
-            UNWIND(Observer);
-            UNWIND(Player);
-            return true;
-        case "Player":
-            UNWIND(Observer);
-            UNWIND(Spectator);
-            return true;
-    }
-    #undef UNWIND
-    return false;
-}
-
-float c1, c2, c3, c4;
-
-void play_countdown(entity this, float finished, Sound samp);
-
-float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit);
-
-bool Spectate(entity this, entity pl);
-
-#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee)
-#define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); }
diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc
deleted file mode 100644 (file)
index 517ee37..0000000
+++ /dev/null
@@ -1,607 +0,0 @@
-#include "cl_impulse.qh"
-#include "round_handler.qh"
-
-#include "bot/waypoints.qh"
-
-#include "weapons/throwing.qh"
-#include "command/common.qh"
-#include "cheats.qh"
-#include "bot/navigation.qh"
-#include "weapons/selection.qh"
-#include "weapons/tracing.qh"
-#include "weapons/weaponsystem.qh"
-
-#include <common/state.qh>
-
-#include "../common/minigames/sv_minigames.qh"
-
-#include "../common/weapons/all.qh"
-#include "../common/vehicles/sv_vehicles.qh"
-
-#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
-
-.entity vehicle;
-
-#define IMPULSE(id) _IMPULSE(IMP_##id)
-#define _IMPULSE(id) \
-       void id##_handle(entity this); \
-       STATIC_INIT_LATE(id) \
-       { \
-               id.impulse_handle = id##_handle; \
-       } \
-       void id##_handle(entity this)
-
-/**
- * Impulse map:
- *
- * 0 reserved (no input)
- *
- * 99: loaded
- *
- * 140: moving clone
- * 141: ctf speedrun
- * 142: fixed clone
- * 143: emergency teleport
- * 148: unfairly eliminate
- *
- * TODO:
- * 200 to 209: prev weapon shortcuts
- * 210 to 219: best weapon shortcuts
- * 220 to 229: next weapon shortcuts
- * 230 to 253: individual weapons (up to 24)
- */
-
-// weapon switching impulses
-
-#define X(slot) \
-       IMPULSE(weapon_group_##slot) \
-       { \
-               if (IS_DEAD(this)) \
-               { \
-                       this.impulse = IMP_weapon_group_##slot.impulse; \
-                       return; \
-               } \
-               W_NextWeaponOnImpulse(this, slot); \
-       }
-X(1)
-X(2)
-X(3)
-X(4)
-X(5)
-X(6)
-X(7)
-X(8)
-X(9)
-X(0)
-#undef X
-
-// custom order weapon cycling
-
-#define X(slot, dir) \
-       IMPULSE(weapon_priority_##slot##_##dir) \
-       { \
-               if (this.vehicle) return; \
-               if (IS_DEAD(this)) \
-               { \
-                       this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
-                       return; \
-               } \
-               noref int prev = -1; \
-               noref int best =  0; \
-               noref int next = +1; \
-               W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \
-       }
-X(0, prev)
-X(1, prev)
-X(2, prev)
-X(3, prev)
-X(4, prev)
-X(5, prev)
-X(6, prev)
-X(7, prev)
-X(8, prev)
-X(9, prev)
-
-X(0, best)
-X(1, best)
-X(2, best)
-X(3, best)
-X(4, best)
-X(5, best)
-X(6, best)
-X(7, best)
-X(8, best)
-X(9, best)
-
-X(0, next)
-X(1, next)
-X(2, next)
-X(3, next)
-X(4, next)
-X(5, next)
-X(6, next)
-X(7, next)
-X(8, next)
-X(9, next)
-#undef X
-
-// direct weapons
-
-#define X(i) \
-       IMPULSE(weapon_byid_##i) \
-       { \
-               if (this.vehicle) return; \
-               if (IS_DEAD(this)) \
-               { \
-                       this.impulse = IMP_weapon_byid_##i.impulse; \
-                       return; \
-               } \
-               W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \
-       }
-X(0)
-X(1)
-X(2)
-X(3)
-X(4)
-X(5)
-X(6)
-X(7)
-X(8)
-X(9)
-X(10)
-X(11)
-X(12)
-X(13)
-X(14)
-X(15)
-X(16)
-X(17)
-X(18)
-X(19)
-X(20)
-X(21)
-X(22)
-X(23)
-#undef X
-
-IMPULSE(weapon_next_byid)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this))
-       {
-               this.impulse = IMP_weapon_next_byid.impulse;
-               return;
-       }
-       W_NextWeapon(this, 0);
-}
-
-IMPULSE(weapon_prev_byid)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this))
-       {
-               this.impulse = IMP_weapon_prev_byid.impulse;
-               return;
-       }
-       W_PreviousWeapon(this, 0);
-}
-
-IMPULSE(weapon_next_bygroup)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this))
-       {
-               this.impulse = IMP_weapon_next_bygroup.impulse;
-               return;
-       }
-       W_NextWeapon(this, 1);
-}
-
-IMPULSE(weapon_prev_bygroup)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this))
-       {
-               this.impulse = IMP_weapon_prev_bygroup.impulse;
-               return;
-       }
-       W_PreviousWeapon(this, 1);
-}
-
-IMPULSE(weapon_next_bypriority)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this))
-       {
-               this.impulse = IMP_weapon_next_bypriority.impulse;
-               return;
-       }
-       W_NextWeapon(this, 2);
-}
-
-IMPULSE(weapon_prev_bypriority)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this))
-       {
-               this.impulse = IMP_weapon_prev_bypriority.impulse;
-               return;
-       }
-       W_PreviousWeapon(this, 2);
-}
-
-IMPULSE(weapon_last)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this)) return;
-       W_LastWeapon(this);
-}
-
-IMPULSE(weapon_best)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this)) return;
-       W_SwitchWeapon(this, w_getbestweapon(this));
-}
-
-IMPULSE(weapon_drop)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this)) return;
-       W_ThrowWeapon(this, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
-}
-
-IMPULSE(weapon_reload)
-{
-       if (this.vehicle) return;
-       if (IS_DEAD(this)) return;
-       if (forbidWeaponUse(this)) return;
-       Weapon w = PS(this).m_weapon;
-       entity actor = this;
-       .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-       w.wr_reload(w, actor, weaponentity);
-}
-
-void ImpulseCommands(entity this)
-{
-       if (gameover) return;
-
-       int imp = this.impulse;
-       if (!imp) return;
-       this.impulse = 0;
-
-       if (MinigameImpulse(this, imp)) return;
-
-       if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
-
-       // allow only weapon change impulses when not in round time
-       if (round_handler_IsActive() && !round_handler_IsRoundStarted())
-       {
-               #define X(id) case IMP_##id.impulse:
-               switch (imp)
-               {
-                       X(weapon_group_0)
-                       X(weapon_group_1)
-                       X(weapon_group_2)
-                       X(weapon_group_3)
-                       X(weapon_group_4)
-                       X(weapon_group_5)
-                       X(weapon_group_6)
-                       X(weapon_group_7)
-                       X(weapon_group_8)
-                       X(weapon_group_9)
-                       X(weapon_next_byid)
-                       X(weapon_prev_byid)
-                       X(weapon_next_bygroup)
-                       X(weapon_prev_bygroup)
-                       X(weapon_next_bypriority)
-                       X(weapon_prev_bypriority)
-                       X(weapon_last)
-                       X(weapon_best)
-                       X(weapon_reload)
-                       X(weapon_priority_0_prev)
-            X(weapon_priority_1_prev)
-            X(weapon_priority_2_prev)
-            X(weapon_priority_3_prev)
-            X(weapon_priority_4_prev)
-            X(weapon_priority_5_prev)
-            X(weapon_priority_6_prev)
-            X(weapon_priority_7_prev)
-            X(weapon_priority_8_prev)
-            X(weapon_priority_9_prev)
-            X(weapon_priority_0_next)
-                       X(weapon_priority_1_next)
-                       X(weapon_priority_2_next)
-                       X(weapon_priority_3_next)
-                       X(weapon_priority_4_next)
-                       X(weapon_priority_5_next)
-                       X(weapon_priority_6_next)
-                       X(weapon_priority_7_next)
-                       X(weapon_priority_8_next)
-                       X(weapon_priority_9_next)
-                       X(weapon_priority_0_best)
-            X(weapon_priority_1_best)
-            X(weapon_priority_2_best)
-            X(weapon_priority_3_best)
-            X(weapon_priority_4_best)
-            X(weapon_priority_5_best)
-            X(weapon_priority_6_best)
-            X(weapon_priority_7_best)
-            X(weapon_priority_8_best)
-            X(weapon_priority_9_best)
-            X(weapon_byid_0)
-            X(weapon_byid_1)
-            X(weapon_byid_2)
-            X(weapon_byid_3)
-            X(weapon_byid_4)
-            X(weapon_byid_5)
-            X(weapon_byid_6)
-            X(weapon_byid_7)
-            X(weapon_byid_8)
-            X(weapon_byid_9)
-            X(weapon_byid_10)
-            X(weapon_byid_11)
-            X(weapon_byid_12)
-            X(weapon_byid_13)
-            X(weapon_byid_14)
-            X(weapon_byid_15)
-            X(weapon_byid_16)
-            X(weapon_byid_17)
-            X(weapon_byid_18)
-            X(weapon_byid_19)
-            X(weapon_byid_20)
-            X(weapon_byid_21)
-            X(weapon_byid_22)
-            X(weapon_byid_23)
-                       break;
-                       default: return;
-               }
-#undef X
-       }
-
-       if (vehicle_impulse(this, imp)) return;
-
-       if (CheatImpulse(this, imp)) return;
-
-       FOREACH(IMPULSES, it.impulse == imp, {
-               void(entity) f = it.impulse_handle;
-               if (!f) continue;
-               f(this);
-               return;
-       });
-}
-
-IMPULSE(use)
-{
-       PlayerUseKey(this);
-}
-
-IMPULSE(waypoint_personal_here)
-{
-       entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "personal waypoint spawned at location\n");
-}
-
-IMPULSE(waypoint_personal_crosshair)
-{
-       WarpZone_crosshair_trace(this);
-       entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "personal waypoint spawned at crosshair\n");
-}
-
-IMPULSE(waypoint_personal_death)
-{
-       if (!this.death_origin) return;
-       entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "personal waypoint spawned at death location\n");
-}
-
-IMPULSE(waypoint_here_follow)
-{
-       if (!teamplay) return;
-       if (IS_DEAD(this)) return;
-       if (!MUTATOR_CALLHOOK(HelpMePing, this))
-       {
-               entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
-               if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
-               else WaypointSprite_Ping(wp);
-       }
-       sprint(this, "HELP ME attached\n");
-}
-
-IMPULSE(waypoint_here_here)
-{
-       entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "HERE spawned at location\n");
-}
-
-IMPULSE(waypoint_here_crosshair)
-{
-       WarpZone_crosshair_trace(this);
-       entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "HERE spawned at crosshair\n");
-}
-
-IMPULSE(waypoint_here_death)
-{
-       if (!this.death_origin) return;
-       entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "HERE spawned at death location\n");
-}
-
-IMPULSE(waypoint_danger_here)
-{
-       entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "DANGER spawned at location\n");
-}
-
-IMPULSE(waypoint_danger_crosshair)
-{
-       WarpZone_crosshair_trace(this);
-       entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "DANGER spawned at crosshair\n");
-}
-
-IMPULSE(waypoint_danger_death)
-{
-       if (!this.death_origin) return;
-       entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
-       if (wp) WaypointSprite_Ping(wp);
-       sprint(this, "DANGER spawned at death location\n");
-}
-
-IMPULSE(waypoint_clear_personal)
-{
-       WaypointSprite_ClearPersonal(this);
-       if (this.personal)
-       {
-               remove(this.personal);
-               this.personal = NULL;
-       }
-       sprint(this, "personal waypoint cleared\n");
-}
-
-IMPULSE(waypoint_clear)
-{
-       WaypointSprite_ClearOwned(this);
-       if (this.personal)
-       {
-               remove(this.personal);
-               this.personal = NULL;
-       }
-       sprint(this, "all waypoints cleared\n");
-}
-
-IMPULSE(navwaypoint_spawn)
-{
-       if (!autocvar_g_waypointeditor) return;
-       waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
-       bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
-}
-
-IMPULSE(navwaypoint_remove)
-{
-       if (!autocvar_g_waypointeditor) return;
-       entity e = navigation_findnearestwaypoint(this, false);
-       if (!e) return;
-       if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
-       bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
-       waypoint_remove(e);
-}
-
-IMPULSE(navwaypoint_relink)
-{
-       if (!autocvar_g_waypointeditor) return;
-       waypoint_schedulerelinkall();
-}
-
-IMPULSE(navwaypoint_save)
-{
-       if (!autocvar_g_waypointeditor) return;
-       waypoint_saveall();
-}
-
-IMPULSE(navwaypoint_unreachable)
-{
-       if (!autocvar_g_waypointeditor) return;
-       for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
-       {
-               e.colormod = '0.5 0.5 0.5';
-               e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
-       }
-       entity e2 = navigation_findnearestwaypoint(this, false);
-       navigation_markroutes(this, e2);
-
-       int i, m;
-
-       i = 0;
-       m = 0;
-       for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
-       {
-               if (e.wpcost < 10000000) continue;
-               LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n");
-               e.colormod_z = 8;
-               e.effects |= EF_NODEPTHTEST | EF_BLUE;
-               ++i;
-               ++m;
-       }
-       if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
-       navigation_markroutes_inverted(e2);
-
-       i = 0;
-       for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
-       {
-               if (e.wpcost < 10000000) continue;
-               LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n");
-               e.colormod_x = 8;
-               if (!(e.effects & EF_NODEPTHTEST))  // not already reported before
-                       ++m;
-               e.effects |= EF_NODEPTHTEST | EF_RED;
-               ++i;
-       }
-       if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i);
-       if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
-
-       i = 0;
-       for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
-       {
-               vector org = e.origin;
-               tracebox(e.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), e.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
-               setorigin(e, trace_endpos);
-               if (navigation_findnearestwaypoint(e, false))
-               {
-                       setorigin(e, org);
-                       e.effects &= ~EF_NODEPTHTEST;
-                       e.model = "";
-               }
-               else
-               {
-                       setorigin(e, org);
-                       LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
-                       e.effects |= EF_NODEPTHTEST;
-                       _setmodel(e, this.model);
-                       e.frame = this.frame;
-                       e.skin = this.skin;
-                       e.colormod = '8 0.5 8';
-                       setsize(e, '0 0 0', '0 0 0');
-                       ++i;
-               }
-       }
-       if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
-
-       i = 0;
-       entity start = findchainflags(flags, FL_ITEM);
-       for (entity e = start; e; e = e.chain)
-       {
-               e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
-               e.colormod = '0.5 0.5 0.5';
-       }
-       for (entity e = start; e; e = e.chain)
-       {
-               if (navigation_findnearestwaypoint(e, false)) continue;
-               LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
-               e.effects |= EF_NODEPTHTEST | EF_RED;
-               e.colormod_x = 8;
-               ++i;
-       }
-       if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i);
-
-       i = 0;
-       for (entity e = start; e; e = e.chain)
-       {
-               if (navigation_findnearestwaypoint(e, true)) continue;
-               LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
-               e.effects |= EF_NODEPTHTEST | EF_BLUE;
-               e.colormod_z = 8;
-               ++i;
-       }
-       if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i);
-}
diff --git a/qcsrc/server/cl_impulse.qh b/qcsrc/server/cl_impulse.qh
deleted file mode 100644 (file)
index 50edc2c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-void ImpulseCommands(entity this);
diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc
deleted file mode 100644 (file)
index 4c54ddd..0000000
+++ /dev/null
@@ -1,952 +0,0 @@
-#include "cl_player.qh"
-
-#include "bot/bot.qh"
-#include "cheats.qh"
-#include "g_damage.qh"
-#include "g_subs.qh"
-#include "miscfunctions.qh"
-#include "portals.qh"
-#include "teamplay.qh"
-#include "weapons/throwing.qh"
-#include "command/common.qh"
-#include "../common/state.qh"
-#include "../common/anim.qh"
-#include "../common/animdecide.qh"
-#include "../common/csqcmodel_settings.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/triggers/subs.qh"
-#include "../common/playerstats.qh"
-#include "../lib/csqcmodel/sv_model.qh"
-
-#include "../common/minigames/sv_minigames.qh"
-
-#include "../common/physics/player.qh"
-#include "../common/effects/qc/all.qh"
-#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
-#include "../common/triggers/include.qh"
-
-#include "weapons/weaponstats.qh"
-
-#include "../common/animdecide.qh"
-
-void Drop_Special_Items(entity player)
-{
-       // called when the player has become stuck or frozen
-       // so objective items aren't stuck with the player
-
-       MUTATOR_CALLHOOK(DropSpecialItems, player);
-}
-
-void CopyBody_Think(entity this)
-{
-       if(this.CopyBody_nextthink && time > this.CopyBody_nextthink)
-       {
-               this.CopyBody_think(this);
-               if(wasfreed(this))
-                       return;
-               this.CopyBody_nextthink = this.nextthink;
-               this.CopyBody_think = getthink(this);
-               setthink(this, CopyBody_Think);
-       }
-       CSQCMODEL_AUTOUPDATE(this);
-       this.nextthink = time;
-}
-void CopyBody(entity this, float keepvelocity)
-{
-       if (this.effects & EF_NODRAW)
-               return;
-       entity clone = new(body);
-       clone.enemy = this;
-       clone.lip = this.lip;
-       clone.colormap = this.colormap;
-       clone.iscreature = this.iscreature;
-       clone.teleportable = this.teleportable;
-       clone.damagedbycontents = this.damagedbycontents;
-       clone.angles = this.angles;
-       clone.v_angle = this.v_angle;
-       clone.avelocity = this.avelocity;
-       clone.damageforcescale = this.damageforcescale;
-       clone.effects = this.effects;
-       clone.glowmod = this.glowmod;
-       clone.event_damage = this.event_damage;
-       clone.anim_state = this.anim_state;
-       clone.anim_time = this.anim_time;
-       clone.anim_lower_action = this.anim_lower_action;
-       clone.anim_lower_time = this.anim_lower_time;
-       clone.anim_upper_action = this.anim_upper_action;
-       clone.anim_upper_time = this.anim_upper_time;
-       clone.anim_implicit_state = this.anim_implicit_state;
-       clone.anim_implicit_time = this.anim_implicit_time;
-       clone.anim_lower_implicit_action = this.anim_lower_implicit_action;
-       clone.anim_lower_implicit_time = this.anim_lower_implicit_time;
-       clone.anim_upper_implicit_action = this.anim_upper_implicit_action;
-       clone.anim_upper_implicit_time = this.anim_upper_implicit_time;
-       clone.dphitcontentsmask = this.dphitcontentsmask;
-       clone.death_time = this.death_time;
-       clone.pain_finished = this.pain_finished;
-       clone.health = this.health;
-       clone.armorvalue = this.armorvalue;
-       clone.armortype = this.armortype;
-       clone.model = this.model;
-       clone.modelindex = this.modelindex;
-       clone.skin = this.skin;
-       clone.species = this.species;
-       clone.movetype = this.movetype;
-       clone.solid = this.solid;
-       clone.ballistics_density = this.ballistics_density;
-       clone.takedamage = this.takedamage;
-       setcefc(clone, getcefc(this));
-       clone.uncustomizeentityforclient = this.uncustomizeentityforclient;
-       clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set;
-       if (keepvelocity == 1)
-               clone.velocity = this.velocity;
-       clone.oldvelocity = clone.velocity;
-       clone.alpha = this.alpha;
-       clone.fade_time = this.fade_time;
-       clone.fade_rate = this.fade_rate;
-       //clone.weapon = this.weapon;
-       setorigin(clone, this.origin);
-       setsize(clone, this.mins, this.maxs);
-       clone.prevorigin = this.origin;
-       clone.reset = SUB_Remove;
-       clone._ps = this._ps;
-
-       Drag_MoveDrag(this, clone);
-
-       if(clone.colormap <= maxclients && clone.colormap > 0)
-               clone.colormap = 1024 + this.clientcolors;
-
-       CSQCMODEL_AUTOINIT(clone);
-       clone.CopyBody_nextthink = this.nextthink;
-       clone.CopyBody_think = getthink(this);
-       clone.nextthink = time;
-       setthink(clone, CopyBody_Think);
-       // "bake" the current animation frame for clones (they don't get clientside animation)
-       animdecide_load_if_needed(clone);
-       animdecide_setframes(clone, false, frame, frame1time, frame2, frame2time);
-}
-
-void player_setupanimsformodel(entity this)
-{
-       // load animation info
-       animdecide_load_if_needed(this);
-       animdecide_setstate(this, 0, false);
-}
-
-void player_anim(entity this)
-{
-       int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
-       if(IS_DEAD(this)) {
-               if (!deadbits) {
-                       // Decide on which death animation to use.
-                       if(random() < 0.5)
-                               deadbits = ANIMSTATE_DEAD1;
-                       else
-                               deadbits = ANIMSTATE_DEAD2;
-               }
-       } else {
-               // Clear a previous death animation.
-               deadbits = 0;
-       }
-       int animbits = deadbits;
-       if(STAT(FROZEN, this))
-               animbits |= ANIMSTATE_FROZEN;
-       if(this.movetype == MOVETYPE_FOLLOW)
-               animbits |= ANIMSTATE_FOLLOW;
-       if(this.crouch)
-               animbits |= ANIMSTATE_DUCK;
-       animdecide_setstate(this, animbits, false);
-       animdecide_setimplicitstate(this, IS_ONGROUND(this));
-}
-
-void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       float take, save;
-       vector v;
-       Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
-
-       // damage resistance (ignore most of the damage from a bullet or similar)
-       damage = max(damage - 5, 1);
-
-       v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage);
-       take = v.x;
-       save = v.y;
-
-       if(sound_allowed(MSG_BROADCAST, attacker))
-       {
-               if (save > 10)
-                       sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
-               else if (take > 30)
-                       sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
-               else if (take > 10)
-                       sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM);
-       }
-
-       if (take > 50)
-               Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker);
-       if (take > 100)
-               Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
-
-       this.armorvalue = this.armorvalue - save;
-       this.health = this.health - take;
-       // pause regeneration for 5 seconds
-       this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
-
-       this.dmg_save = this.dmg_save + save;//max(save - 10, 0);
-       this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
-       this.dmg_inflictor = inflictor;
-
-       if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0)
-       {
-               // don't use any animations as a gib
-               this.frame = 0;
-               // view just above the floor
-               this.view_ofs = '0 0 4';
-
-               Violence_GibSplash(this, 1, 1, attacker);
-               this.alpha = -1;
-               this.solid = SOLID_NOT; // restore later
-               this.takedamage = DAMAGE_NO; // restore later
-               this.damagedbycontents = false;
-       }
-}
-
-void calculate_player_respawn_time(entity this)
-{
-       if(g_ca)
-               return;
-
-       float gametype_setting_tmp;
-       float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
-       float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
-       float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large);
-       float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count);
-       float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count);
-       float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves);
-
-       float pcount = 1;  // Include myself whether or not team is already set right and I'm a "player".
-       if (teamplay)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA(
-                       if(it.team == this.team)
-                               ++pcount;
-               ));
-               if (sdelay_small_count == 0)
-                       sdelay_small_count = 1;
-               if (sdelay_large_count == 0)
-                       sdelay_large_count = 1;
-       }
-       else
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA(
-                       ++pcount;
-               ));
-               if (sdelay_small_count == 0)
-               {
-                       if (g_cts)
-                       {
-                               // Players play independently. No point in requiring enemies.
-                               sdelay_small_count = 1;
-                       }
-                       else
-                       {
-                               // Players play AGAINST each other. Enemies required.
-                               sdelay_small_count = 2;
-                       }
-               }
-               if (sdelay_large_count == 0)
-               {
-                       if (g_cts)
-                       {
-                               // Players play independently. No point in requiring enemies.
-                               sdelay_large_count = 1;
-                       }
-                       else
-                       {
-                               // Players play AGAINST each other. Enemies required.
-                               sdelay_large_count = 2;
-                       }
-               }
-       }
-
-       float sdelay;
-
-       if (pcount <= sdelay_small_count)
-               sdelay = sdelay_small;
-       else if (pcount >= sdelay_large_count)
-               sdelay = sdelay_large;
-       else  // NOTE: this case implies sdelay_large_count > sdelay_small_count.
-               sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count);
-
-       if(waves)
-               this.respawn_time = ceil((time + sdelay) / waves) * waves;
-       else
-               this.respawn_time = time + sdelay;
-
-       if(sdelay < sdelay_max)
-               this.respawn_time_max = time + sdelay_max;
-       else
-               this.respawn_time_max = this.respawn_time;
-
-       if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75))
-               this.respawn_countdown = 10; // first number to count down from is 10
-       else
-               this.respawn_countdown = -1; // do not count down
-
-       if(autocvar_g_forced_respawn)
-               this.respawn_flags = this.respawn_flags | RESPAWN_FORCE;
-}
-
-void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       float take, save, dh, da;
-       vector v;
-       float valid_damage_for_weaponstats;
-       float excess;
-
-       dh = max(this.health, 0);
-       da = max(this.armorvalue, 0);
-
-       if(!DEATH_ISSPECIAL(deathtype))
-       {
-               damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0));
-               if(this != attacker)
-                       damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0));
-       }
-
-       if(DEATH_ISWEAPON(deathtype, WEP_TUBA))
-       {
-               // tuba causes blood to come out of the ears
-               vector ear1, ear2;
-               vector d;
-               float f;
-               ear1 = this.origin;
-               ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8
-               ear2 = ear1;
-               makevectors(this.angles);
-               ear1 += v_right * -10;
-               ear2 += v_right * +10;
-               d = inflictor.origin - this.origin;
-               if (d)
-                       f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right!
-               else
-                       f = 0;  // Assum ecenter.
-               force = v_right * vlen(force);
-               Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker);
-               Violence_GibSplash_At(ear2, force,      2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker);
-               if(f > 0)
-               {
-                       hitloc = ear1;
-                       force = force * -1;
-               }
-               else
-               {
-                       hitloc = ear2;
-                       // force is already good
-               }
-       }
-       else
-               Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
-
-
-       v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage);
-       take = v.x;
-       save = v.y;
-
-       if(attacker == this)
-       {
-               // don't reset pushltime for this damage as it may be an attempt to
-               // escape a lava pit or similar
-               //this.pushltime = 0;
-               this.istypefrag = 0;
-       }
-       else if(IS_PLAYER(attacker))
-       {
-               this.pusher = attacker;
-               this.pushltime = time + autocvar_g_maxpushtime;
-               this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this);
-       }
-       else if(time < this.pushltime)
-       {
-               attacker = this.pusher;
-               this.pushltime = max(this.pushltime, time + 0.6);
-       }
-       else
-       {
-               this.pushltime = 0;
-               this.istypefrag = 0;
-       }
-
-       if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
-       {
-               vector v = healtharmor_applydamage(this.armorvalue, max(0, autocvar_g_spawnshield_blockdamage), deathtype, damage);
-               take = v.x;
-               save = v.y;
-       }
-
-       MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage);
-       take = bound(0, M_ARGV(4, float), this.health);
-       save = bound(0, M_ARGV(5, float), this.armorvalue);
-       excess = max(0, damage - take - save);
-
-       if(sound_allowed(MSG_BROADCAST, attacker))
-       {
-               if (save > 10)
-                       sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
-               else if (take > 30)
-                       sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
-               else if (take > 10)
-                       sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them?
-       }
-
-       if (take > 50)
-               Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker);
-       if (take > 100)
-               Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
-
-       if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1)
-       {
-               if (!(this.flags & FL_GODMODE))
-               {
-                       this.armorvalue = this.armorvalue - save;
-                       this.health = this.health - take;
-                       // pause regeneration for 5 seconds
-                       if(take)
-                               this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
-
-                       if (time > this.pain_finished)          //Don't switch pain sequences like crazy
-                       {
-                               this.pain_finished = time + 0.5;        //Supajoe
-
-                               if(autocvar_sv_gentle < 1) {
-                                       if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
-                                       {
-                                               if (!this.animstate_override)
-                                               {
-                                                       if (random() > 0.5)
-                                                               animdecide_setaction(this, ANIMACTION_PAIN1, true);
-                                                       else
-                                                               animdecide_setaction(this, ANIMACTION_PAIN2, true);
-                                               }
-                                       }
-
-                                       if(sound_allowed(MSG_BROADCAST, attacker))
-                                       if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser
-                                       if(this.health > 1)
-                                       // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
-                                       {
-                                               if(deathtype == DEATH_FALL.m_id)
-                                                       PlayerSound(this, playersound_fall, CH_PAIN, VOICETYPE_PLAYERSOUND);
-                                               else if(this.health > 75)
-                                                       PlayerSound(this, playersound_pain100, CH_PAIN, VOICETYPE_PLAYERSOUND);
-                                               else if(this.health > 50)
-                                                       PlayerSound(this, playersound_pain75, CH_PAIN, VOICETYPE_PLAYERSOUND);
-                                               else if(this.health > 25)
-                                                       PlayerSound(this, playersound_pain50, CH_PAIN, VOICETYPE_PLAYERSOUND);
-                                               else
-                                                       PlayerSound(this, playersound_pain25, CH_PAIN, VOICETYPE_PLAYERSOUND);
-                                       }
-                               }
-                       }
-
-                       // throw off bot aim temporarily
-                       float shake;
-                       if(IS_BOT_CLIENT(this) && this.health >= 1)
-                       {
-                               shake = damage * 5 / (bound(0,skill,100) + 1);
-                               this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake;
-                               this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake;
-                               this.v_angle_x = bound(-90, this.v_angle.x, 90);
-                       }
-               }
-               else
-                       this.max_armorvalue += (save + take);
-       }
-       this.dmg_save = this.dmg_save + save;//max(save - 10, 0);
-       this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
-       this.dmg_inflictor = inflictor;
-
-       if (this != attacker) {
-               float realdmg = damage - excess;
-               if (IS_PLAYER(attacker)) {
-                       PlayerScore_Add(attacker, SP_DMG, realdmg);
-               }
-               if (IS_PLAYER(this)) {
-                       PlayerScore_Add(this, SP_DMGTAKEN, realdmg);
-               }
-       }
-
-       bool abot = (IS_BOT_CLIENT(attacker));
-       bool vbot = (IS_BOT_CLIENT(this));
-
-       valid_damage_for_weaponstats = 0;
-       Weapon awep = WEP_Null;
-
-       if(vbot || IS_REAL_CLIENT(this))
-       if(abot || IS_REAL_CLIENT(attacker))
-       if(attacker && this != attacker)
-       if(DIFF_TEAM(this, attacker))
-       {
-               if(DEATH_ISSPECIAL(deathtype))
-                       awep = PS(attacker).m_weapon;
-               else
-                       awep = DEATH_WEAPONOF(deathtype);
-               valid_damage_for_weaponstats = 1;
-       }
-
-       if(valid_damage_for_weaponstats)
-       {
-               dh = dh - max(this.health, 0);
-               da = da - max(this.armorvalue, 0);
-               WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da);
-               MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype);
-       }
-
-       if (this.health < 1)
-       {
-               float defer_ClientKill_Now_TeamChange;
-               defer_ClientKill_Now_TeamChange = false;
-
-               if(this.alivetime)
-               {
-                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
-                       this.alivetime = 0;
-               }
-
-               if(valid_damage_for_weaponstats)
-                       WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot);
-
-               if(autocvar_sv_gentle < 1)
-               if(sound_allowed(MSG_BROADCAST, attacker))
-               {
-                       if(deathtype == DEATH_DROWN.m_id)
-                               PlayerSound(this, playersound_drown, CH_PAIN, VOICETYPE_PLAYERSOUND);
-                       else
-                               PlayerSound(this, playersound_death, CH_PAIN, VOICETYPE_PLAYERSOUND);
-               }
-
-               // get rid of kill indicator
-               if(this.killindicator)
-               {
-                       remove(this.killindicator);
-                       this.killindicator = NULL;
-                       if(this.killindicator_teamchange)
-                               defer_ClientKill_Now_TeamChange = true;
-
-                       if(this.classname == "body")
-                       if(deathtype == DEATH_KILL.m_id)
-                       {
-                               // for the lemmings fans, a small harmless explosion
-                               Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
-                       }
-               }
-
-               // print an obituary message
-               if(this.classname != "body")
-                       Obituary (attacker, inflictor, this, deathtype);
-
-        // increment frag counter for used weapon type
-        Weapon w = DEATH_WEAPONOF(deathtype);
-        if(w != WEP_Null)
-       if(accuracy_isgooddamage(attacker, this))
-        attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1;
-
-               MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
-               excess = M_ARGV(4, float);
-
-               Weapon wep = PS(this).m_weapon;
-               wep.wr_playerdeath(wep, this);
-
-               RemoveGrapplingHook(this);
-
-               Portal_ClearAllLater(this);
-
-               this.fixangle = true;
-
-               if(defer_ClientKill_Now_TeamChange)
-                       ClientKill_Now_TeamChange(this); // can turn player into spectator
-
-               // player could have been miraculously resuscitated ;)
-               // e.g. players in freezetag get frozen, they don't really die
-               if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body"))
-                       return;
-
-               // when we get here, player actually dies
-
-               Unfreeze(this); // remove any icy remains
-               this.health = 0; // Unfreeze resets health, so we need to set it back
-
-               // clear waypoints
-               WaypointSprite_PlayerDead(this);
-               // throw a weapon
-               SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id);
-
-               // become fully visible
-               this.alpha = default_player_alpha;
-               // make the corpse upright (not tilted)
-               this.angles_x = 0;
-               this.angles_z = 0;
-               // don't spin
-               this.avelocity = '0 0 0';
-               // view from the floor
-               this.view_ofs = '0 0 -8';
-               // toss the corpse
-               this.movetype = MOVETYPE_TOSS;
-               // shootable corpse
-               this.solid = SOLID_CORPSE;
-               this.ballistics_density = autocvar_g_ballistics_density_corpse;
-               // don't stick to the floor
-               UNSET_ONGROUND(this);
-               // dying animation
-               this.deadflag = DEAD_DYING;
-
-               // when to allow respawn
-               calculate_player_respawn_time(this);
-
-               this.death_time = time;
-               if (random() < 0.5)
-                       animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true);
-               else
-                       animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true);
-               if (this.maxs.z > 5)
-               {
-                       this.maxs_z = 5;
-                       setsize(this, this.mins, this.maxs);
-               }
-               // set damage function to corpse damage
-               this.event_damage = PlayerCorpseDamage;
-               // call the corpse damage function just in case it wants to gib
-               this.event_damage(this, inflictor, attacker, excess, deathtype, hitloc, force);
-
-               // set up to fade out later
-               SUB_SetFade (this, time + 6 + random (), 1);
-               // reset body think wrapper broken by SUB_SetFade
-               if(this.classname == "body" && getthink(this) != CopyBody_Think) {
-                       this.CopyBody_think = getthink(this);
-                       this.CopyBody_nextthink = this.nextthink;
-                       setthink(this, CopyBody_Think);
-                       this.nextthink = time;
-               }
-
-               if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") {
-                       // remove corpse
-                       // clones don't run any animation code any more, so we must gib them when they die :(
-                       PlayerCorpseDamage(this, inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force);
-               }
-
-               // reset fields the weapons may use just in case
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       it.wr_resetplayer(it, this);
-                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-                       {
-                               ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0;
-                       }
-               ));
-       }
-}
-
-void MoveToTeam(entity client, int team_colour, int type)
-{
-       int lockteams_backup = lockteams;  // backup any team lock
-       lockteams = 0;  // disable locked teams
-       TeamchangeFrags(client);  // move the players frags
-       SetPlayerColors(client, team_colour - 1);  // set the players colour
-       Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0');  // kill the player
-       lockteams = lockteams_backup;  // restore the team lock
-       LogTeamchange(client.playerid, client.team, type);
-}
-
-/** print(), but only print if the server is not local */
-void dedicated_print(string input)
-{
-       if (server_is_dedicated) print(input);
-}
-
-/**
- * message "": do not say, just test flood control
- * return value:
- *   1 = accept
- *   0 = reject
- *  -1 = fake accept
- */
-int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol)
-{
-       if (!teamsay && !privatesay) if (substring(msgin, 0, 1) == " ")
-        msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!)
-
-       msgin = formatmessage(source, msgin);
-
-    string colorstr;
-       if (!IS_PLAYER(source))
-               colorstr = "^0"; // black for spectators
-       else if(teamplay)
-               colorstr = Team_ColorCode(source.team);
-       else
-       {
-               colorstr = "";
-               teamsay = false;
-       }
-
-       if(intermission_running)
-               teamsay = false;
-
-    if (!source) {
-               colorstr = "";
-               teamsay = false;
-    }
-
-       if(msgin != "")
-               msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin);
-
-       /*
-        * using bprint solves this... me stupid
-       // how can we prevent the message from appearing in a listen server?
-       // for now, just give "say" back and only handle say_team
-       if(!teamsay)
-       {
-               clientcommand(source, strcat("say ", msgin));
-               return;
-       }
-       */
-
-    string namestr = "";
-    if (source)
-        namestr = autocvar_g_chat_teamcolors ? playername(source) : source.netname;
-
-    string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7";
-
-    string msgstr, cmsgstr;
-    string privatemsgprefix = string_null;
-    int privatemsgprefixlen = 0;
-       if (msgin == "") {
-        msgstr = cmsgstr = "";
-       } else {
-               if(privatesay)
-               {
-                       msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7");
-                       privatemsgprefixlen = strlen(msgstr);
-                       msgstr = strcat(msgstr, msgin);
-                       cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin);
-                       if(autocvar_g_chat_teamcolors)
-                               privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7");
-                       else
-                               privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^7");
-               }
-               else if(teamsay)
-               {
-                       if(strstrofs(msgin, "/me", 0) >= 0)
-                       {
-                               //msgin = strreplace("/me", "", msgin);
-                               //msgin = substring(msgin, 3, strlen(msgin));
-                               msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin);
-                               msgstr = strcat("\{1}\{13}^4* ", "^7", msgin);
-                       }
-                       else
-                               msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin);
-                       cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin);
-               }
-               else
-               {
-                       if(strstrofs(msgin, "/me", 0) >= 0)
-                       {
-                               //msgin = strreplace("/me", "", msgin);
-                               //msgin = substring(msgin, 3, strlen(msgin));
-                               msgin = strreplace("/me", strcat(colorprefix, namestr), msgin);
-                               msgstr = strcat("\{1}^4* ", "^7", msgin);
-                       }
-                       else {
-                msgstr = "\{1}";
-                msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7");
-                msgstr = strcat(msgstr, msgin);
-            }
-                       cmsgstr = "";
-               }
-               msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
-       }
-
-       string fullmsgstr = msgstr;
-       string fullcmsgstr = cmsgstr;
-
-       // FLOOD CONTROL
-       int flood = 0;
-       var .float flood_field = floodcontrol_chat;
-       if(floodcontrol)
-       {
-               float flood_spl;
-               float flood_burst;
-               float flood_lmax;
-               float lines;
-               if(privatesay)
-               {
-                       flood_spl = autocvar_g_chat_flood_spl_tell;
-                       flood_burst = autocvar_g_chat_flood_burst_tell;
-                       flood_lmax = autocvar_g_chat_flood_lmax_tell;
-                       flood_field = floodcontrol_chattell;
-               }
-               else if(teamsay)
-               {
-                       flood_spl = autocvar_g_chat_flood_spl_team;
-                       flood_burst = autocvar_g_chat_flood_burst_team;
-                       flood_lmax = autocvar_g_chat_flood_lmax_team;
-                       flood_field = floodcontrol_chatteam;
-               }
-               else
-               {
-                       flood_spl = autocvar_g_chat_flood_spl;
-                       flood_burst = autocvar_g_chat_flood_burst;
-                       flood_lmax = autocvar_g_chat_flood_lmax;
-                       flood_field = floodcontrol_chat;
-               }
-               flood_burst = max(0, flood_burst - 1);
-               // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four!
-
-               // do flood control for the default line size
-               if(msgstr != "")
-               {
-                       getWrappedLine_remaining = msgstr;
-                       msgstr = "";
-                       lines = 0;
-                       while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax))
-                       {
-                               msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width
-                               ++lines;
-                       }
-                       msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
-
-                       if(getWrappedLine_remaining != "")
-                       {
-                               msgstr = strcat(msgstr, "\n");
-                               flood = 2;
-                       }
-
-                       if (time >= source.(flood_field))
-                       {
-                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl;
-                       }
-                       else
-                       {
-                               flood = 1;
-                               msgstr = fullmsgstr;
-                       }
-               }
-               else
-               {
-                       if (time >= source.(flood_field))
-                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl;
-                       else
-                               flood = 1;
-               }
-
-               if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection
-                       source.(flood_field) = flood = 0;
-       }
-
-    string sourcemsgstr, sourcecmsgstr;
-       if(flood == 2) // cannot happen for empty msgstr
-       {
-               if(autocvar_g_chat_flood_notify_flooder)
-               {
-                       sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n");
-                       sourcecmsgstr = "";
-               }
-               else
-               {
-                       sourcemsgstr = fullmsgstr;
-                       sourcecmsgstr = fullcmsgstr;
-               }
-               cmsgstr = "";
-       }
-       else
-       {
-               sourcemsgstr = msgstr;
-               sourcecmsgstr = cmsgstr;
-       }
-
-       if (!privatesay && source && !IS_PLAYER(source))
-       {
-               if (!intermission_running)
-                       if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover)))
-                               teamsay = -1; // spectators
-       }
-
-       if(flood)
-               LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n");
-
-       // build sourcemsgstr by cutting off a prefix and replacing it by the other one
-       if(privatesay)
-               sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1));
-
-    int ret;
-       if(source.muted)
-       {
-               // always fake the message
-               ret = -1;
-       }
-       else if(flood == 1)
-       {
-               if (autocvar_g_chat_flood_notify_flooder)
-               {
-                       sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n"));
-                       ret = 0;
-               }
-               else
-                       ret = -1;
-       }
-       else
-       {
-               ret = 1;
-       }
-
-       if(sourcemsgstr != "" && ret != 0)
-       {
-               if(ret < 0) // faked message, because the player is muted
-               {
-                       sprint(source, sourcemsgstr);
-                       if(sourcecmsgstr != "" && !privatesay)
-                               centerprint(source, sourcecmsgstr);
-               }
-               else if(privatesay) // private message, between 2 people only
-               {
-                       sprint(source, sourcemsgstr);
-                       sprint(privatesay, msgstr);
-                       if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
-                       if(cmsgstr != "")
-                               centerprint(privatesay, cmsgstr);
-               }
-               else if ( teamsay && source.active_minigame )
-               {
-                       sprint(source, sourcemsgstr);
-                       dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr));
-               }
-               else if(teamsay > 0) // team message, only sent to team mates
-               {
-                       sprint(source, sourcemsgstr);
-                       dedicated_print(msgstr); // send to server console too
-                       if(sourcecmsgstr != "")
-                               centerprint(source, sourcecmsgstr);
-                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, {
-                               sprint(it, msgstr);
-                               if(cmsgstr != "")
-                                       centerprint(it, cmsgstr);
-                       });
-               }
-               else if(teamsay < 0) // spectator message, only sent to spectators
-               {
-                       sprint(source, sourcemsgstr);
-                       dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
-               }
-               else
-               {
-            if (source) {
-                sprint(source, sourcemsgstr);
-                dedicated_print(msgstr); // send to server console too
-                MX_Say(strcat(playername(source), "^7: ", msgin));
-            }
-            FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
-        }
-       }
-
-       return ret;
-}
diff --git a/qcsrc/server/cl_player.qh b/qcsrc/server/cl_player.qh
deleted file mode 100644 (file)
index b5f8ca0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-.entity pusher;
-.float pushltime;
-.float istypefrag;
-
-.float CopyBody_nextthink;
-.void(entity this) CopyBody_think;
-void CopyBody_Think(entity this);
-void CopyBody(entity this, float keepvelocity);
-
-void player_setupanimsformodel(entity this);
-
-void player_anim(entity this);
-
-void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-
-// g_<gametype>_str:
-// If 0, default is used.
-// If <0, 0 is used.
-// Otherwise, g_str (default value) is used.
-// For consistency, negative values there are mapped to zero too.
-#define GAMETYPE_DEFAULTED_SETTING(str) \
-       ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \
-       (gametype_setting_tmp < 0) ? 0 \
-       : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \
-       : gametype_setting_tmp)
-
-void calculate_player_respawn_time(entity this);
-
-void ClientKill_Now_TeamChange(entity this);
-
-void MoveToTeam(entity client, float team_colour, float type);
-
-void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-
-/** to be used by `prvm_edictset server playernumber muted 1` */
-.float muted;
-int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol);
diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc
new file mode 100644 (file)
index 0000000..d4a3e9f
--- /dev/null
@@ -0,0 +1,2648 @@
+#include "client.qh"
+
+#include "anticheat.qh"
+#include "impulse.qh"
+#include "player.qh"
+#include "ipban.qh"
+#include "miscfunctions.qh"
+#include "portals.qh"
+#include "teamplay.qh"
+#include "playerdemo.qh"
+#include "spawnpoints.qh"
+#include "g_damage.qh"
+#include "g_hook.qh"
+#include "command/common.qh"
+#include "cheats.qh"
+#include "g_world.qh"
+#include "race.qh"
+#include "antilag.qh"
+#include "campaign.qh"
+#include "command/common.qh"
+#include "scores_rules.qh"
+
+#include "bot/api.qh"
+
+#include "../common/ent_cs.qh"
+#include <common/state.qh>
+
+#include <common/effects/qc/globalsound.qh>
+
+#include "../common/triggers/func/conveyor.qh"
+#include "../common/triggers/teleporters.qh"
+
+#include "../common/vehicles/all.qh"
+
+#include "weapons/hitplot.qh"
+#include "weapons/weaponsystem.qh"
+
+#include "../common/net_notice.qh"
+#include "../common/net_linked.qh"
+#include "../common/physics/player.qh"
+
+#include "../common/items/_mod.qh"
+
+#include "../common/mutators/mutator/waypoints/all.qh"
+
+#include "../common/triggers/subs.qh"
+#include "../common/triggers/triggers.qh"
+#include "../common/triggers/trigger/secret.qh"
+
+#include "../common/minigames/sv_minigames.qh"
+
+#include "../common/items/inventory.qh"
+
+#include "../common/monsters/sv_monsters.qh"
+
+#include "../lib/warpzone/server.qh"
+
+STATIC_METHOD(Client, Add, void(Client this, int _team))
+{
+    ClientConnect(this);
+    TRANSMUTE(Player, this);
+    this.frame = 12; // 7
+    this.team = _team;
+    PutClientInServer(this);
+}
+
+void PutObserverInServer(entity this);
+
+STATIC_METHOD(Client, Remove, void(Client this))
+{
+    TRANSMUTE(Observer, this);
+    PutClientInServer(this);
+    ClientDisconnect(this);
+}
+
+void send_CSQC_teamnagger() {
+       WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
+}
+
+int CountSpectators(entity player, entity to)
+{
+       if(!player) { return 0; } // not sure how, but best to be safe
+
+       int spec_count = 0;
+
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
+       {
+               spec_count++;
+       });
+
+       return spec_count;
+}
+
+void WriteSpectators(entity player, entity to)
+{
+       if(!player) { return; } // not sure how, but best to be safe
+
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
+       {
+               WriteByte(MSG_ENTITY, num_for_edict(it));
+       });
+}
+
+bool ClientData_Send(entity this, entity to, int sf)
+{
+       assert(to == this.owner, return false);
+
+       entity e = to;
+       if (IS_SPEC(e)) e = e.enemy;
+
+       sf = 0;
+       if (e.race_completed)       sf |= 1; // forced scoreboard
+       if (to.spectatee_status)    sf |= 2; // spectator ent number follows
+       if (e.zoomstate)            sf |= 4; // zoomed
+       if (e.porto_v_angle_held)   sf |= 8; // angles held
+       if (autocvar_sv_showspectators) sf |= 16; // show spectators
+
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
+       WriteByte(MSG_ENTITY, sf);
+
+       if (sf & 2)
+       {
+               WriteByte(MSG_ENTITY, to.spectatee_status);
+       }
+       if (sf & 8)
+       {
+               WriteAngle(MSG_ENTITY, e.v_angle.x);
+               WriteAngle(MSG_ENTITY, e.v_angle.y);
+       }
+
+       if(sf & 16)
+       {
+               float specs = CountSpectators(e, to);
+               WriteByte(MSG_ENTITY, specs);
+               WriteSpectators(e, to);
+       }
+
+       return true;
+}
+
+void ClientData_Attach(entity this)
+{
+       Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send);
+       this.clientdata.drawonlytoclient = this;
+       this.clientdata.owner = this;
+}
+
+void ClientData_Detach(entity this)
+{
+       delete(this.clientdata);
+       this.clientdata = NULL;
+}
+
+void ClientData_Touch(entity e)
+{
+       e.clientdata.SendFlags = 1;
+
+       // make it spectatable
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1));
+}
+
+.string netname_previous;
+
+void SetSpectatee(entity this, entity spectatee);
+void SetSpectatee_status(entity this, int spectatee_num);
+
+
+/*
+=============
+CheckPlayerModel
+
+Checks if the argument string can be a valid playermodel.
+Returns a valid one in doubt.
+=============
+*/
+string FallbackPlayerModel;
+string CheckPlayerModel(string plyermodel) {
+       if(FallbackPlayerModel != cvar_defstring("_cl_playermodel"))
+       {
+               // note: we cannot summon Don Strunzone here, some player may
+               // still have the model string set. In case anyone manages how
+               // to change a cvar default, we'll have a small leak here.
+               FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
+       }
+       // only in right path
+       if( substring(plyermodel,0,14) != "models/player/")
+               return FallbackPlayerModel;
+       // only good file extensions
+       if(substring(plyermodel,-4,4) != ".zym")
+       if(substring(plyermodel,-4,4) != ".dpm")
+       if(substring(plyermodel,-4,4) != ".iqm")
+       if(substring(plyermodel,-4,4) != ".md3")
+       if(substring(plyermodel,-4,4) != ".psk")
+               return FallbackPlayerModel;
+       // forbid the LOD models
+       if(substring(plyermodel, -9,5) == "_lod1")
+               return FallbackPlayerModel;
+       if(substring(plyermodel, -9,5) == "_lod2")
+               return FallbackPlayerModel;
+       if(plyermodel != strtolower(plyermodel))
+               return FallbackPlayerModel;
+       // also, restrict to server models
+       if(autocvar_sv_servermodelsonly)
+       {
+               if(!fexists(plyermodel))
+                       return FallbackPlayerModel;
+       }
+       return plyermodel;
+}
+
+void setplayermodel(entity e, string modelname)
+{
+       precache_model(modelname);
+       _setmodel(e, modelname);
+       player_setupanimsformodel(e);
+       if(!autocvar_g_debug_globalsounds)
+               UpdatePlayerSounds(e);
+}
+
+void FixPlayermodel(entity player);
+/** putting a client as observer in the server */
+void PutObserverInServer(entity this)
+{
+    bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
+       PlayerState_detach(this);
+
+       if (IS_PLAYER(this) && this.health >= 1) {
+        // despawn effect
+               Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+    }
+
+    {
+        entity spot = SelectSpawnPoint(this, true);
+        if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
+        this.angles = spot.angles;
+        this.angles_z = 0;
+        this.fixangle = true;
+        // offset it so that the spectator spawns higher off the ground, looks better this way
+        setorigin(this, spot.origin + STAT(PL_VIEW_OFS, this));
+        this.prevorigin = this.origin;
+        if (IS_REAL_CLIENT(this))
+        {
+            msg_entity = this;
+            WriteByte(MSG_ONE, SVC_SETVIEW);
+            WriteEntity(MSG_ONE, this);
+        }
+        // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
+        // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
+        if(!autocvar_g_debug_globalsounds)
+        {
+               // needed for player sounds
+               this.model = "";
+               FixPlayermodel(this);
+        }
+        setmodel(this, MDL_Null);
+        setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
+        this.view_ofs = '0 0 0';
+    }
+
+    RemoveGrapplingHook(this);
+       Portal_ClearAll(this);
+       Unfreeze(this);
+       SetSpectatee(this, NULL);
+
+       if (this.alivetime)
+       {
+               if (!warmup_stage)
+                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+               this.alivetime = 0;
+       }
+
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+
+       WaypointSprite_PlayerDead(this);
+
+       if (mutator_returnvalue) {
+           // mutator prevents resetting teams+score
+       } else {
+               this.team = -1;  // move this as it is needed to log the player spectating in eventlog
+        this.frags = FRAGS_SPECTATOR;
+        PlayerScore_Clear(this);  // clear scores when needed
+    }
+
+       if (this.killcount != FRAGS_SPECTATOR)
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
+               if(!gameover)
+               if(autocvar_g_chat_nospectators == 1 || (!warmup_stage && autocvar_g_chat_nospectators == 2))
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
+
+               if(this.just_joined == false) {
+                       LogTeamchange(this.playerid, -1, 4);
+               } else
+                       this.just_joined = false;
+       }
+
+       accuracy_resend(this);
+
+       this.spectatortime = time;
+       if(this.bot_attack)
+               IL_REMOVE(g_bot_targets, this);
+       this.bot_attack = false;
+    this.hud = HUD_NORMAL;
+       TRANSMUTE(Observer, this);
+       this.iscreature = false;
+       this.teleportable = TELEPORT_SIMPLE;
+       if(this.damagedbycontents)
+               IL_REMOVE(g_damagedbycontents, this);
+       this.damagedbycontents = false;
+       this.health = FRAGS_SPECTATOR;
+       SetSpectatee_status(this, etof(this));
+       this.takedamage = DAMAGE_NO;
+       this.solid = SOLID_NOT;
+       set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink
+       this.flags = FL_CLIENT | FL_NOTARGET;
+       this.armorvalue = 666;
+       this.effects = 0;
+       this.armorvalue = autocvar_g_balance_armor_start;
+       this.pauserotarmor_finished = 0;
+       this.pauserothealth_finished = 0;
+       this.pauseregen_finished = 0;
+       this.damageforcescale = 0;
+       this.death_time = 0;
+       this.respawn_flags = 0;
+       this.respawn_time = 0;
+       this.stat_respawn_time = 0;
+       this.alpha = 0;
+       this.scale = 0;
+       this.fade_time = 0;
+       this.pain_frame = 0;
+       this.pain_finished = 0;
+       this.strength_finished = 0;
+       this.invincible_finished = 0;
+       this.superweapons_finished = 0;
+       this.pushltime = 0;
+       this.istypefrag = 0;
+       setthink(this, func_null);
+       this.nextthink = 0;
+       this.hook_time = 0;
+       this.deadflag = DEAD_NO;
+       this.crouch = false;
+       this.revive_progress = 0;
+       this.revival_time = 0;
+
+       this.items = 0;
+       this.weapons = '0 0 0';
+       this.drawonlytoclient = this;
+
+       this.weaponname = "";
+       this.weaponmodel = "";
+       for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               this.weaponentities[slot] = NULL;
+       }
+       this.exteriorweaponentity = NULL;
+       this.killcount = FRAGS_SPECTATOR;
+       this.velocity = '0 0 0';
+       this.avelocity = '0 0 0';
+       this.punchangle = '0 0 0';
+       this.punchvector = '0 0 0';
+       this.oldvelocity = this.velocity;
+       this.fire_endtime = -1;
+       this.event_damage = func_null;
+
+       STAT(ACTIVEWEAPON, this) = WEP_Null.m_id;
+       STAT(SWITCHINGWEAPON, this) = WEP_Null.m_id;
+       STAT(SWITCHWEAPON, this) = WEP_Null.m_id;
+}
+
+int player_getspecies(entity this)
+{
+       get_model_parameters(this.model, this.skin);
+       int s = get_model_parameters_species;
+       get_model_parameters(string_null, 0);
+       if (s < 0) return SPECIES_HUMAN;
+       return s;
+}
+
+.float model_randomizer;
+void FixPlayermodel(entity player)
+{
+       string defaultmodel = "";
+       int defaultskin = 0;
+       if(autocvar_sv_defaultcharacter)
+       {
+               if(teamplay)
+               {
+                       string s = Static_Team_ColorName_Lower(player.team);
+                       if (s != "neutral")
+                       {
+                               defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s));
+                               defaultskin = cvar(strcat("sv_defaultplayerskin_", s));
+                       }
+               }
+
+               if(defaultmodel == "")
+               {
+                       defaultmodel = autocvar_sv_defaultplayermodel;
+                       defaultskin = autocvar_sv_defaultplayerskin;
+               }
+
+               int n = tokenize_console(defaultmodel);
+               if(n > 0)
+               {
+                       defaultmodel = argv(floor(n * player.model_randomizer));
+                       // However, do NOT randomize if the player-selected model is in the list.
+                       for (int i = 0; i < n; ++i)
+                               if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin))
+                                       defaultmodel = argv(i);
+               }
+
+               int i = strstrofs(defaultmodel, ":", 0);
+               if(i >= 0)
+               {
+                       defaultskin = stof(substring(defaultmodel, i+1, -1));
+                       defaultmodel = substring(defaultmodel, 0, i);
+               }
+       }
+       if(autocvar_sv_defaultcharacterskin && !defaultskin)
+       {
+               if(teamplay)
+               {
+                       string s = Static_Team_ColorName_Lower(player.team);
+                       if (s != "neutral")
+                               defaultskin = cvar(strcat("sv_defaultplayerskin_", s));
+               }
+
+               if(!defaultskin)
+                       defaultskin = autocvar_sv_defaultplayerskin;
+       }
+
+       MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player);
+       defaultmodel = M_ARGV(0, string);
+       defaultskin = M_ARGV(1, int);
+
+       bool chmdl = false;
+       int oldskin;
+       if(defaultmodel != "")
+       {
+               if (defaultmodel != player.model)
+               {
+                       vector m1 = player.mins;
+                       vector m2 = player.maxs;
+                       setplayermodel (player, defaultmodel);
+                       setsize (player, m1, m2);
+                       chmdl = true;
+               }
+
+               oldskin = player.skin;
+               player.skin = defaultskin;
+       } else {
+               if (player.playermodel != player.model || player.playermodel == "")
+               {
+                       player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop
+                       vector m1 = player.mins;
+                       vector m2 = player.maxs;
+                       setplayermodel (player, player.playermodel);
+                       setsize (player, m1, m2);
+                       chmdl = true;
+               }
+
+               if(!autocvar_sv_defaultcharacterskin)
+               {
+                       oldskin = player.skin;
+                       player.skin = stof(player.playerskin);
+               }
+               else
+               {
+                       oldskin = player.skin;
+                       player.skin = defaultskin;
+               }
+       }
+
+       if(chmdl || oldskin != player.skin) // model or skin has changed
+       {
+               player.species = player_getspecies(player); // update species
+               if(!autocvar_g_debug_globalsounds)
+                       UpdatePlayerSounds(player); // update skin sounds
+       }
+
+       if(!teamplay)
+               if(strlen(autocvar_sv_defaultplayercolors))
+                       if(player.clientcolors != stof(autocvar_sv_defaultplayercolors))
+                               setcolor(player, stof(autocvar_sv_defaultplayercolors));
+}
+
+
+/** Called when a client spawns in the server */
+void PutClientInServer(entity this)
+{
+       if (IS_BOT_CLIENT(this)) {
+               TRANSMUTE(Player, this);
+       } else if (IS_REAL_CLIENT(this)) {
+               msg_entity = this;
+               WriteByte(MSG_ONE, SVC_SETVIEW);
+               WriteEntity(MSG_ONE, this);
+       }
+       if (gameover) {
+               TRANSMUTE(Observer, this);
+       }
+
+       SetSpectatee(this, NULL);
+
+       // reset player keys
+       this.itemkeys = 0;
+
+       MUTATOR_CALLHOOK(PutClientInServer, this);
+
+       if (IS_OBSERVER(this)) {
+               PutObserverInServer(this);
+       } else if (IS_PLAYER(this)) {
+               if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+
+               PlayerState_attach(this);
+               accuracy_resend(this);
+
+               if (this.team < 0)
+                       JoinBestTeam(this, false, true);
+
+               entity spot = SelectSpawnPoint(this, false);
+               if (!spot) {
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
+                       return; // spawn failed
+               }
+
+               TRANSMUTE(Player, this);
+
+               this.wasplayer = true;
+               this.iscreature = true;
+               this.teleportable = TELEPORT_NORMAL;
+               if(!this.damagedbycontents)
+                       IL_PUSH(g_damagedbycontents, this);
+               this.damagedbycontents = true;
+               set_movetype(this, MOVETYPE_WALK);
+               this.solid = SOLID_SLIDEBOX;
+               this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
+               if (autocvar_g_playerclip_collisions)
+                       this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
+               if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions)
+                       this.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
+               this.frags = FRAGS_PLAYER;
+               if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this);
+               this.flags = FL_CLIENT | FL_PICKUPITEMS;
+               if (autocvar__notarget)
+                       this.flags |= FL_NOTARGET;
+               this.takedamage = DAMAGE_AIM;
+               this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
+               this.dmg = 2; // WTF
+
+               if (warmup_stage) {
+                       this.ammo_shells = warmup_start_ammo_shells;
+                       this.ammo_nails = warmup_start_ammo_nails;
+                       this.ammo_rockets = warmup_start_ammo_rockets;
+                       this.ammo_cells = warmup_start_ammo_cells;
+                       this.ammo_plasma = warmup_start_ammo_plasma;
+                       this.ammo_fuel = warmup_start_ammo_fuel;
+                       this.health = warmup_start_health;
+                       this.armorvalue = warmup_start_armorvalue;
+                       this.weapons = WARMUP_START_WEAPONS;
+               } else {
+                       this.ammo_shells = start_ammo_shells;
+                       this.ammo_nails = start_ammo_nails;
+                       this.ammo_rockets = start_ammo_rockets;
+                       this.ammo_cells = start_ammo_cells;
+                       this.ammo_plasma = start_ammo_plasma;
+                       this.ammo_fuel = start_ammo_fuel;
+                       this.health = start_health;
+                       this.armorvalue = start_armorvalue;
+                       this.weapons = start_weapons;
+               }
+               SetSpectatee_status(this, 0);
+
+               this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
+
+               this.items = start_items;
+
+               this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
+               this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
+               this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
+               this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
+               this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
+               // extend the pause of rotting if client was reset at the beginning of the countdown
+               if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted?
+                       float f = game_starttime - time;
+                       this.spawnshieldtime += f;
+                       this.pauserotarmor_finished += f;
+                       this.pauserothealth_finished += f;
+                       this.pauseregen_finished += f;
+               }
+               this.damageforcescale = 2;
+               this.death_time = 0;
+               this.respawn_flags = 0;
+               this.respawn_time = 0;
+               this.stat_respawn_time = 0;
+               this.scale = autocvar_sv_player_scale;
+               this.fade_time = 0;
+               this.pain_frame = 0;
+               this.pain_finished = 0;
+               this.pushltime = 0;
+               setthink(this, func_null); // players have no think function
+               this.nextthink = 0;
+               this.dmg_team = 0;
+               this.ballistics_density = autocvar_g_ballistics_density_player;
+
+               this.deadflag = DEAD_NO;
+
+               this.angles = spot.angles;
+               this.angles_z = 0; // never spawn tilted even if the spot says to
+               if (IS_BOT_CLIENT(this))
+                       this.v_angle = this.angles;
+               this.fixangle = true; // turn this way immediately
+               this.oldvelocity = this.velocity = '0 0 0';
+               this.avelocity = '0 0 0';
+               this.punchangle = '0 0 0';
+               this.punchvector = '0 0 0';
+
+               this.strength_finished = 0;
+               this.invincible_finished = 0;
+               this.fire_endtime = -1;
+               this.revive_progress = 0;
+               this.revival_time = 0;
+               this.air_finished = time + 12;
+
+               entity spawnevent = new_pure(spawnevent);
+               spawnevent.owner = this;
+               Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
+
+               // Cut off any still running player sounds.
+               stopsound(this, CH_PLAYER_SINGLE);
+
+               this.model = "";
+               FixPlayermodel(this);
+               this.drawonlytoclient = NULL;
+
+               this.viewloc = NULL;
+
+               this.crouch = false;
+               this.view_ofs = STAT(PL_VIEW_OFS, this);
+               setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
+               this.spawnorigin = spot.origin;
+               setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
+               // don't reset back to last position, even if new position is stuck in solid
+               this.oldorigin = this.origin;
+               this.prevorigin = this.origin;
+               this.lastteleporttime = time; // prevent insane speeds due to changing origin
+               if(this.conveyor)
+                       IL_REMOVE(g_conveyed, this);
+               this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
+               this.hud = HUD_NORMAL;
+
+               this.event_damage = PlayerDamage;
+
+               if(!this.bot_attack)
+                       IL_PUSH(g_bot_targets, this);
+               this.bot_attack = true;
+               this.monster_attack = true;
+
+               PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
+
+               if (this.killcount == FRAGS_SPECTATOR) {
+                       PlayerScore_Clear(this);
+                       this.killcount = 0;
+               }
+
+               for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       CL_SpawnWeaponentity(this, weaponentities[slot]);
+               }
+               this.alpha = default_player_alpha;
+               this.colormod = '1 1 1' * autocvar_g_player_brightness;
+               this.exteriorweaponentity.alpha = default_weapon_alpha;
+
+               this.speedrunning = false;
+
+               target_voicescript_clear(this);
+
+               // reset fields the weapons may use
+               FOREACH(Weapons, true, LAMBDA(
+                       it.wr_resetplayer(it, this);
+                       // reload all reloadable weapons
+                       if (it.spawnflags & WEP_FLAG_RELOADABLE) {
+                               this.weapon_load[it.m_id] = it.reloading_ammo;
+                       }
+               ));
+
+               {
+                       string s = spot.target;
+                       spot.target = string_null;
+                       SUB_UseTargets(spot, this, NULL);
+                       spot.target = s;
+               }
+
+               Unfreeze(this);
+
+               MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
+
+               if (autocvar_spawn_debug)
+               {
+                       sprint(this, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
+                       delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
+               }
+
+               PS(this).m_switchweapon = w_getbestweapon(this);
+               this.cnt = -1; // W_LastWeapon will not complain
+               PS(this).m_weapon = WEP_Null;
+               this.weaponname = "";
+               PS(this).m_switchingweapon = WEP_Null;
+
+               if (!warmup_stage && !this.alivetime)
+                       this.alivetime = time;
+
+               antilag_clear(this, CS(this));
+       }
+}
+
+void ClientInit_misc(entity this);
+
+// TODO do we need all these fields, or should we stop autodetecting runtime
+// changes and just have a console command to update this?
+bool ClientInit_SendEntity(entity this, entity to, int sf)
+{
+       WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
+       return = true;
+       msg_entity = to;
+       // MSG_INIT replacement
+       // TODO: make easier to use
+       Registry_send_all();
+       W_PROP_reload(MSG_ONE, to);
+       ClientInit_misc(this);
+       MUTATOR_CALLHOOK(Ent_Init);
+}
+void ClientInit_misc(entity this)
+{
+       int channel = MSG_ONE;
+       WriteHeader(channel, ENT_CLIENT_INIT);
+       WriteByte(channel, g_nexball_meter_period * 32);
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
+
+       if(sv_foginterval && world.fog != "")
+               WriteString(channel, world.fog);
+       else
+               WriteString(channel, "");
+       WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
+       WriteByte(channel, serverflags);
+       WriteCoord(channel, autocvar_g_trueaim_minrange);
+}
+
+void ClientInit_CheckUpdate(entity this)
+{
+       this.nextthink = time;
+       if(this.count != autocvar_g_balance_armor_blockpercent)
+       {
+               this.count = autocvar_g_balance_armor_blockpercent;
+               this.SendFlags |= 1;
+       }
+}
+
+void ClientInit_Spawn()
+{
+       entity e = new_pure(clientinit);
+       setthink(e, ClientInit_CheckUpdate);
+       Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
+
+       ClientInit_CheckUpdate(e);
+}
+
+/*
+=============
+SetNewParms
+=============
+*/
+void SetNewParms ()
+{
+       // initialize parms for a new player
+       parm1 = -(86400 * 366);
+
+       MUTATOR_CALLHOOK(SetNewParms);
+}
+
+/*
+=============
+SetChangeParms
+=============
+*/
+void SetChangeParms (entity this)
+{
+       // save parms for level change
+       parm1 = this.parm_idlesince - time;
+
+       MUTATOR_CALLHOOK(SetChangeParms);
+}
+
+/*
+=============
+DecodeLevelParms
+=============
+*/
+void DecodeLevelParms(entity this)
+{
+       // load parms
+       this.parm_idlesince = parm1;
+       if (this.parm_idlesince == -(86400 * 366))
+               this.parm_idlesince = time;
+
+       // whatever happens, allow 60 seconds of idling directly after connect for map loading
+       this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60);
+
+       MUTATOR_CALLHOOK(DecodeLevelParms);
+}
+
+/*
+=============
+ClientKill
+
+Called when a client types 'kill' in the console
+=============
+*/
+
+.float clientkill_nexttime;
+void ClientKill_Now_TeamChange(entity this)
+{
+       if(this.killindicator_teamchange == -1)
+       {
+               JoinBestTeam( this, false, true );
+       }
+       else if(this.killindicator_teamchange == -2)
+       {
+               if(blockSpectators)
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
+               PutObserverInServer(this);
+       }
+       else
+               SV_ChangeTeam(this, this.killindicator_teamchange - 1);
+       this.killindicator_teamchange = 0;
+}
+
+void ClientKill_Now(entity this)
+{
+       if(this.vehicle)
+       {
+           vehicles_exit(this.vehicle, VHEF_RELEASE);
+           if(!this.killindicator_teamchange)
+           {
+            this.vehicle_health = -1;
+            Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0');
+           }
+       }
+
+       if(this.killindicator && !wasfreed(this.killindicator))
+               delete(this.killindicator);
+
+       this.killindicator = NULL;
+
+       if(this.killindicator_teamchange)
+               ClientKill_Now_TeamChange(this);
+
+       if(!IS_SPEC(this) && !IS_OBSERVER(this))
+               Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0');
+
+       // now I am sure the player IS dead
+}
+void KillIndicator_Think(entity this)
+{
+       if (gameover)
+       {
+               this.owner.killindicator = NULL;
+               delete(this);
+               return;
+       }
+
+       if (this.owner.alpha < 0 && !this.owner.vehicle)
+       {
+               this.owner.killindicator = NULL;
+               delete(this);
+               return;
+       }
+
+       if(this.cnt <= 0)
+       {
+               ClientKill_Now(this.owner);
+               return;
+       }
+    else if(g_cts && this.health == 1) // health == 1 means that it's silent
+    {
+        this.nextthink = time + 1;
+        this.cnt -= 1;
+    }
+       else
+       {
+               if(this.cnt <= 10)
+                       setmodel(this, MDL_NUM(this.cnt));
+               if(IS_REAL_CLIENT(this.owner))
+               {
+                       if(this.cnt <= 10)
+                               { Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); }
+               }
+               this.nextthink = time + 1;
+               this.cnt -= 1;
+       }
+}
+
+float clientkilltime;
+void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec
+{
+       float killtime;
+       float starttime;
+
+       if (gameover)
+               return;
+
+       killtime = autocvar_g_balance_kill_delay;
+
+       if(g_race_qualifying || g_cts)
+               killtime = 0;
+
+    if(MUTATOR_CALLHOOK(ClientKill, this, killtime))
+       return;
+
+       this.killindicator_teamchange = targetteam;
+
+    if(!this.killindicator)
+       {
+               if(!IS_DEAD(this))
+               {
+                       killtime = max(killtime, this.clientkill_nexttime - time);
+                       this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam;
+               }
+
+               if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this))
+               {
+                       ClientKill_Now(this);
+               }
+               else
+               {
+                       starttime = max(time, clientkilltime);
+
+                       this.killindicator = spawn();
+                       this.killindicator.owner = this;
+                       this.killindicator.scale = 0.5;
+                       setattachment(this.killindicator, this, "");
+                       setorigin(this.killindicator, '0 0 52');
+                       setthink(this.killindicator, KillIndicator_Think);
+                       this.killindicator.nextthink = starttime + (this.lip) * 0.05;
+                       clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05);
+                       this.killindicator.cnt = ceil(killtime);
+                       this.killindicator.count = bound(0, ceil(killtime), 10);
+                       //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n"));
+
+                       IL_EACH(g_clones, it.enemy == this && !(it.effects & CSQCMODEL_EF_RESPAWNGHOST),
+                       {
+                               it.killindicator = spawn();
+                               it.killindicator.owner = it;
+                               it.killindicator.scale = 0.5;
+                               setattachment(it.killindicator, it, "");
+                               setorigin(it.killindicator, '0 0 52');
+                               setthink(it.killindicator, KillIndicator_Think);
+                               it.killindicator.nextthink = starttime + (it.lip) * 0.05;
+                               //clientkilltime = max(clientkilltime, it.killindicator.nextthink + 0.05);
+                               it.killindicator.cnt = ceil(killtime);
+                       });
+                       this.lip = 0;
+               }
+       }
+       if(this.killindicator)
+       {
+               if(targetteam == 0) // just die
+               {
+                       this.killindicator.colormod = '0 0 0';
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, this.killindicator.cnt);
+               }
+               else if(targetteam == -1) // auto
+               {
+                       this.killindicator.colormod = '0 1 0';
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, this.killindicator.cnt);
+               }
+               else if(targetteam == -2) // spectate
+               {
+                       this.killindicator.colormod = '0.5 0.5 0.5';
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, this.killindicator.cnt);
+               }
+               else
+               {
+                       this.killindicator.colormod = Team_ColorRGB(targetteam);
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE), this.killindicator.cnt);
+               }
+       }
+
+}
+
+void ClientKill (entity this)
+{
+       if(gameover) return;
+       if(this.player_blocked) return;
+       if(STAT(FROZEN, this)) return;
+
+       ClientKill_TeamChange(this, 0);
+}
+
+void FixClientCvars(entity e)
+{
+       // send prediction settings to the client
+       stuffcmd(e, "\nin_bindmap 0 0\n");
+       if(autocvar_g_antilag == 3) // client side hitscan
+               stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
+       if(autocvar_sv_gentle)
+               stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
+
+       MUTATOR_CALLHOOK(FixClientCvars, e);
+}
+
+float PlayerInIDList(entity p, string idlist)
+{
+       float n, i;
+       string s;
+
+       // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this
+       if (!p.crypto_idfp)
+               return 0;
+
+       // this function allows abbreviated player IDs too!
+       n = tokenize_console(idlist);
+       for(i = 0; i < n; ++i)
+       {
+               s = argv(i);
+               if(s == substring(p.crypto_idfp, 0, strlen(s)))
+                       return 1;
+       }
+
+       return 0;
+}
+
+#ifdef DP_EXT_PRECONNECT
+/*
+=============
+ClientPreConnect
+
+Called once (not at each match start) when a client begins a connection to the server
+=============
+*/
+void ClientPreConnect(entity this)
+{
+       if(autocvar_sv_eventlog)
+       {
+               GameLogEcho(sprintf(":connect:%d:%d:%s",
+                       this.playerid,
+                       etof(this),
+                       ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot")
+               ));
+       }
+}
+#endif
+
+/**
+=============
+ClientConnect
+
+Called when a client connects to the server
+=============
+*/
+void ClientConnect(entity this)
+{
+       if (Ban_MaybeEnforceBanOnce(this)) return;
+       assert(!IS_CLIENT(this), return);
+       this.flags |= FL_CLIENT;
+       assert(player_count >= 0, player_count = 0);
+
+#ifdef WATERMARK
+       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK);
+#endif
+       this.version_nagtime = time + 10 + random() * 10;
+       TRANSMUTE(Client, this);
+
+       // identify the right forced team
+       if (autocvar_g_campaign)
+       {
+               if (IS_REAL_CLIENT(this)) // only players, not bots
+               {
+                       switch (autocvar_g_campaign_forceteam)
+                       {
+                               case 1: this.team_forced = NUM_TEAM_1; break;
+                               case 2: this.team_forced = NUM_TEAM_2; break;
+                               case 3: this.team_forced = NUM_TEAM_3; break;
+                               case 4: this.team_forced = NUM_TEAM_4; break;
+                               default: this.team_forced = 0;
+                       }
+               }
+       }
+       else if (PlayerInIDList(this, autocvar_g_forced_team_red))    this.team_forced = NUM_TEAM_1;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_blue))   this.team_forced = NUM_TEAM_2;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_pink))   this.team_forced = NUM_TEAM_4;
+       else switch (autocvar_g_forced_team_otherwise)
+       {
+               default: this.team_forced = 0; break;
+               case "red": this.team_forced = NUM_TEAM_1; break;
+               case "blue": this.team_forced = NUM_TEAM_2; break;
+               case "yellow": this.team_forced = NUM_TEAM_3; break;
+               case "pink": this.team_forced = NUM_TEAM_4; break;
+               case "spectate":
+               case "spectator":
+                       this.team_forced = -1;
+                       break;
+       }
+       if (!teamplay && this.team_forced > 0) this.team_forced = 0;
+
+    {
+        int id = this.playerid;
+        this.playerid = 0; // silent
+           JoinBestTeam(this, false, false); // if the team number is valid, keep it
+           this.playerid = id;
+    }
+
+       if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) {
+               TRANSMUTE(Observer, this);
+       } else {
+               if (!teamplay || autocvar_g_balance_teams) {
+                       TRANSMUTE(Player, this);
+                       campaign_bots_may_start = true;
+               } else {
+                       TRANSMUTE(Observer, this); // do it anyway
+               }
+       }
+
+       PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid));
+
+       // always track bots, don't ask for cl_allow_uidtracking
+    if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this);
+
+       if (autocvar_sv_eventlog)
+               GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname));
+
+       LogTeamchange(this.playerid, this.team, 1);
+
+       this.just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
+
+       this.netname_previous = strzone(this.netname);
+
+       if(teamplay && IS_PLAYER(this))
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname);
+       else
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname);
+
+       stuffcmd(this, clientstuff, "\n");
+       stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
+
+       FixClientCvars(this);
+
+       // get version info from player
+       stuffcmd(this, "cmd clientversion $gameversion\n");
+
+       // notify about available teams
+       if (teamplay)
+       {
+               CheckAllowedTeams(this);
+               int t = 0;
+               if (c1 >= 0) t |= BIT(0);
+               if (c2 >= 0) t |= BIT(1);
+               if (c3 >= 0) t |= BIT(2);
+               if (c4 >= 0) t |= BIT(3);
+               stuffcmd(this, sprintf("set _teams_available %d\n", t));
+       }
+       else
+       {
+               stuffcmd(this, "set _teams_available 0\n");
+       }
+
+       bot_relinkplayerlist();
+
+       this.spectatortime = time;
+       if (blockSpectators)
+       {
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
+       }
+
+       this.jointime = time;
+       this.allowed_timeouts = autocvar_sv_timeout_number;
+
+       if (IS_REAL_CLIENT(this))
+       {
+               if (g_weaponarena_weapons == WEPSET(TUBA))
+                       stuffcmd(this, "cl_cmd settemp chase_active 1\n");
+       }
+
+       if (!sv_foginterval && world.fog != "")
+               stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
+
+       if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2))
+               if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
+                       send_CSQC_teamnagger();
+
+       CSQCMODEL_AUTOINIT(this);
+
+       this.model_randomizer = random();
+
+       if (IS_REAL_CLIENT(this))
+               sv_notice_join(this);
+
+       // update physics stats (players can spawn before physics runs)
+       Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
+
+       IL_EACH(g_initforplayer, it.init_for_player, {
+               it.init_for_player(it, this);
+       });
+
+       MUTATOR_CALLHOOK(ClientConnect, this);
+
+       if (IS_REAL_CLIENT(this))
+       {
+               if (!autocvar_g_campaign && !IS_PLAYER(this))
+               {
+                       this.motd_actived_time = -1;
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
+               }
+       }
+}
+/*
+=============
+ClientDisconnect
+
+Called when a client disconnects from the server
+=============
+*/
+.entity chatbubbleentity;
+void ReadyCount();
+void ClientDisconnect(entity this)
+{
+       assert(IS_CLIENT(this), return);
+
+       PlayerStats_GameReport_FinalizePlayer(this);
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+       if (this.active_minigame) part_minigame(this);
+       if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+
+       if (autocvar_sv_eventlog)
+               GameLogEcho(strcat(":part:", ftos(this.playerid)));
+
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
+
+       SetSpectatee(this, NULL);
+
+    MUTATOR_CALLHOOK(ClientDisconnect, this);
+
+       ClientState_detach(this);
+
+       Portal_ClearAll(this);
+
+       Unfreeze(this);
+
+       RemoveGrapplingHook(this);
+
+       // Here, everything has been done that requires this player to be a client.
+
+       this.flags &= ~FL_CLIENT;
+
+       if (this.chatbubbleentity) delete(this.chatbubbleentity);
+       if (this.killindicator) delete(this.killindicator);
+
+       WaypointSprite_PlayerGone(this);
+
+       bot_relinkplayerlist();
+
+       if (this.netname_previous) strunzone(this.netname_previous);
+       if (this.clientstatus) strunzone(this.clientstatus);
+       if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse);
+       if (this.personal) delete(this.personal);
+
+       this.playerid = 0;
+       ReadyCount();
+       if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
+
+       ONREMOVE(this);
+}
+
+void ChatBubbleThink(entity this)
+{
+       this.nextthink = time;
+       if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this)
+       {
+               if(this.owner) // but why can that ever be NULL?
+                       this.owner.chatbubbleentity = NULL;
+               delete(this);
+               return;
+       }
+
+       this.mdl = "";
+
+       if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) )
+       {
+               if ( this.owner.active_minigame )
+                       this.mdl = "models/sprites/minigame_busy.iqm";
+               else if (PHYS_INPUT_BUTTON_CHAT(this.owner))
+                       this.mdl = "models/misc/chatbubble.spr";
+       }
+
+       if ( this.model != this.mdl )
+               _setmodel(this, this.mdl);
+
+}
+
+void UpdateChatBubble(entity this)
+{
+       if (this.alpha < 0)
+               return;
+       // spawn a chatbubble entity if needed
+       if (!this.chatbubbleentity)
+       {
+               this.chatbubbleentity = new(chatbubbleentity);
+               this.chatbubbleentity.owner = this;
+               this.chatbubbleentity.exteriormodeltoclient = this;
+               setthink(this.chatbubbleentity, ChatBubbleThink);
+               this.chatbubbleentity.nextthink = time;
+               setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
+               //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
+               setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1');
+               setattachment(this.chatbubbleentity, this, "");  // sticks to moving player better, also conserves bandwidth
+               this.chatbubbleentity.mdl = this.chatbubbleentity.model;
+               //this.chatbubbleentity.model = "";
+               this.chatbubbleentity.effects = EF_LOWPRECISION;
+       }
+}
+
+
+// LordHavoc: this hack will be removed when proper _pants/_shirt layers are
+// added to the model skins
+/*void UpdateColorModHack()
+{
+       float c;
+       c = this.clientcolors & 15;
+       // LordHavoc: only bothering to support white, green, red, yellow, blue
+            if (!teamplay) this.colormod = '0 0 0';
+       else if (c ==  0) this.colormod = '1.00 1.00 1.00';
+       else if (c ==  3) this.colormod = '0.10 1.73 0.10';
+       else if (c ==  4) this.colormod = '1.73 0.10 0.10';
+       else if (c == 12) this.colormod = '1.22 1.22 0.10';
+       else if (c == 13) this.colormod = '0.10 0.10 1.73';
+       else this.colormod = '1 1 1';
+}*/
+
+void respawn(entity this)
+{
+       if(this.alpha >= 0 && autocvar_g_respawn_ghosts)
+       {
+               this.solid = SOLID_NOT;
+               this.takedamage = DAMAGE_NO;
+               set_movetype(this, MOVETYPE_FLY);
+               this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
+               this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
+               this.effects |= CSQCMODEL_EF_RESPAWNGHOST;
+               Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1);
+               if(autocvar_g_respawn_ghosts_maxtime)
+                       SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
+       }
+
+       CopyBody(this, 1);
+
+       this.effects |= EF_NODRAW; // prevent another CopyBody
+       PutClientInServer(this);
+}
+
+void play_countdown(entity this, float finished, Sound samp)
+{
+    TC(Sound, samp);
+       if(IS_REAL_CLIENT(this))
+               if(floor(finished - time - frametime) != floor(finished - time))
+                       if(finished - time < 6)
+                               sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
+}
+
+void player_powerups(entity this)
+{
+       // add a way to see what the items were BEFORE all of these checks for the mutator hook
+       int items_prev = this.items;
+
+       if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover)
+               this.modelflags |= MF_ROCKET;
+       else
+               this.modelflags &= ~MF_ROCKET;
+
+       this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
+
+       if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
+               return;
+
+       Fire_ApplyDamage(this);
+       Fire_ApplyEffect(this);
+
+       if (!g_instagib)
+       {
+               if (this.items & ITEM_Strength.m_itemid)
+               {
+                       play_countdown(this, this.strength_finished, SND_POWEROFF);
+                       this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > this.strength_finished)
+                       {
+                               this.items = this.items - (this.items & ITEM_Strength.m_itemid);
+                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
+                       }
+               }
+               else
+               {
+                       if (time < this.strength_finished)
+                       {
+                               this.items = this.items | ITEM_Strength.m_itemid;
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH);
+                       }
+               }
+               if (this.items & ITEM_Shield.m_itemid)
+               {
+                       play_countdown(this, this.invincible_finished, SND_POWEROFF);
+                       this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > this.invincible_finished)
+                       {
+                               this.items = this.items - (this.items & ITEM_Shield.m_itemid);
+                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
+                       }
+               }
+               else
+               {
+                       if (time < this.invincible_finished)
+                       {
+                               this.items = this.items | ITEM_Shield.m_itemid;
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD);
+                       }
+               }
+               if (this.items & IT_SUPERWEAPON)
+               {
+                       if (!(this.weapons & WEPSET_SUPERWEAPONS))
+                       {
+                               this.superweapons_finished = 0;
+                               this.items = this.items - (this.items & IT_SUPERWEAPON);
+                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
+                       }
+                       else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
+                       {
+                               // don't let them run out
+                       }
+                       else
+                       {
+                               play_countdown(this, this.superweapons_finished, SND_POWEROFF);
+                               if (time > this.superweapons_finished)
+                               {
+                                       this.items = this.items - (this.items & IT_SUPERWEAPON);
+                                       this.weapons &= ~WEPSET_SUPERWEAPONS;
+                                       //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
+                               }
+                       }
+               }
+               else if(this.weapons & WEPSET_SUPERWEAPONS)
+               {
+                       if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS))
+                       {
+                               this.items = this.items | IT_SUPERWEAPON;
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
+                       }
+                       else
+                       {
+                               this.superweapons_finished = 0;
+                               this.weapons &= ~WEPSET_SUPERWEAPONS;
+                       }
+               }
+               else
+               {
+                       this.superweapons_finished = 0;
+               }
+       }
+
+       if(autocvar_g_nodepthtestplayers)
+               this.effects = this.effects | EF_NODEPTHTEST;
+
+       if(autocvar_g_fullbrightplayers)
+               this.effects = this.effects | EF_FULLBRIGHT;
+
+       if (time >= game_starttime)
+       if (time < this.spawnshieldtime)
+               this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
+
+       MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
+}
+
+float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
+{
+       if(current > stable)
+               return current;
+       else if(current > stable - 0.25) // when close enough, "snap"
+               return stable;
+       else
+               return min(stable, current + (stable - current) * regenfactor * regenframetime);
+}
+
+float CalcRot(float current, float stable, float rotfactor, float rotframetime)
+{
+       if(current < stable)
+               return current;
+       else if(current < stable + 0.25) // when close enough, "snap"
+               return stable;
+       else
+               return max(stable, current + (stable - current) * rotfactor * rotframetime);
+}
+
+float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit)
+{
+       if(current > rotstable)
+       {
+               if(rotframetime > 0)
+               {
+                       current = CalcRot(current, rotstable, rotfactor, rotframetime);
+                       current = max(rotstable, current - rotlinear * rotframetime);
+               }
+       }
+       else if(current < regenstable)
+       {
+               if(regenframetime > 0)
+               {
+                       current = CalcRegen(current, regenstable, regenfactor, regenframetime);
+                       current = min(regenstable, current + regenlinear * regenframetime);
+               }
+       }
+
+       if(current > limit)
+               current = limit;
+
+       return current;
+}
+
+void player_regen(entity this)
+{
+       float max_mod, regen_mod, rot_mod, limit_mod;
+       max_mod = regen_mod = rot_mod = limit_mod = 1;
+
+       float regen_health = autocvar_g_balance_health_regen;
+       float regen_health_linear = autocvar_g_balance_health_regenlinear;
+       float regen_health_rot = autocvar_g_balance_health_rot;
+       float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
+       float regen_health_stable = autocvar_g_balance_health_regenstable;
+       float regen_health_rotstable = autocvar_g_balance_health_rotstable;
+       bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
+               regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
+       max_mod = M_ARGV(1, float);
+       regen_mod = M_ARGV(2, float);
+       rot_mod = M_ARGV(3, float);
+       limit_mod = M_ARGV(4, float);
+       regen_health = M_ARGV(5, float);
+       regen_health_linear = M_ARGV(6, float);
+       regen_health_rot = M_ARGV(7, float);
+       regen_health_rotlinear = M_ARGV(8, float);
+       regen_health_stable = M_ARGV(9, float);
+       regen_health_rotstable = M_ARGV(10, float);
+
+
+       if(!mutator_returnvalue)
+       if(!STAT(FROZEN, this))
+       {
+               float mina, maxa, limith, limita;
+               maxa = autocvar_g_balance_armor_rotstable;
+               mina = autocvar_g_balance_armor_regenstable;
+               limith = autocvar_g_balance_health_limit;
+               limita = autocvar_g_balance_armor_limit;
+
+               regen_health_rotstable = regen_health_rotstable * max_mod;
+               regen_health_stable = regen_health_stable * max_mod;
+               limith = limith * limit_mod;
+               limita = limita * limit_mod;
+
+               this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita);
+               this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith);
+       }
+
+       // if player rotted to death...  die!
+       // check this outside above checks, as player may still be able to rot to death
+       if(this.health < 1)
+       {
+               if(this.vehicle)
+                       vehicles_exit(this.vehicle, VHEF_RELEASE);
+               if(this.event_damage)
+                       this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0');
+       }
+
+       if (!(this.items & IT_UNLIMITED_WEAPON_AMMO))
+       {
+               float minf, maxf, limitf;
+
+               maxf = autocvar_g_balance_fuel_rotstable;
+               minf = autocvar_g_balance_fuel_regenstable;
+               limitf = autocvar_g_balance_fuel_limit;
+
+               this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf);
+       }
+}
+
+bool zoomstate_set;
+void SetZoomState(entity this, float newzoom)
+{
+       if(newzoom != this.zoomstate)
+       {
+               this.zoomstate = newzoom;
+               ClientData_Touch(this);
+       }
+       zoomstate_set = true;
+}
+
+void GetPressedKeys(entity this)
+{
+       MUTATOR_CALLHOOK(GetPressedKeys, this);
+       int keys = STAT(PRESSED_KEYS, this);
+       keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
+       keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
+       keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
+       keys = BITSET(keys, KEY_LEFT,           this.movement.y < 0);
+
+       keys = BITSET(keys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(this));
+       keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
+       keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
+       keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
+       this.pressedkeys = keys; // store for other users
+
+       STAT(PRESSED_KEYS, this) = keys;
+}
+
+/*
+======================
+spectate mode routines
+======================
+*/
+
+void SpectateCopy(entity this, entity spectatee)
+{
+    TC(Client, this); TC(Client, spectatee);
+
+       MUTATOR_CALLHOOK(SpectateCopy, spectatee, this);
+       PS(this) = PS(spectatee);
+       this.armortype = spectatee.armortype;
+       this.armorvalue = spectatee.armorvalue;
+       this.ammo_cells = spectatee.ammo_cells;
+       this.ammo_plasma = spectatee.ammo_plasma;
+       this.ammo_shells = spectatee.ammo_shells;
+       this.ammo_nails = spectatee.ammo_nails;
+       this.ammo_rockets = spectatee.ammo_rockets;
+       this.ammo_fuel = spectatee.ammo_fuel;
+       this.clip_load = spectatee.clip_load;
+       this.clip_size = spectatee.clip_size;
+       this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
+       this.health = spectatee.health;
+       this.impulse = 0;
+       this.items = spectatee.items;
+       this.last_pickup = spectatee.last_pickup;
+       this.hit_time = spectatee.hit_time;
+       this.strength_finished = spectatee.strength_finished;
+       this.invincible_finished = spectatee.invincible_finished;
+       STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
+       this.weapons = spectatee.weapons;
+       this.vortex_charge = spectatee.vortex_charge;
+       this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
+       this.hagar_load = spectatee.hagar_load;
+       this.arc_heat_percent = spectatee.arc_heat_percent;
+       this.minelayer_mines = spectatee.minelayer_mines;
+       this.punchangle = spectatee.punchangle;
+       this.view_ofs = spectatee.view_ofs;
+       this.velocity = spectatee.velocity;
+       this.dmg_take = spectatee.dmg_take;
+       this.dmg_save = spectatee.dmg_save;
+       this.dmg_inflictor = spectatee.dmg_inflictor;
+       this.v_angle = spectatee.v_angle;
+       this.angles = spectatee.v_angle;
+       STAT(FROZEN, this) = STAT(FROZEN, spectatee);
+       this.revive_progress = spectatee.revive_progress;
+       this.viewloc = spectatee.viewloc;
+       if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
+               this.fixangle = true;
+       setorigin(this, spectatee.origin);
+       setsize(this, spectatee.mins, spectatee.maxs);
+       SetZoomState(this, spectatee.zoomstate);
+
+    anticheat_spectatecopy(this, spectatee);
+       this.hud = spectatee.hud;
+       if(spectatee.vehicle)
+    {
+       this.angles = spectatee.v_angle;
+
+        //this.fixangle = false;
+        //this.velocity = spectatee.vehicle.velocity;
+        this.vehicle_health = spectatee.vehicle_health;
+        this.vehicle_shield = spectatee.vehicle_shield;
+        this.vehicle_energy = spectatee.vehicle_energy;
+        this.vehicle_ammo1 = spectatee.vehicle_ammo1;
+        this.vehicle_ammo2 = spectatee.vehicle_ammo2;
+        this.vehicle_reload1 = spectatee.vehicle_reload1;
+        this.vehicle_reload2 = spectatee.vehicle_reload2;
+
+        //msg_entity = this;
+
+       // WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
+            //WriteAngle(MSG_ONE,  spectatee.v_angle.x);
+           // WriteAngle(MSG_ONE,  spectatee.v_angle.y);
+           // WriteAngle(MSG_ONE,  spectatee.v_angle.z);
+
+        //WriteByte (MSG_ONE, SVC_SETVIEW);
+        //    WriteEntity(MSG_ONE, this);
+        //makevectors(spectatee.v_angle);
+        //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/
+    }
+}
+
+bool SpectateUpdate(entity this)
+{
+       if(!this.enemy)
+               return false;
+
+       if(!IS_PLAYER(this.enemy) || this == this.enemy)
+       {
+               SetSpectatee(this, NULL);
+               return false;
+       }
+
+       SpectateCopy(this, this.enemy);
+
+       return true;
+}
+
+bool SpectateSet(entity this)
+{
+       if(!IS_PLAYER(this.enemy))
+               return false;
+
+       ClientData_Touch(this.enemy);
+
+       msg_entity = this;
+       WriteByte(MSG_ONE, SVC_SETVIEW);
+       WriteEntity(MSG_ONE, this.enemy);
+       set_movetype(this, MOVETYPE_NONE);
+       accuracy_resend(this);
+
+       if(!SpectateUpdate(this))
+               PutObserverInServer(this);
+
+       return true;
+}
+
+void SetSpectatee_status(entity this, int spectatee_num)
+{
+       int oldspectatee_status = this.spectatee_status;
+       this.spectatee_status = spectatee_num;
+
+       if (this.spectatee_status != oldspectatee_status)
+       {
+               ClientData_Touch(this);
+               if (g_race || g_cts) race_InitSpectator();
+       }
+}
+
+void SetSpectatee(entity this, entity spectatee)
+{
+       entity old_spectatee = this.enemy;
+
+       this.enemy = spectatee;
+
+       // WEAPONTODO
+       // these are required to fix the spectator bug with arc
+       if(old_spectatee)
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(old_spectatee.(weaponentity).arc_beam)
+                               old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS;
+               }
+       }
+       if(this.enemy)
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(this.enemy.(weaponentity).arc_beam)
+                               this.enemy.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS;
+               }
+       }
+
+       if (this.enemy)
+               SetSpectatee_status(this, etof(this.enemy));
+
+       // needed to update spectator list
+       if(old_spectatee) { ClientData_Touch(old_spectatee); }
+}
+
+bool Spectate(entity this, entity pl)
+{
+       if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
+               return false;
+       pl = M_ARGV(1, entity);
+
+       SetSpectatee(this, pl);
+       return SpectateSet(this);
+}
+
+bool SpectateNext(entity this)
+{
+       entity ent = find(this.enemy, classname, STR_PLAYER);
+
+       if (MUTATOR_CALLHOOK(SpectateNext, this, ent))
+               ent = M_ARGV(1, entity);
+       else if (!ent)
+               ent = find(ent, classname, STR_PLAYER);
+
+       if(ent) { SetSpectatee(this, ent); }
+
+       return SpectateSet(this);
+}
+
+bool SpectatePrev(entity this)
+{
+       // NOTE: chain order is from the highest to the lower entnum (unlike find)
+       entity ent = findchain(classname, STR_PLAYER);
+       if (!ent) // no player
+               return false;
+
+       entity first = ent;
+       // skip players until current spectated player
+       if(this.enemy)
+       while(ent && ent != this.enemy)
+               ent = ent.chain;
+
+       switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first))
+       {
+               case MUT_SPECPREV_FOUND:
+                   ent = M_ARGV(1, entity);
+                   break;
+               case MUT_SPECPREV_RETURN:
+                   return true;
+               case MUT_SPECPREV_CONTINUE:
+               default:
+               {
+                       if(ent.chain)
+                               ent = ent.chain;
+                       else
+                               ent = first;
+                       break;
+               }
+       }
+
+       SetSpectatee(this, ent);
+       return SpectateSet(this);
+}
+
+/*
+=============
+ShowRespawnCountdown()
+
+Update a respawn countdown display.
+=============
+*/
+void ShowRespawnCountdown(entity this)
+{
+       float number;
+       if(!IS_DEAD(this)) // just respawned?
+               return;
+       else
+       {
+               number = ceil(this.respawn_time - time);
+               if(number <= 0)
+                       return;
+               if(number <= this.respawn_countdown)
+               {
+                       this.respawn_countdown = number - 1;
+                       if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
+                               { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); }
+               }
+       }
+}
+
+.bool team_selected;
+bool ShowTeamSelection(entity this)
+{
+       if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
+               return false;
+       stuffcmd(this, "menu_showteamselect\n");
+       return true;
+}
+void Join(entity this)
+{
+       TRANSMUTE(Player, this);
+
+       if(!this.team_selected)
+       if(autocvar_g_campaign || autocvar_g_balance_teams)
+               JoinBestTeam(this, false, true);
+
+       if(autocvar_g_campaign)
+               campaign_bots_may_start = true;
+
+       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
+
+       PutClientInServer(this);
+
+       if(teamplay && this.team != -1)
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_PLAY_TEAM), this.netname);
+       else
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
+       this.team_selected = false;
+}
+
+/**
+ * Determines whether the player is allowed to join. This depends on cvar
+ * g_maxplayers, if it isn't used this function always return true, otherwise
+ * it checks whether the number of currently playing players exceeds g_maxplayers.
+ * @return int number of free slots for players, 0 if none
+ */
+int nJoinAllowed(entity this, entity ignore)
+{
+       if(!ignore)
+       // this is called that way when checking if anyone may be able to join (to build qcstatus)
+       // so report 0 free slots if restricted
+       {
+               if(autocvar_g_forced_team_otherwise == "spectate")
+                       return 0;
+               if(autocvar_g_forced_team_otherwise == "spectator")
+                       return 0;
+       }
+
+       if(this && this.team_forced < 0)
+               return 0; // forced spectators can never join
+
+       // TODO simplify this
+       int totalClients = 0;
+       int currentlyPlaying = 0;
+       FOREACH_CLIENT(true, LAMBDA(
+               if(it != ignore)
+                       ++totalClients;
+               if(IS_REAL_CLIENT(it))
+               if(IS_PLAYER(it) || it.caplayer)
+                       ++currentlyPlaying;
+       ));
+
+       float free_slots = 0;
+       if (!autocvar_g_maxplayers)
+               free_slots = maxclients - totalClients;
+       else if(currentlyPlaying < autocvar_g_maxplayers)
+               free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
+
+       static float join_prevent_msg_time = 0;
+       if(this && ignore && !free_slots && time > join_prevent_msg_time)
+       {
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
+               join_prevent_msg_time = time + 3;
+       }
+
+       return free_slots;
+}
+
+/**
+ * Checks whether the client is an observer or spectator, if so, he will get kicked after
+ * g_maxplayers_spectator_blocktime seconds
+ */
+void checkSpectatorBlock(entity this)
+{
+       if(IS_SPEC(this) || IS_OBSERVER(this))
+       if(!this.caplayer)
+       if(IS_REAL_CLIENT(this))
+       {
+               if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
+                       dropclient(this);
+               }
+       }
+}
+
+void PrintWelcomeMessage(entity this)
+{
+       if(this.motd_actived_time == 0)
+       {
+               if (autocvar_g_campaign) {
+                       if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) {
+                               this.motd_actived_time = time;
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message);
+                       }
+               } else {
+                       if (PHYS_INPUT_BUTTON_INFO(this)) {
+                               this.motd_actived_time = time;
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
+                       }
+               }
+       }
+       else if(this.motd_actived_time > 0) // showing MOTD or campaign message
+       {
+               if (autocvar_g_campaign) {
+                       if (PHYS_INPUT_BUTTON_INFO(this))
+                               this.motd_actived_time = time;
+                       else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
+                               this.motd_actived_time = 0;
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
+                       }
+               } else {
+                       if (PHYS_INPUT_BUTTON_INFO(this))
+                               this.motd_actived_time = time;
+                       else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
+                               this.motd_actived_time = 0;
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
+                       }
+               }
+       }
+       else //if(this.motd_actived_time < 0) // just connected, motd is active
+       {
+               if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD
+                       this.motd_actived_time = -2; // wait until BUTTON_INFO gets released
+               else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this))
+               {
+                       // instanctly hide MOTD
+                       this.motd_actived_time = 0;
+                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
+               }
+       }
+}
+
+bool joinAllowed(entity this)
+{
+       if (this.version_mismatch) return false;
+       if (!nJoinAllowed(this, this)) return false;
+       if (teamplay && lockteams) return false;
+       if (ShowTeamSelection(this)) return false;
+       if (MUTATOR_CALLHOOK(ForbidSpawn, this)) return false;
+       return true;
+}
+
+void ObserverThink(entity this)
+{
+       if ( this.impulse )
+       {
+               MinigameImpulse(this, this.impulse);
+               this.impulse = 0;
+       }
+
+       if (this.flags & FL_JUMPRELEASED) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       this.flags |= FL_SPAWNING;
+               } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       if(SpectateNext(this)) {
+                               TRANSMUTE(Spectator, this);
+                       }
+               } else {
+                       int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       set_movetype(this, preferred_movetype);
+               }
+       } else {
+               if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) {
+                       this.flags |= FL_JUMPRELEASED;
+                       if(this.flags & FL_SPAWNING)
+                       {
+                               this.flags &= ~FL_SPAWNING;
+                               Join(this);
+                               return;
+                       }
+               }
+       }
+}
+
+void SpectatorThink(entity this)
+{
+       if ( this.impulse )
+       {
+               if(MinigameImpulse(this, this.impulse))
+                       this.impulse = 0;
+
+               if (this.impulse == IMP_weapon_drop.impulse)
+               {
+                       STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3;
+                       this.impulse = 0;
+                       return;
+               }
+       }
+
+       if (this.flags & FL_JUMPRELEASED) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       this.flags |= FL_SPAWNING;
+               } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       if(SpectateNext(this)) {
+                               TRANSMUTE(Spectator, this);
+                       } else {
+                               TRANSMUTE(Observer, this);
+                               PutClientInServer(this);
+                       }
+                       this.impulse = 0;
+               } else if(this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       if(SpectatePrev(this)) {
+                               TRANSMUTE(Spectator, this);
+                       } else {
+                               TRANSMUTE(Observer, this);
+                               PutClientInServer(this);
+                       }
+                       this.impulse = 0;
+               } else if (PHYS_INPUT_BUTTON_ATCK2(this)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       TRANSMUTE(Observer, this);
+                       PutClientInServer(this);
+               } else {
+                       if(!SpectateUpdate(this))
+                               PutObserverInServer(this);
+               }
+       } else {
+               if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) {
+                       this.flags |= FL_JUMPRELEASED;
+                       if(this.flags & FL_SPAWNING)
+                       {
+                               this.flags &= ~FL_SPAWNING;
+                               Join(this);
+                               return;
+                       }
+               }
+               if(!SpectateUpdate(this))
+                       PutObserverInServer(this);
+       }
+
+       this.flags |= FL_CLIENT | FL_NOTARGET;
+}
+
+void vehicles_enter (entity pl, entity veh);
+void PlayerUseKey(entity this)
+{
+       if (!IS_PLAYER(this))
+               return;
+
+       if(this.vehicle)
+       {
+               if(!gameover)
+               {
+                       vehicles_exit(this.vehicle, VHEF_NORMAL);
+                       return;
+               }
+       }
+       else if(autocvar_g_vehicles_enter)
+       {
+               if(!STAT(FROZEN, this))
+               if(!IS_DEAD(this))
+               if(!gameover)
+               {
+                       entity head, closest_target = NULL;
+                       head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true);
+
+                       while(head) // find the closest acceptable target to enter
+                       {
+                               if(IS_VEHICLE(head))
+                               if(!IS_DEAD(head))
+                               if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this)))
+                               if(head.takedamage != DAMAGE_NO)
+                               {
+                                       if(closest_target)
+                                       {
+                                               if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin))
+                                               { closest_target = head; }
+                                       }
+                                       else { closest_target = head; }
+                               }
+
+                               head = head.chain;
+                       }
+
+                       if(closest_target) { vehicles_enter(this, closest_target); return; }
+               }
+       }
+
+       // a use key was pressed; call handlers
+       MUTATOR_CALLHOOK(PlayerUseKey, this);
+}
+
+
+/*
+=============
+PlayerPreThink
+
+Called every frame for each client before the physics are run
+=============
+*/
+.float usekeypressed;
+.float last_vehiclecheck;
+.int items_added;
+void PlayerPreThink (entity this)
+{
+       WarpZone_PlayerPhysics_FixVAngle(this);
+
+    STAT(GAMESTARTTIME, this) = game_starttime;
+       STAT(ROUNDSTARTTIME, this) = round_starttime;
+       STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam;
+       STAT(LEADLIMIT, this) = autocvar_leadlimit;
+
+       STAT(WEAPONSINMAP, this) = weaponsInMap;
+
+       if (frametime) {
+               // physics frames: update anticheat stuff
+               anticheat_prethink(this);
+       }
+
+       if (blockSpectators && frametime) {
+               // WORKAROUND: only use dropclient in server frames (frametime set).
+               // Never use it in cl_movement frames (frametime zero).
+               checkSpectatorBlock(this);
+    }
+
+       zoomstate_set = false;
+
+       // Check for nameless players
+       if (isInvisibleString(this.netname)) {
+               this.netname = strzone(sprintf("Player#%d", this.playerid));
+               // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
+       }
+       if (this.netname != this.netname_previous) {
+               if (autocvar_sv_eventlog) {
+                       GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname));
+        }
+               if (this.netname_previous) strunzone(this.netname_previous);
+               this.netname_previous = strzone(this.netname);
+       }
+
+       // version nagging
+       if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) {
+        this.version_nagtime = 0;
+        if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) {
+            // git client
+        } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
+            // git server
+            Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
+        } else {
+            int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion);
+            if (r < 0) { // old client
+                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
+            } else if (r > 0) { // old server
+                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
+            }
+        }
+    }
+
+       // GOD MODE info
+       if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
+       {
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
+               this.max_armorvalue = 0;
+       }
+
+       if (STAT(FROZEN, this) == 2)
+       {
+               this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1);
+               this.health = max(1, this.revive_progress * start_health);
+               this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1);
+
+               if (this.revive_progress >= 1)
+                       Unfreeze(this);
+       }
+       else if (STAT(FROZEN, this) == 3)
+       {
+               this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1);
+               this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress );
+
+               if (this.health < 1)
+               {
+                       if (this.vehicle)
+                               vehicles_exit(this.vehicle, VHEF_RELEASE);
+                       if(this.event_damage)
+                               this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
+               }
+               else if (this.revive_progress <= 0)
+                       Unfreeze(this);
+       }
+
+       MUTATOR_CALLHOOK(PlayerPreThink, this);
+
+       if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !gameover && !this.vehicle)
+       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this))
+       {
+               FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it),
+               {
+                       if(!IS_DEAD(it) && it.takedamage != DAMAGE_NO)
+                       if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this))
+                       {
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
+                       }
+                       else if(!it.owner)
+                       {
+                               if(!it.team || SAME_TEAM(this, it))
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
+                               else if(autocvar_g_vehicles_steal)
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
+                       }
+               });
+
+               this.last_vehiclecheck = time + 1;
+       }
+
+       if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
+       {
+               if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed)
+                       PlayerUseKey(this);
+               this.usekeypressed = PHYS_INPUT_BUTTON_USE(this);
+       }
+
+       if (IS_REAL_CLIENT(this))
+               PrintWelcomeMessage(this);
+
+       if (IS_PLAYER(this)) {
+               CheckRules_Player(this);
+
+               if (gameover || intermission_running) {
+                       if(intermission_running)
+                               IntermissionThink(this);
+                       return;
+               }
+
+               if (timeout_status == TIMEOUT_ACTIVE) {
+            // don't allow the player to turn around while game is paused
+                       // FIXME turn this into CSQC stuff
+                       this.v_angle = this.lastV_angle;
+                       this.angles = this.lastV_angle;
+                       this.fixangle = true;
+               }
+
+               if (frametime) player_powerups(this);
+
+               if (IS_DEAD(this)) {
+                       if (this.personal && g_race_qualifying) {
+                               if (time > this.respawn_time) {
+                                       STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
+                                       respawn(this);
+                                       this.impulse = CHIMPULSE_SPEEDRUN.impulse;
+                               }
+                       } else {
+                               if (frametime) player_anim(this);
+                               bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
+
+                               switch(this.deadflag)
+                               {
+                                       case DEAD_DYING:
+                                       {
+                                               if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
+                                                       this.deadflag = DEAD_RESPAWNING;
+                                               else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
+                                                       this.deadflag = DEAD_DEAD;
+                                               break;
+                                       }
+                                       case DEAD_DEAD:
+                                       {
+                                               if (button_pressed)
+                                                       this.deadflag = DEAD_RESPAWNABLE;
+                                               else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
+                                                       this.deadflag = DEAD_RESPAWNING;
+                                               break;
+                                       }
+                                       case DEAD_RESPAWNABLE:
+                                       {
+                                               if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
+                                                       this.deadflag = DEAD_RESPAWNING;
+                                               break;
+                                       }
+                                       case DEAD_RESPAWNING:
+                                       {
+                                               if (time > this.respawn_time)
+                                               {
+                                                       this.respawn_time = time + 1; // only retry once a second
+                                                       this.respawn_time_max = this.respawn_time;
+                                                       respawn(this);
+                                               }
+                                               break;
+                                       }
+                               }
+
+                               ShowRespawnCountdown(this);
+
+                               if (this.respawn_flags & RESPAWN_SILENT)
+                                       STAT(RESPAWN_TIME, this) = 0;
+                               else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
+                               {
+                                       if (time < this.respawn_time)
+                                               STAT(RESPAWN_TIME, this) = this.respawn_time;
+                                       else if (this.deadflag != DEAD_RESPAWNING)
+                                               STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
+                               }
+                               else
+                                       STAT(RESPAWN_TIME, this) = this.respawn_time;
+                       }
+
+                       // if respawning, invert stat_respawn_time to indicate this, the client translates it
+                       if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
+                               STAT(RESPAWN_TIME, this) *= -1;
+
+                       return;
+               }
+
+               this.prevorigin = this.origin;
+
+               bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
+               if (this.hook.state) {
+                       do_crouch = false;
+               } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
+                       do_crouch = false;
+               } else if (this.vehicle) {
+                       do_crouch = false;
+               } else if (STAT(FROZEN, this)) {
+                       do_crouch = false;
+        }
+
+               if (do_crouch) {
+                       if (!this.crouch) {
+                               this.crouch = true;
+                               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
+                               setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
+                               // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway
+                       }
+               } else if (this.crouch) {
+            tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this);
+            if (!trace_startsolid) {
+                this.crouch = false;
+                this.view_ofs = STAT(PL_VIEW_OFS, this);
+                setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
+            }
+               }
+
+               FixPlayermodel(this);
+
+               // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
+               //if(frametime)
+               {
+                       this.items &= ~this.items_added;
+
+                       //for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                       //{
+                               //.entity weaponentity = weaponentities[slot];
+                               //W_WeaponFrame(this, weaponentity);
+                       //}
+                       .entity weaponentity = weaponentities[0]; // TODO
+                       W_WeaponFrame(this, weaponentity);
+
+                       this.items_added = 0;
+                       if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01))
+                this.items_added |= IT_FUEL;
+
+                       this.items |= this.items_added;
+               }
+
+               player_regen(this);
+
+               // WEAPONTODO: Add a weapon request for this
+               // rot vortex charge to the charge limit
+               if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time)
+                       this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
+
+               if (frametime) player_anim(this);
+
+               // secret status
+               secrets_setstatus(this);
+
+               // monsters status
+               monsters_setstatus(this);
+
+               this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
+       }
+       else if (gameover || intermission_running) {
+               if(intermission_running)
+                       IntermissionThink(this);
+               return;
+       }
+       else if (IS_OBSERVER(this)) {
+               ObserverThink(this);
+       }
+       else if (IS_SPEC(this)) {
+               SpectatorThink(this);
+       }
+
+       // WEAPONTODO: Add weapon request for this
+       if (!zoomstate_set) {
+               SetZoomState(this,
+                       PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this)
+                       || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX)
+                       || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)
+               );
+    }
+
+       if (this.teamkill_soundtime && time > this.teamkill_soundtime)
+       {
+               this.teamkill_soundtime = 0;
+
+               entity e = this.teamkill_soundsource;
+               entity oldpusher = e.pusher;
+               e.pusher = this;
+               PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY);
+               e.pusher = oldpusher;
+       }
+
+       if (this.taunt_soundtime && time > this.taunt_soundtime) {
+               this.taunt_soundtime = 0;
+               PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT);
+       }
+
+       target_voicescript_next(this);
+
+       // WEAPONTODO: Move into weaponsystem somehow
+       // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
+       if (PS(this).m_weapon == WEP_Null)
+               this.clip_load = this.clip_size = 0;
+}
+
+void DrownPlayer(entity this)
+{
+       if(IS_DEAD(this))
+               return;
+
+       if (this.waterlevel != WATERLEVEL_SUBMERGED || this.vehicle)
+       {
+               if(this.air_finished < time)
+                       PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
+               this.air_finished = time + autocvar_g_balance_contents_drowndelay;
+               this.dmg = 2;
+       }
+       else if (this.air_finished < time)
+       {       // drown!
+               if (this.pain_finished < time)
+               {
+                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0');
+                       this.pain_finished = time + 0.5;
+               }
+       }
+}
+
+.bool move_qcphysics;
+
+void Player_Physics(entity this)
+{
+       set_movetype(this, this.move_movetype);
+
+       if(!this.move_qcphysics)
+               return;
+
+       if(!frametime && !this.pm_frametime)
+               return;
+
+       Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true);
+
+       this.pm_frametime = 0;
+}
+
+/*
+=============
+PlayerPostThink
+
+Called every frame for each client after the physics are run
+=============
+*/
+.float idlekick_lasttimeleft;
+void PlayerPostThink (entity this)
+{
+       Player_Physics(this);
+
+       if (sv_maxidle > 0)
+       if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
+       if (IS_REAL_CLIENT(this))
+       if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle)
+       {
+               int totalClients = 0;
+               if(sv_maxidle_slots > 0)
+               {
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) || sv_maxidle_slots_countbots,
+                       {
+                               ++totalClients;
+                       });
+               }
+
+               if (sv_maxidle_slots > 0 && (maxclients - totalClients) > sv_maxidle_slots)
+               { /* do nothing */ }
+               else if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
+               {
+                       if (this.idlekick_lasttimeleft)
+                       {
+                               this.idlekick_lasttimeleft = 0;
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
+                       }
+               }
+               else
+               {
+                       float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince));
+                       if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10
+                               if (!this.idlekick_lasttimeleft)
+                                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
+                       }
+                       if (timeleft <= 0) {
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
+                               dropclient(this);
+                               return;
+                       }
+                       else if (timeleft <= 10) {
+                               if (timeleft != this.idlekick_lasttimeleft) {
+                                   Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft));
+                }
+                               this.idlekick_lasttimeleft = timeleft;
+                       }
+               }
+       }
+
+       CheatFrame(this);
+
+       //CheckPlayerJump();
+       if (gameover)
+       {
+               this.solid = SOLID_NOT;
+               this.takedamage = DAMAGE_NO;
+               set_movetype(this, MOVETYPE_NONE);
+       }
+
+       if (IS_PLAYER(this)) {
+               DrownPlayer(this);
+               CheckRules_Player(this);
+               UpdateChatBubble(this);
+               if (this.impulse) ImpulseCommands(this);
+               if (gameover)
+               {
+                       CSQCMODEL_AUTOUPDATE(this);
+                       return;
+               }
+               GetPressedKeys(this);
+       }
+
+       if (this.waypointsprite_attachedforcarrier) {
+           vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id);
+               WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v);
+    }
+
+       playerdemo_write(this);
+
+       CSQCMODEL_AUTOUPDATE(this);
+}
diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh
new file mode 100644 (file)
index 0000000..c2317af
--- /dev/null
@@ -0,0 +1,115 @@
+#pragma once
+
+void ClientState_attach(entity this);
+
+IntrusiveList g_players;
+STATIC_INIT(g_players) { g_players = IL_NEW(); }
+
+CLASS(Client, Object)
+    /** Client name */
+    ATTRIB(Client, netname, string, this.netname);
+    ATTRIB(Client, colormap, int, this.colormap);
+    ATTRIB(Client, team, int, this.team);
+    ATTRIB(Client, clientcolors, int, this.clientcolors);
+    /** Client IP */
+    ATTRIB(Client, netaddress, string, this.netaddress);
+    ATTRIB(Client, playermodel, string, this.playermodel);
+    ATTRIB(Client, playerskin, int, this.playerskin);
+
+    /** fingerprint of CA key the player used to authenticate */
+    ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp);
+    /** fingerprint of CA key the server used to authenticate to the player */
+    ATTRIB(Client, crypto_mykeyfp, string, this.crypto_mykeyfp);
+    /** fingerprint of ID used by the player entity, or string_null if not identified */
+    ATTRIB(Client, crypto_idfp, string, this.crypto_idfp);
+    /** set if the player's ID has been signed */
+    ATTRIB(Client, crypto_idfp_signed, bool, this.crypto_idfp_signed);
+    /** the string "AES128" if encrypting, and string_null if plaintext */
+    ATTRIB(Client, crypto_encryptmethod, string, this.crypto_encryptmethod);
+    /** the string "HMAC-SHA256" if signing, and string_null if plaintext */
+    ATTRIB(Client, crypto_signmethod, string, this.crypto_signmethod);
+
+    // custom
+
+    ATTRIB(Client, playerid, int, this.playerid);
+
+    METHOD(Client, m_unwind, bool(Client this));
+
+    STATIC_METHOD(Client, Add, void(Client this, int _team));
+    STATIC_METHOD(Client, Remove, void(Client this));
+
+    INIT(Client) {
+        if (this.m_unwind(this)) return this;
+        make_impure(this);
+        this.classname = "player_joining";
+        static int playerid_last;
+        this.playerid = ++playerid_last;
+        ClientState_attach(this);
+    }
+    DESTRUCTOR(Client) {
+        Client_Remove(this);
+    }
+    CONSTRUCTOR(Client, string name) {
+        CONSTRUCT(Client);
+        this.netname = name;
+        this.netaddress = "local";
+        this.playermodel = "models/player/megaerebus.iqm";
+    }
+ENDCLASS(Client)
+
+CLASS(Observer, Client)
+    INIT(Observer) {
+        this.classname = STR_OBSERVER;
+    }
+    DESTRUCTOR(Observer) { }
+ENDCLASS(Observer)
+
+CLASS(Spectator, Client)
+    INIT(Spectator) {
+        this.classname = STR_SPECTATOR;
+    }
+    DESTRUCTOR(Spectator) { }
+ENDCLASS(Spectator)
+
+CLASS(Player, Client)
+    INIT(Player) {
+        this.classname = STR_PLAYER;
+        IL_PUSH(g_players, this);
+    }
+    DESTRUCTOR(Player) {
+        IL_REMOVE(g_players, this);
+    }
+ENDCLASS(Player)
+
+METHOD(Client, m_unwind, bool(Client this))
+{
+    TC(Client, this);
+    #define UNWIND(class) MACRO_BEGIN if (this.instanceOf##class) { METHOD_REFERENCE(class, dtorimpl)(this); } MACRO_END
+    switch (this.classname) {
+        case "Observer":
+            UNWIND(Spectator);
+            UNWIND(Player);
+            return true;
+        case "Spectator":
+            UNWIND(Observer);
+            UNWIND(Player);
+            return true;
+        case "Player":
+            UNWIND(Observer);
+            UNWIND(Spectator);
+            return true;
+    }
+    #undef UNWIND
+    return false;
+}
+
+float c1, c2, c3, c4;
+
+void play_countdown(entity this, float finished, Sound samp);
+
+float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit);
+
+bool Spectate(entity this, entity pl);
+
+#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee)
+#define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); }
index fa15432311d98b7a247ca547451b3fbdd6310131..2cabd69c7f8f35fb58e029bb4bb629c85dbf3bbd 100644 (file)
@@ -1,5 +1,4 @@
 // generated file; do not modify
-#include <server/command/all.qc>
 #include <server/command/banning.qc>
 #include <server/command/cmd.qc>
 #ifdef SVQC
@@ -8,4 +7,5 @@
 #include <server/command/common.qc>
 #include <server/command/getreplies.qc>
 #include <server/command/radarmap.qc>
+#include <server/command/reg.qc>
 #include <server/command/vote.qc>
index 60e34ffe521214af8f4e232cd59c93b0a4e406fc..6a3b175a4bad845e0692e2f9446d6b8b21d89bfb 100644 (file)
@@ -1,8 +1,11 @@
 // generated file; do not modify
-#include <server/command/all.qh>
 #include <server/command/banning.qh>
 #include <server/command/cmd.qh>
+#ifdef SVQC
+    #include <server/command/sv_cmd.qh>
+#endif
 #include <server/command/common.qh>
 #include <server/command/getreplies.qh>
 #include <server/command/radarmap.qh>
+#include <server/command/reg.qh>
 #include <server/command/vote.qh>
diff --git a/qcsrc/server/command/all.qc b/qcsrc/server/command/all.qc
deleted file mode 100644 (file)
index bc15eeb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "all.qh"
-#include <common/command/all.qc>
diff --git a/qcsrc/server/command/all.qh b/qcsrc/server/command/all.qh
deleted file mode 100644 (file)
index cde5ef3..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <common/command/command.qh>
-REGISTRY(SERVER_COMMANDS, BITS(7))
-#define SERVER_COMMANDS_from(i) _SERVER_COMMANDS_from(i, NULL)
-REGISTER_REGISTRY(SERVER_COMMANDS)
-REGISTRY_SORT(SERVER_COMMANDS)
-
-#define SERVER_COMMAND(id, description) \
-       CLASS(servercommand_##id, Command) \
-               ATTRIB(servercommand_##id, m_name, string, #id); \
-       ATTRIB(servercommand_##id, m_description, string, description); \
-       ENDCLASS(servercommand_##id) \
-    REGISTER(SERVER_COMMANDS, CMD_SV, id, m_id, NEW(servercommand_##id)); \
-       METHOD(servercommand_##id, m_invokecmd, void(servercommand_##id this, int request, entity caller, int arguments, string command))
-
-STATIC_INIT(SERVER_COMMANDS_aliases) {
-       FOREACH(SERVER_COMMANDS, true, LAMBDA(localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_sv"))));
-}
-
-#include "sv_cmd.qh"
-
-#include "banning.qh"
-#include "cmd.qh"
-#include "common.qh"
-#include "getreplies.qh"
-#include "radarmap.qh"
-#include "vote.qh"
index d6968c262de869826bd4f82db3247e514bbfdd2b..8a35bec2925c2f89658dfe7145308e56dabb0c2a 100644 (file)
@@ -1,10 +1,10 @@
 #include "banning.qh"
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 #include "banning.qh"
 
 #include "common.qh"
 
-#include "../cl_player.qh"
+#include "../player.qh"
 #include "../ipban.qh"
 
 #include <common/util.qh>
index 2ede443b362401cd687f02d1bdb1cb469983ad3d..ea9610b16073048fc10fd73ac97a09e1147c8065 100644 (file)
@@ -1,18 +1,18 @@
 #include "cmd.qh"
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 
 #include "common.qh"
 #include "vote.qh"
 
 #include "../campaign.qh"
 #include "../cheats.qh"
-#include "../cl_player.qh"
+#include "../player.qh"
 #include "../ipban.qh"
 #include "../mapvoting.qh"
 #include "../scores.qh"
 #include "../teamplay.qh"
 
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 
 #ifdef SVQC
        #include <common/vehicles/all.qh>
@@ -30,8 +30,8 @@
 
 #include <common/minigames/sv_minigames.qh>
 
-#include <common/monsters/all.qc>
-#include <common/monsters/spawn.qh>
+#include <common/monsters/_mod.qh>
+#include <common/monsters/sv_spawn.qh>
 #include <common/monsters/sv_monsters.qh>
 
 #include <lib/warpzone/common.qh>
@@ -159,33 +159,19 @@ void ClientCommand_mv_getpicture(entity caller, float request, float argc)  // i
        }
 }
 
+bool joinAllowed(entity this);
+void Join(entity this);
 void ClientCommand_join(entity caller, float request)
 {
        switch (request)
        {
                case CMD_REQUEST_COMMAND:
                {
-                       if (IS_CLIENT(caller))
-                       {
-                               if (!IS_PLAYER(caller) && !lockteams && !gameover)
-                               {
-                                       if (caller.caplayer) return;
-                                       if (nJoinAllowed(caller, caller))
-                                       {
-                                               if (autocvar_g_campaign)   campaign_bots_may_start = true;
-                                               TRANSMUTE(Player, caller);
-                                               PlayerScore_Clear(caller);
-                                               Kill_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CPID_PREVENT_JOIN);
-                                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && caller.team != -1) ? APP_TEAM_ENT(caller, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), caller.netname);
-                                               WITHSELF(caller, PutClientInServer());
-                                       }
-                                       else
-                                       {
-                                               // player may not join because of g_maxplayers is set
-                                               Send_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CENTER_JOIN_PREVENT);
-                                       }
-                               }
-                       }
+                       if (!gameover)
+                       if (IS_CLIENT(caller) && !IS_PLAYER(caller))
+                       if (joinAllowed(caller))
+                               Join(caller);
+
                        return;  // never fall through to usage
                }
 
@@ -326,6 +312,7 @@ void ClientCommand_say_team(entity caller, float request, float argc, string com
        }
 }
 
+.bool team_selected;
 void ClientCommand_selectteam(entity caller, float request, float argc)
 {
        switch (request)
@@ -363,7 +350,7 @@ void ClientCommand_selectteam(entity caller, float request, float argc)
 
                                                                if (selection)
                                                                {
-                                                                       if (caller.team == selection && !IS_DEAD(caller))
+                                                                       if (caller.team == selection && selection != -1 && !IS_DEAD(caller))
                                                                        {
                                                                                sprint(caller, "^7You already are on that team.\n");
                                                                        }
@@ -385,6 +372,8 @@ void ClientCommand_selectteam(entity caller, float request, float argc)
                                                                                }
                                                                                ClientKill_TeamChange(caller, selection);
                                                                        }
+                                                                       if(!IS_PLAYER(caller))
+                                                                               caller.team_selected = true; // avoids asking again for team selection on join
                                                                }
                                                        }
                                                        else
@@ -488,9 +477,11 @@ void ClientCommand_spectate(entity caller, float request)
 
                                if (mutator_returnvalue == MUT_SPECCMD_RETURN) return;
 
-                               if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE) && autocvar_sv_spectate == 1) ClientKill_TeamChange(caller, -2); // observe
+                               if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE))
+                               if (autocvar_sv_spectate == 1)
+                                       ClientKill_TeamChange(caller, -2); // observe
                        }
-                       return;                                                                                                                        // never fall through to usage
+                       return; // never fall through to usage
                }
 
                default:
@@ -704,8 +695,8 @@ void ClientCommand_macro_write_aliases(float fh)
 // ======================================
 // If this function exists, server game code parses clientcommand before the engine code gets it.
 
-void SV_ParseClientCommand(string command)
-{ENGINE_EVENT();
+void SV_ParseClientCommand(entity this, string command)
+{
        // If invalid UTF-8, don't even parse it
        string command2 = "";
        float len = strlen(command);
index 5f2c86e4086c89d6f8bf4547f7b6659aef421785..3f8e4c47af5f31caabf33413712944ff080f037c 100644 (file)
@@ -2,7 +2,6 @@
 
 .float cmd_floodtime;
 .float cmd_floodcount;
-.float lms_spectate_warning;
 
 string MapVote_Suggest(entity this, string m);
 
index 3c97737e541d559ab13723d0091a6d361986adcd..5b01f134942729fe68f746328195d4c8d70665ae 100644 (file)
@@ -1,10 +1,10 @@
 #include "common.qh"
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 #include "common.qh"
 
 #include "../scores.qh"
 
-#include <common/monsters/all.qh>
+#include <common/monsters/_mod.qh>
 #include <common/notifications/all.qh>
 #include <lib/warpzone/common.qh>
 
@@ -189,7 +189,7 @@ void timeout_handler_reset(entity this)
        timeout_time = 0;
        timeout_leadtime = 0;
 
-       remove(this);
+       delete(this);
 }
 
 void timeout_handler_think(entity this)
@@ -210,6 +210,7 @@ void timeout_handler_think(entity this)
                        }
                        else  // time to end the timeout
                        {
+                               Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEIN);
                                timeout_status = TIMEOUT_INACTIVE;
 
                                // reset the slowmo value back to normal
@@ -364,10 +365,10 @@ void CommonCommand_editmob(int request, entity caller, int argc)
 
                                        if (arg_lower == "list") { print_to(caller, monsterlist_reply); return; }
 
-                                       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
-                                               if(it.realowner == caller)
-                                                       ++tmp_moncount;
-                                       ));
+                                       IL_EACH(g_monsters, it.realowner == caller,
+                                       {
+                                               ++tmp_moncount;
+                                       });
 
                                        if (!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; }
                                        if (autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; }
@@ -380,17 +381,17 @@ void CommonCommand_editmob(int request, entity caller, int argc)
                                        if (tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; }
 
                                        bool found = false;
-                                       for (int i = MON_FIRST; i <= MON_LAST; ++i)
+                                       FOREACH(Monsters, it != MON_Null && it.netname == arg_lower,
                                        {
-                                               mon = get_monsterinfo(i);
-                                               if (mon.netname == arg_lower) { found = true; break; }
-                                       }
+                                               found = true;
+                                               break;
+                                       });
 
                                        if (!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; }
 
                                        totalspawned += 1;
                                        WarpZone_TraceBox(CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller);
-                                       mon = spawnmonster(arg_lower, 0, caller, caller, trace_endpos, false, false, moveflag);
+                                       mon = spawnmonster(spawn(), arg_lower, 0, caller, caller, trace_endpos, false, false, moveflag);
                                        print_to(caller, strcat("Spawned ", mon.monster_name));
                                        return;
                                }
@@ -436,10 +437,12 @@ void CommonCommand_editmob(int request, entity caller, int argc)
 
                                        int tmp_remcount = 0;
 
-                                       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
+                                       IL_EACH(g_monsters, true,
+                                       {
                                                Monster_Remove(it);
                                                ++tmp_remcount;
-                                       ));
+                                       });
+                                       IL_CLEAR(g_monsters);
 
                                        monsters_total = monsters_killed = totalspawned = 0;
 
index 63b1c708f21f1bdea6a7fe441988bc2dd57e97bd..978a92b14f0c1fd4f6d5224f4a3610cf9caf67d9 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 REGISTRY(COMMON_COMMANDS, BITS(7))
 #define COMMON_COMMANDS_from(i) _COMMON_COMMANDS_from(i, NULL)
 REGISTER_REGISTRY(COMMON_COMMANDS)
@@ -19,10 +19,9 @@ STATIC_INIT(COMMON_COMMANDS_aliases) {
 }
 
 #include "vote.qh"
-#include <common/monsters/spawn.qh>
+#include <common/monsters/sv_spawn.qh>
 
-#include <common/command/generic.qh>
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 
 // ============================================================
 //  Shared declarations for server commands, written by Samual
index 93ce85a61a89b36b4c849bf2167ae4cebad3cd6f..d01448aad81ca4d022f415c42d12759eebff606a 100644 (file)
@@ -1,14 +1,15 @@
 #include "getreplies.qh"
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 #include "getreplies.qh"
 
 #include "../race.qh"
 
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/mapinfo.qh>
 #include <common/util.qh>
 
-#include <common/monsters/all.qh>
+#include <common/monsters/_mod.qh>
 
 // =========================================================
 //  Reply messages for common commands, re-worked by Samual
@@ -241,16 +242,22 @@ string getmaplist()
        return sprintf("^7Maps in list: %s\n", maplist);
 }
 
-
+const int LSMAPS_MAX = 250;
 string getlsmaps()
 {
        string lsmaps = "", col;
-       float i, newmaps = 0;
+       bool newmaps = false;
+       int added = 0;
 
-       for (i = 0; i < MapInfo_count; ++i)
+       for (int i = 0; i < MapInfo_count; ++i)
        {
                if ((MapInfo_Get_ByID(i)) && !(MapInfo_Map_flags & MapInfo_ForbiddenFlags()))
                {
+                       ++added;
+
+                       if(added > LSMAPS_MAX)
+                               continue; // we still get the added count, but skip the actual processing
+
                        // todo: Check by play count of maps for other game types?
                        if (
                            (g_race && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time"))))
@@ -270,19 +277,22 @@ string getlsmaps()
                }
        }
 
+       if(added > LSMAPS_MAX)
+               lsmaps = sprintf("%s^7(%d not listed)", lsmaps, added - LSMAPS_MAX);
+
        MapInfo_ClearTemps();
-       return sprintf("^7Maps available (%d)%s: %s\n", tokenize_console(lsmaps), (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps);
+       return sprintf("^7Maps available (%d)%s: %s\n", added, (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps);
 }
 
 string getmonsterlist()
 {
-       string monsterlist = "", col;
+       string monsterlist = "";
 
-       for (int i = MON_FIRST; i <= MON_LAST; ++i)
+       FOREACH(Monsters, it != MON_Null,
        {
-               if (i % 2) col = "^2"; else col = "^3";
-               monsterlist = sprintf("%s%s%s ", monsterlist, col, (get_monsterinfo(i)).netname);
-       }
+               string col = ((i % 2) ? "^2" : "^3");
+               monsterlist = sprintf("%s%s%s ", monsterlist, col, it.netname);
+       });
 
        return sprintf("^7Monsters available: %s\n", monsterlist);
 }
index b12977823eadf5f2466a6a5ee3d5e59fc3fcc756..ad86afb23ce84d280d1e41316814e5a3ac9e0017 100644 (file)
@@ -1,5 +1,5 @@
 #include "radarmap.qh"
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 #include "radarmap.qh"
 
 #include "../g_world.qh"
@@ -26,7 +26,7 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b)
        float n, m;
        n = m = 0;
 
-       while (vlen(c - b) > 1)
+       while (vdist(c - b, >, 1))
        {
                ++m;
 
@@ -45,22 +45,22 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b)
                c = trace_endpos;
        }
 
-       if (n > 200) LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations\n");
+       if (n > 200) LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations");
 
        return white / (black + white);
 }
-float RadarMapAtPoint_Trace(float x, float y, float w, float h, float zmin, float zsize, float q)
+float RadarMapAtPoint_Trace(float e, float f, float w, float h, float zmin, float zsize, float q)
 {
        vector a, b, mi, ma;
 
        mi = '0 0 0';
        ma = '1 0 0' * w + '0 1 0' * h;
-       a = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
-       b = '1 0 0' * x + '0 1 0' * y + '0 0 1' * (zsize + zmin);
+       a = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
+       b = '1 0 0' * e + '0 1 0' * f + '0 0 1' * (zsize + zmin);
 
        return FullTraceFraction(a, mi, ma, b);
 }
-float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, float zsize, float q)
+float RadarMapAtPoint_LineBlock(float e, float f, float w, float h, float zmin, float zsize, float q)
 {
        vector o, mi, ma;
        float i, r;
@@ -72,12 +72,12 @@ float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin,
        mi = '0 0 0';
        dz = (zsize / q) * '0 0 1';
        ma = '1 0 0' * w + '0 1 0' * h + dz;
-       o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
+       o = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
 
-       if (x < world.absmin.x - w) return 0;
-       if (y < world.absmin.y - h) return 0;
-       if (x > world.absmax.x) return 0;
-       if (y > world.absmax.y) return 0;
+       if (e < world.absmin.x - w) return 0;
+       if (f < world.absmin.y - h) return 0;
+       if (e > world.absmax.x) return 0;
+       if (f > world.absmax.y) return 0;
 
        r = 0;
        for (i = 0; i < q; ++i)
@@ -95,7 +95,7 @@ float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin,
        }
        return r / q;
 }
-float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, float zsize, float q)
+float RadarMapAtPoint_Block(float e, float f, float w, float h, float zmin, float zsize, float q)
 {
        vector o, mi, ma;
        float i, r;
@@ -107,12 +107,12 @@ float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, floa
        mi = '0 0 0';
        dz = (zsize / q) * '0 0 1';
        ma = '1 0 0' * w + '0 1 0' * h + dz;
-       o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
+       o = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
 
-       if (x < world.absmin.x - w) return 0;
-       if (y < world.absmin.y - h) return 0;
-       if (x > world.absmax.x) return 0;
-       if (y > world.absmax.y) return 0;
+       if (e < world.absmin.x - w) return 0;
+       if (f < world.absmin.y - h) return 0;
+       if (e > world.absmax.x) return 0;
+       if (f > world.absmax.y) return 0;
 
        r = 0;
        for (i = 0; i < q; ++i)
@@ -122,7 +122,7 @@ float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, floa
        }
        return r / q;
 }
-float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, float zsize, float q)
+float RadarMapAtPoint_Sample(float e, float f, float w, float h, float zmin, float zsize, float q)
 {
        vector a, b, mi, ma;
 
@@ -133,7 +133,7 @@ float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, flo
 
        mi = '0 0 0';
        ma = '1 0 0' * w + '0 1 0' * h;
-       a = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
+       a = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
        b = '1 0 0' * w + '0 1 0' * h + '0 0 1' * zsize;
 
        float c, i;
@@ -151,31 +151,31 @@ float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, flo
 
        return c / q;
 }
-void sharpen_set(int x, float v)
+void sharpen_set(int b, float v)
 {
-       sharpen_buffer[x + 2 * RADAR_WIDTH_MAX] = v;
+       sharpen_buffer[b + 2 * RADAR_WIDTH_MAX] = v;
 }
-float sharpen_getpixel(int x, int y)
+float sharpen_getpixel(int b, int c)
 {
-       if (x < 0) return 0;
-       if (x >= RADAR_WIDTH_MAX) return 0;
-       if (y < 0) return 0;
-       if (y > 2) return 0;
-       return sharpen_buffer[x + y * RADAR_WIDTH_MAX];
+       if (b < 0) return 0;
+       if (b >= RADAR_WIDTH_MAX) return 0;
+       if (c < 0) return 0;
+       if (c > 2) return 0;
+       return sharpen_buffer[b + c * RADAR_WIDTH_MAX];
 }
-float sharpen_get(float x, float a)
+float sharpen_get(float b, float a)
 {
-       float sum = sharpen_getpixel(x, 1);
+       float sum = sharpen_getpixel(b, 1);
        if (a == 0) return sum;
        sum *= (8 + 1 / a);
-       sum -= sharpen_getpixel(x - 1, 0);
-       sum -= sharpen_getpixel(x - 1, 1);
-       sum -= sharpen_getpixel(x - 1, 2);
-       sum -= sharpen_getpixel(x + 1, 0);
-       sum -= sharpen_getpixel(x + 1, 1);
-       sum -= sharpen_getpixel(x + 1, 2);
-       sum -= sharpen_getpixel(x, 0);
-       sum -= sharpen_getpixel(x, 2);
+       sum -= sharpen_getpixel(b - 1, 0);
+       sum -= sharpen_getpixel(b - 1, 1);
+       sum -= sharpen_getpixel(b - 1, 2);
+       sum -= sharpen_getpixel(b + 1, 0);
+       sum -= sharpen_getpixel(b + 1, 1);
+       sum -= sharpen_getpixel(b + 1, 2);
+       sum -= sharpen_getpixel(b, 0);
+       sum -= sharpen_getpixel(b, 2);
        return bound(0, sum * a, 1);
 }
 void sharpen_shift(int w)
@@ -207,7 +207,7 @@ void RadarMap_Next()
                localcmd(strcat("defer 1 \"sv_cmd radarmap --flags ", ftos(radarmapper.count), strcat(" --res ", ftos(radarmapper.size.x), " ", ftos(radarmapper.size.y), " --sharpen ", ftos(radarmapper.ltime), " --qual ", ftos(radarmapper.size.z)), "\"\n"));
                GotoNextMap(0);
        }
-       remove(radarmapper);
+       delete(radarmapper);
        radarmapper = NULL;
 }
 void RadarMap_Think(entity this)
@@ -253,7 +253,7 @@ void RadarMap_Think(entity this)
                if (this.cnt < 0)
                {
                        LOG_INFO("Error writing ", this.netname, "\n");
-                       remove(this);
+                       delete(this);
                        radarmapper = NULL;
                        return;
                }
@@ -439,7 +439,7 @@ float RadarMap_Make(float argc)
 
                                default:
                                        i = argc;
-                                       remove(radarmapper);
+                                       delete(radarmapper);
                                        radarmapper = NULL;
                                        break;
                        }
diff --git a/qcsrc/server/command/reg.qc b/qcsrc/server/command/reg.qc
new file mode 100644 (file)
index 0000000..c0af5b5
--- /dev/null
@@ -0,0 +1 @@
+#include "reg.qh"
diff --git a/qcsrc/server/command/reg.qh b/qcsrc/server/command/reg.qh
new file mode 100644 (file)
index 0000000..b135c04
--- /dev/null
@@ -0,0 +1,18 @@
+#pragma once
+
+REGISTRY(SERVER_COMMANDS, BITS(7))
+#define SERVER_COMMANDS_from(i) _SERVER_COMMANDS_from(i, NULL)
+REGISTER_REGISTRY(SERVER_COMMANDS)
+REGISTRY_SORT(SERVER_COMMANDS)
+
+#define SERVER_COMMAND(id, description) \
+       CLASS(servercommand_##id, Command) \
+               ATTRIB(servercommand_##id, m_name, string, #id); \
+       ATTRIB(servercommand_##id, m_description, string, description); \
+       ENDCLASS(servercommand_##id) \
+    REGISTER(SERVER_COMMANDS, CMD_SV, id, m_id, NEW(servercommand_##id)); \
+       METHOD(servercommand_##id, m_invokecmd, void(servercommand_##id this, int request, entity caller, int arguments, string command))
+
+STATIC_INIT(SERVER_COMMANDS_aliases) {
+       FOREACH(SERVER_COMMANDS, true, LAMBDA(localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_sv"))));
+}
index 895318affc8261a3e3da6f16e7be025e7443a506..55879fd35b1f51eb55a28838fd522aeee6376b59 100644 (file)
@@ -1,5 +1,5 @@
 #include "sv_cmd.qh"
-#include "all.qh"
+#include "_mod.qh"
 
 #include "banning.qh"
 #include "cmd.qh"
@@ -9,20 +9,19 @@
 
 #include "../anticheat.qh"
 #include "../campaign.qh"
-#include "../cl_client.qh"
-#include "../cl_player.qh"
+#include "../client.qh"
+#include "../player.qh"
 #include "../g_world.qh"
 #include "../ipban.qh"
 #include "../playerdemo.qh"
 #include "../teamplay.qh"
 
-#include "../bot/bot.qh"
-#include "../bot/navigation.qh"
-#include "../bot/scripting.qh"
+#include "../bot/api.qh"
 
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/mapinfo.qh>
 #include <common/notifications/all.qh>
 #include <common/teams.qh>
@@ -41,11 +40,11 @@ void PutObserverInServer(entity this);
 //  used by GameCommand_make_mapinfo()
 void make_mapinfo_Think(entity this)
 {
-       if (MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
+       if (_MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
        {
                LOG_INFO("Done rebuiling mapinfos.\n");
                MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
-               remove(this);
+               delete(this);
        }
        else
        {
@@ -141,7 +140,7 @@ void GameCommand_adminmsg(float request, float argc)
                                        }
 
                                        successful = strcat(successful, (successful ? ", " : ""), client.netname);
-                                       LOG_TRACE("Message sent to ", client.netname, "\n");
+                                       LOG_TRACE("Message sent to ", client.netname);
                                        continue;
                                }
 
@@ -227,7 +226,7 @@ void GameCommand_anticheat(float request, float argc)
 
                        if (accepted > 0)
                        {
-                               anticheat_report(client);
+                               anticheat_report_to_eventlog(client);
                                return;
                        }
                        else
@@ -384,10 +383,16 @@ void GameCommand_bot_cmd(float request, float argc, string command)
                                                }
                                                else
                                                {
-                                                       // let's start at token 2 so we can skip sv_cmd bot_cmd
-                                                       bot = find_bot_by_number(stof(argv(2)));
-                                                       if (bot == NULL) bot = find_bot_by_name(argv(2));
-                                                       if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1));
+                                                       if(argv(2) == "*" || argv(2) == "all")
+                                                               FOREACH_CLIENT(IS_BOT_CLIENT(it), {
+                                                                       bot_queuecommand(it, substring(s, argv_start_index(3), -1));
+                                                               });
+                                                       else
+                                                       {
+                                                               bot = find_bot_by_number(stof(argv(2)));
+                                                               if (bot == NULL) bot = find_bot_by_name(argv(2));
+                                                               if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1));
+                                                       }
                                                }
                                        }
                                        else
@@ -409,17 +414,31 @@ void GameCommand_bot_cmd(float request, float argc, string command)
                        }
                        else if (argc >= 3)  // this comes last
                        {
-                               bot = find_bot_by_number(stof(argv(1)));
-                               if (bot == NULL) bot = find_bot_by_name(argv(1));
-                               if (bot)
+                               if(argv(1) == "*" || argv(1) == "all")
                                {
-                                       LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n"));
-                                       bot_queuecommand(bot, substring(command, argv_start_index(2), -1));
+                                       int bot_num = 0;
+                                       FOREACH_CLIENT(IS_BOT_CLIENT(it), {
+                                               bot_queuecommand(it, substring(command, argv_start_index(2), -1));
+                                               bot_num++;
+                                       });
+                                       if(bot_num)
+                                               LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to all bots (", ftos(bot_num), ")\n"));
                                        return;
                                }
                                else
                                {
-                                       LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n"));  // don't return so that usage is shown
+                                       bot = find_bot_by_number(stof(argv(1)));
+                                       if (bot == NULL) bot = find_bot_by_name(argv(1));
+                                       if (bot)
+                                       {
+                                               LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n"));
+                                               bot_queuecommand(bot, substring(command, argv_start_index(2), -1));
+                                               return;
+                                       }
+                                       else
+                                       {
+                                               LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n"));  // don't return so that usage is shown
+                                       }
                                }
                        }
                }
@@ -429,10 +448,12 @@ void GameCommand_bot_cmd(float request, float argc, string command)
                case CMD_REQUEST_USAGE:
                {
                        LOG_INFO("\nUsage:^3 sv_cmd bot_cmd client command [argument]\n");
-                       LOG_INFO("  'client' can be either the name or entity id of the bot\n");
+                       LOG_INFO("  'client' can be either the name of the bot or a progressive number (not the entity number!)\n");
+                       LOG_INFO("           can also be '*' or 'all' to allow sending the command to all the bots\n");
                        LOG_INFO("  For full list of commands, see bot_cmd help [command].\n");
-                       LOG_INFO("Examples: sv_cmd bot_cmd client cc \"say something\"\n");
-                       LOG_INFO("          sv_cmd bot_cmd client presskey jump\n");
+                       LOG_INFO("Examples: sv_cmd bot_cmd 1 cc \"say something\"\n");
+                       LOG_INFO("          sv_cmd bot_cmd 1 presskey jump\n");
+                       LOG_INFO("          sv_cmd bot_cmd * pause\n");
                        return;
                }
        }
@@ -742,7 +763,7 @@ void GameCommand_gametype(float request, float argc)
                        if (argv(1) != "")
                        {
                                string s = argv(1);
-                               float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
+                               Gametype t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
 
                                if (t)
                                {
@@ -830,7 +851,7 @@ void GameCommand_gettaginfo(float request, float argc)
                                        LOG_INFO("bone not found\n");
                                }
 
-                               remove(tmp_entity);
+                               delete(tmp_entity);
                                return;
                        }
                }
@@ -889,7 +910,7 @@ void GameCommand_animbench(float request, float argc)
                                LOG_INFO("model ", tmp_entity.model, " frame ", ftos(f1), " animtime ", ftos(n / t1), "/s\n");
                                LOG_INFO("model ", tmp_entity.model, " frame ", ftos(f2), " animtime ", ftos(n / t2), "/s\n");
 
-                               remove(tmp_entity);
+                               delete(tmp_entity);
                                return;
                        }
                }
@@ -1329,7 +1350,7 @@ void GameCommand_shuffleteams(float request)
                {
                        if (teamplay)
                        {
-                               float x, t_teams, t_players, team_color;
+                               float t_teams, t_players, team_color;
 
                                // count the total amount of players and total amount of teams
                                t_players = 0;
@@ -1367,15 +1388,15 @@ void GameCommand_shuffleteams(float request)
                                for (int i = 1; i <= t_teams; ++i)
                                {
                                        // find out how many players to assign to this team
-                                       x = (t_players / t_teams);
-                                       x = ((i == 1) ? ceil(x) : floor(x));
+                                       int pnum = (t_players / t_teams);
+                                       pnum = ((i == 1) ? ceil(pnum) : floor(pnum));
 
                                        team_color = Team_NumberToTeam(i);
 
                                        // sort through the random list of players made earlier
                                        for (int z = 1; z <= maxclients; ++z)
                                        {
-                                               if (!(shuffleteams_teams[i] >= x))
+                                               if (!(shuffleteams_teams[i] >= pnum))
                                                {
                                                        if (!(shuffleteams_players[z])) continue;  // not a player, move on to next random slot
 
@@ -1507,15 +1528,15 @@ void GameCommand_trace(float request, float argc)
                                                start = stov(vtos(start));
                                                end = stov(vtos(end));
 
-                                               tracebox(start, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), end, MOVE_NOMONSTERS, NULL);
+                                               tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL);
                                                if (!trace_startsolid && trace_fraction < 1)
                                                {
                                                        p = trace_endpos;
-                                                       tracebox(p, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), p, MOVE_NOMONSTERS, NULL);
+                                                       tracebox(p, PL_MIN_CONST, PL_MAX_CONST, p, MOVE_NOMONSTERS, NULL);
                                                        if (trace_startsolid)
                                                        {
                                                                rint(42);  // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
-                                                               tracebox(start, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), end, MOVE_NOMONSTERS, NULL);
+                                                               tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL);
 
                                                                // how much do we need to back off?
                                                                safe = 1;
@@ -1523,7 +1544,7 @@ void GameCommand_trace(float request, float argc)
                                                                for ( ; ; )
                                                                {
                                                                        pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
-                                                                       tracebox(pos, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), pos, MOVE_NOMONSTERS, NULL);
+                                                                       tracebox(pos, PL_MIN_CONST, PL_MAX_CONST, pos, MOVE_NOMONSTERS, NULL);
                                                                        if (trace_startsolid)
                                                                        {
                                                                                if ((safe + unsafe) * 0.5 == unsafe) break;
@@ -1539,7 +1560,7 @@ void GameCommand_trace(float request, float argc)
                                                                LOG_INFO("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n");
                                                                LOG_INFO("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n");
 
-                                                               tracebox(p, STAT(PL_MIN, NULL) + '0.1 0.1 0.1', STAT(PL_MAX, NULL) - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL);
+                                                               tracebox(p, PL_MIN_CONST + '0.1 0.1 0.1', PL_MAX_CONST - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL);
                                                                if (trace_startsolid) LOG_INFO("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
                                                                else LOG_INFO("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
                                                                if (++hitcount >= 10) break;
@@ -1553,7 +1574,7 @@ void GameCommand_trace(float request, float argc)
                                                                {
                                                                        q = p + normalize(end - p) * (dq + dqf);
                                                                        if (q == q0) break;
-                                                                       tracebox(p, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), q, MOVE_NOMONSTERS, NULL);
+                                                                       tracebox(p, PL_MIN_CONST, PL_MAX_CONST, q, MOVE_NOMONSTERS, NULL);
                                                                        if (trace_startsolid) error("THIS ONE cannot happen");
                                                                        if (trace_fraction > 0) dq += dqf * trace_fraction;
                                                                        dqf *= 0.5;
index 337b428c5566defe5b16d15f2cfd4bb3310490b7..b91a8f4df3e208633e21e0d7ee40ef12fcddffc1 100644 (file)
@@ -1,5 +1,5 @@
 #include "vote.qh"
-#include <common/command/command.qh>
+#include <common/command/_mod.qh>
 #include "vote.qh"
 
 #include "common.qh"
 #include "../round_handler.qh"
 #include "../scores.qh"
 
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/mapinfo.qh>
 #include <common/notifications/all.qh>
 #include <common/playerstats.qh>
@@ -333,7 +334,13 @@ void VoteThink()
 // Resets the state of all clients, items, weapons, waypoints, ... of the map.
 void reset_map(bool dorespawn)
 {
-       if (time <= game_starttime && round_handler_IsActive()) round_handler_Reset(game_starttime);
+       if (time <= game_starttime)
+       {
+               if (gameover)
+                       return;
+               if (round_handler_IsActive())
+                       round_handler_Reset(game_starttime);
+       }
 
        MUTATOR_CALLHOOK(reset_map_global);
 
@@ -344,7 +351,7 @@ void reset_map(bool dorespawn)
                        continue;
                }
                if (it.team_saved) it.team = it.team_saved;
-               if (it.flags & FL_PROJECTILE) remove(it);  // remove any projectiles left
+               if (it.flags & FL_PROJECTILE) delete(it);  // remove any projectiles left
        });
 
        // Waypoints and assault start come LAST
@@ -378,7 +385,7 @@ void reset_map(bool dorespawn)
                                                it.velocity = '0 0 0';
                                                it.avelocity = '0 0 0';
                                                it.movement = '0 0 0';
-                                               WITHSELF(it, PutClientInServer());
+                                               PutClientInServer(it);
                                        }
                                }
                        ));
@@ -392,12 +399,15 @@ void ReadyRestart_think(entity this)
        restart_mapalreadyrestarted = true;
        reset_map(true);
        Score_ClearAll();
-       remove(this);
+       delete(this);
 }
 
 // Forces a restart of the game without actually reloading the map // this is a mess...
 void ReadyRestart_force()
 {
+       if (time <= game_starttime && gameover)
+               return;
+
        bprint("^1Server is restarting...\n");
 
        VoteReset();
@@ -454,8 +464,7 @@ void ReadyRestart_force()
 
 void ReadyRestart()
 {
-       // no assault support yet...
-       if (g_assault | gameover | intermission_running | race_completing) localcmd("restart\n");
+       if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || gameover || race_completing) localcmd("restart\n");
        else localcmd("\nsv_hook_gamerestart\n");
 
        // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
diff --git a/qcsrc/server/compat/_mod.inc b/qcsrc/server/compat/_mod.inc
new file mode 100644 (file)
index 0000000..69d031e
--- /dev/null
@@ -0,0 +1,5 @@
+// generated file; do not modify
+#include <server/compat/halflife.qc>
+#include <server/compat/quake.qc>
+#include <server/compat/quake3.qc>
+#include <server/compat/wop.qc>
diff --git a/qcsrc/server/compat/_mod.qh b/qcsrc/server/compat/_mod.qh
new file mode 100644 (file)
index 0000000..bd65858
--- /dev/null
@@ -0,0 +1,5 @@
+// generated file; do not modify
+#include <server/compat/halflife.qh>
+#include <server/compat/quake.qh>
+#include <server/compat/quake3.qh>
+#include <server/compat/wop.qh>
diff --git a/qcsrc/server/compat/halflife.qc b/qcsrc/server/compat/halflife.qc
new file mode 100644 (file)
index 0000000..74e5013
--- /dev/null
@@ -0,0 +1,35 @@
+#include "halflife.qh"
+.float  roomtype;
+.float  radius;
+.float  pitch;
+.float  renderamt;
+.float  rendermode;
+.vector rendercolor;
+
+spawnfunc(weapon_crossbow) {}
+spawnfunc(weapon_handgrenade) {}
+spawnfunc(ammo_crossbow) {}
+spawnfunc(ammo_9mmclip) {}
+spawnfunc(ammo_gaussclip) {}
+spawnfunc(weapon_rpg) {}
+spawnfunc(weapon_357) {}
+void ammo_ARgrenades() {}
+spawnfunc(item_battery) {}
+spawnfunc(ammo_rpgclip) {}
+void weapon_9mmAR() {}
+spawnfunc(weapon_tripmine) {}
+spawnfunc(weapon_snark) {}
+spawnfunc(ammo_buckshot) {}
+void ammo_9mmAR() {}
+spawnfunc(ammo_357) {}
+spawnfunc(weapon_gauss) {}
+spawnfunc(weapon_hornetgun) {}
+//spawnfunc(weapon_shotgun) {}
+spawnfunc(item_healthkit) {}
+spawnfunc(item_longjump) {}
+spawnfunc(item_antidote) {}
+spawnfunc(func_recharge) {}
+spawnfunc(info_node) {}
+spawnfunc(env_sound) {}
+spawnfunc(light_spot) {}
+spawnfunc(func_healthcharger) {}
diff --git a/qcsrc/server/compat/halflife.qh b/qcsrc/server/compat/halflife.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/compat/quake.qc b/qcsrc/server/compat/quake.qc
new file mode 100644 (file)
index 0000000..539042d
--- /dev/null
@@ -0,0 +1,30 @@
+#include "quake.qh"
+
+#include <common/weapons/_all.qh>
+
+spawnfunc(weapon_electro);
+spawnfunc(weapon_hagar);
+spawnfunc(weapon_machinegun);
+spawnfunc(item_bullets);
+spawnfunc(item_armor_mega);
+spawnfunc(item_health_mega);
+spawnfunc(item_health_medium);
+
+//***********************
+//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons
+//***********************
+spawnfunc(weapon_nailgun) {spawnfunc_weapon_electro(this);}
+spawnfunc(weapon_supernailgun) {spawnfunc_weapon_hagar(this);}
+spawnfunc(weapon_supershotgun) {spawnfunc_weapon_machinegun(this);}
+
+spawnfunc(item_spikes) {spawnfunc_item_bullets(this);}
+//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);}  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
+spawnfunc(item_armor2) {spawnfunc_item_armor_mega(this);}
+spawnfunc(item_armorInv) {spawnfunc_item_armor_mega(this);} // TODO: make sure we actually want this
+spawnfunc(item_health) {if (this.spawnflags & 2) spawnfunc_item_health_mega(this);else spawnfunc_item_health_medium(this);}
+
+//spawnfunc_item_spikes
+//spawnfunc_item_health
+
+
+
diff --git a/qcsrc/server/compat/quake.qh b/qcsrc/server/compat/quake.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc
new file mode 100644 (file)
index 0000000..1c53de3
--- /dev/null
@@ -0,0 +1,208 @@
+#include "quake3.qh"
+
+#include <common/weapons/_all.qh>
+
+spawnfunc(weapon_crylink);
+spawnfunc(weapon_electro);
+spawnfunc(weapon_hagar);
+spawnfunc(weapon_machinegun);
+spawnfunc(weapon_vortex);
+
+spawnfunc(target_items);
+
+spawnfunc(item_bullets);
+spawnfunc(item_cells);
+spawnfunc(item_rockets);
+spawnfunc(item_shells);
+
+spawnfunc(item_jetpack);
+
+spawnfunc(item_armor_big);
+spawnfunc(item_armor_mega);
+spawnfunc(item_armor_small);
+
+spawnfunc(item_health_medium);
+spawnfunc(item_health_mega);
+
+//***********************
+//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons
+//***********************
+
+// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG
+
+// SG -> SG
+spawnfunc(ammo_shells)         { spawnfunc_item_shells(this);         }
+
+// MG -> MG
+spawnfunc(ammo_bullets)        { spawnfunc_item_bullets(this);        }
+
+// GL -> Mortar
+spawnfunc(ammo_grenades)       { spawnfunc_item_rockets(this);        }
+
+// LG -> Lightning
+spawnfunc(weapon_lightning)    { spawnfunc_weapon_electro(this);      }
+spawnfunc(ammo_lightning)      { spawnfunc_item_cells(this);          }
+
+// Plasma -> Hagar
+spawnfunc(weapon_plasmagun)    { spawnfunc_weapon_hagar(this);        }
+spawnfunc(ammo_cells)          { spawnfunc_item_rockets(this);        }
+
+// Rail -> Vortex
+spawnfunc(weapon_railgun)      { spawnfunc_weapon_vortex(this);          }
+spawnfunc(ammo_slugs)          { spawnfunc_item_cells(this);          }
+
+// BFG -> Crylink
+spawnfunc(weapon_bfg)          { spawnfunc_weapon_crylink(this);      }
+spawnfunc(ammo_bfg)            { spawnfunc_item_cells(this);          }
+
+// RL -> RL
+spawnfunc(ammo_rockets)        { spawnfunc_item_rockets(this);        }
+
+// Armor
+spawnfunc(item_armor_body)     { spawnfunc_item_armor_mega(this);    }
+spawnfunc(item_armor_combat)   { spawnfunc_item_armor_big(this);      }
+spawnfunc(item_armor_shard)    { spawnfunc_item_armor_small(this);    }
+spawnfunc(item_enviro)         { spawnfunc_item_invincible(this);     }
+
+.float wait;
+.float delay;
+
+// weapon remove ent from df
+void target_init_verify(entity this)
+{
+       entity trigger, targ;
+       for(trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); )
+               for(targ = NULL; (targ = find(targ, targetname, trigger.target)); )
+                       if (targ.classname == "target_init" || targ.classname == "target_give" || targ.classname == "target_items")
+                       {
+                               trigger.wait = 0;
+                               trigger.delay = 0;
+                               targ.wait = 0;
+                               targ.delay = 0;
+
+                               //setsize(targ, trigger.mins, trigger.maxs);
+                               //setorigin(targ, trigger.origin);
+                               //remove(trigger);
+                       }
+}
+
+spawnfunc(target_init)
+{
+       this.spawnflags = 0; // remove all weapons except the ones listed below
+       this.netname = "shotgun"; // keep these weapons through the remove trigger
+       spawnfunc_target_items(this);
+       InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET);
+}
+
+// weapon give ent from defrag
+void target_give_init(entity this)
+{
+       IL_EACH(g_items, it.targetname == this.target,
+       {
+               if (it.classname == "weapon_rocketlauncher" || it.classname == "weapon_devastator") {
+                       this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo);
+                       this.netname = "devastator";
+               }
+               else if (it.classname == "weapon_plasmagun") {
+                       this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
+                       if(this.netname == "")
+                               this.netname = "hagar";
+                       else
+                               this.netname = strcat(this.netname, " hagar");
+               }
+               else if (it.classname == "weapon_bfg") {
+                       this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo);
+                       if(this.netname == "")
+                               this.netname = "crylink";
+                       else
+                               this.netname = strcat(this.netname, " crylink");
+               }
+               else if (it.classname == "weapon_grenadelauncher" || it.classname == "weapon_mortar") {
+                       this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO
+                       if(this.netname == "")
+                               this.netname = "mortar";
+                       else
+                               this.netname = strcat(this.netname, " mortar");
+               }
+               else if (it.classname == "item_armor_body")
+                       this.armorvalue = 100;
+               else if (it.classname == "item_health_mega")
+                       this.health = 200;
+               //remove(it); // removing ents in init functions causes havoc, workaround:
+        setthink(it, SUB_Remove);
+        it.nextthink = time;
+       });
+       this.spawnflags = 2;
+       spawnfunc_target_items(this);
+       InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET);
+}
+
+spawnfunc(target_give)
+{
+       InitializeEntity(this, target_give_init, INITPRIO_FINDTARGET);
+}
+
+//spawnfunc(item_flight)       /* handled by jetpack */
+//spawnfunc(item_haste)        /* handled by buffs mutator */
+//spawnfunc(item_health)       /* handled in t_quake.qc */
+//spawnfunc(item_health_large) /* handled in t_items.qc */
+//spawnfunc(item_health_small) /* handled in t_items.qc */
+//spawnfunc(item_health_mega)  /* handled in t_items.qc */
+//spawnfunc(item_invis)        /* handled by buffs mutator */
+//spawnfunc(item_regen)        /* handled by buffs mutator */
+
+// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
+
+spawnfunc(item_flight)
+{
+       spawnfunc_item_jetpack(this);
+}
+
+.float notteam;
+.float notsingle;
+.float notfree;
+.float notq3a;
+.float notta;
+.string gametype;
+bool DoesQ3ARemoveThisEntity(entity this)
+{
+       // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY)
+
+       if(this.notq3a)
+               if(!teamplay || g_tdm || g_ctf)
+                       return true;
+
+       if(this.notta)
+               if (!(!teamplay || g_tdm || g_ctf))
+                       return true;
+
+       if(this.notsingle)
+               if(maxclients == 1)
+                       return true;
+
+       if(this.notteam)
+               if(teamplay)
+                       return true;
+
+       if(this.notfree)
+               if(!teamplay)
+                       return true;
+
+       if(this.gametype)
+       {
+               string gametypename;
+               // static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"}
+               gametypename = "ffa";
+               if(teamplay)
+                       gametypename = "team";
+               if(g_ctf)
+                       gametypename = "ctf";
+               if(maxclients == 1)
+                       gametypename = "single";
+               // we do not have the other types (oneflag, obelisk, harvester, teamtournament)
+               if(strstrofs(this.gametype, gametypename, 0) < 0)
+                       return true;
+       }
+
+       return false;
+}
diff --git a/qcsrc/server/compat/quake3.qh b/qcsrc/server/compat/quake3.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/compat/wop.qc b/qcsrc/server/compat/wop.qc
new file mode 100644 (file)
index 0000000..6d53e18
--- /dev/null
@@ -0,0 +1,62 @@
+#include "wop.qh"
+
+#include <common/weapons/_all.qh>
+// #include <server/mutators/gamemode.qh>
+
+spawnfunc(weapon_arc);
+spawnfunc(weapon_crylink);
+spawnfunc(weapon_electro);
+spawnfunc(weapon_mortar);
+spawnfunc(weapon_hagar);
+spawnfunc(weapon_machinegun);
+spawnfunc(weapon_devastator);
+spawnfunc(weapon_shotgun);
+spawnfunc(weapon_vortex);
+
+spawnfunc(item_armor_big);
+spawnfunc(item_armor_mega);
+spawnfunc(item_armor_small);
+
+spawnfunc(item_bullets);
+spawnfunc(item_cells);
+spawnfunc(item_quad);
+spawnfunc(item_rockets);
+spawnfunc(item_shells);
+
+spawnfunc(item_jetpack);
+
+spawnfunc(item_haste);
+spawnfunc(item_health_medium);
+spawnfunc(item_health_mega);
+spawnfunc(item_invis);
+spawnfunc(item_medic);
+
+//***********************
+//WORD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
+//***********************
+
+spawnfunc(weapon_punchy)       { spawnfunc_weapon_arc(this);                   }
+spawnfunc(weapon_nipper)       { spawnfunc_weapon_machinegun(this);    }
+spawnfunc(weapon_pumper)       { spawnfunc_weapon_shotgun(this);               }
+spawnfunc(weapon_boaster)      { spawnfunc_weapon_electro(this);               }
+spawnfunc(weapon_splasher)     { spawnfunc_weapon_vortex(this);                }
+spawnfunc(weapon_bubbleg)      { spawnfunc_weapon_hagar(this);                 }
+spawnfunc(weapon_balloony)     { spawnfunc_weapon_mortar(this);                }
+spawnfunc(weapon_betty)                { spawnfunc_weapon_devastator(this);    }
+spawnfunc(weapon_imperius)     { spawnfunc_weapon_crylink(this);               }
+
+spawnfunc(ammo_pumper)         { spawnfunc_item_shells(this);                  }
+spawnfunc(ammo_nipper)         { spawnfunc_item_bullets(this);                 }
+spawnfunc(ammo_balloony)       { spawnfunc_item_rockets(this);                 }
+spawnfunc(ammo_bubbleg)                { spawnfunc_item_rockets(this);                 }
+spawnfunc(ammo_boaster)                { spawnfunc_item_cells(this);                   }
+spawnfunc(ammo_betty)          { spawnfunc_item_rockets(this);                 }
+spawnfunc(ammo_imperius)       { spawnfunc_item_cells(this);                   }
+
+spawnfunc(item_padpower)       { spawnfunc_item_quad(this);                    }
+spawnfunc(item_climber)                { spawnfunc_item_invincible(this);              }
+spawnfunc(item_speedy)         { spawnfunc_item_haste(this);                   }
+spawnfunc(item_jump)           { spawnfunc_item_jetpack(this);                 }
+spawnfunc(item_visionless)     { spawnfunc_item_invis(this);                   }
+spawnfunc(item_revival)                { spawnfunc_item_medic(this);                   }
+spawnfunc(item_armor_padshield)        { spawnfunc_item_armor_mega(this);      }
diff --git a/qcsrc/server/compat/wop.qh b/qcsrc/server/compat/wop.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 8eea0dc203d189b4e4007dfbe8f51a23ea63ba27..fedd5eb1ab346354f6121da51fa247627a5de039 100644 (file)
@@ -1,7 +1,7 @@
 #pragma once
 
 float warmup_limit;
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/stats.qh>
 
 #define INDEPENDENT_ATTACK_FINISHED 1
@@ -12,20 +12,6 @@ float g_footsteps, g_grappling_hook, g_instagib;
 float g_warmup_allguns;
 float g_warmup_allow_timeout;
 float warmup_stage;
-PROPERTY(float, g_pickup_respawntime_weapon)
-PROPERTY(float, g_pickup_respawntime_superweapon)
-PROPERTY(float, g_pickup_respawntime_ammo)
-PROPERTY(float, g_pickup_respawntime_short)
-PROPERTY(float, g_pickup_respawntime_medium)
-PROPERTY(float, g_pickup_respawntime_long)
-PROPERTY(float, g_pickup_respawntime_powerup)
-PROPERTY(float, g_pickup_respawntimejitter_weapon)
-PROPERTY(float, g_pickup_respawntimejitter_superweapon)
-PROPERTY(float, g_pickup_respawntimejitter_ammo)
-PROPERTY(float, g_pickup_respawntimejitter_short)
-PROPERTY(float, g_pickup_respawntimejitter_medium)
-PROPERTY(float, g_pickup_respawntimejitter_long)
-PROPERTY(float, g_pickup_respawntimejitter_powerup)
 float g_jetpack;
 
 float sv_clones;
@@ -35,7 +21,7 @@ float player_count;
 float currentbots;
 float bots_would_leave;
 
-void UpdateFrags(entity player, float f);
+void UpdateFrags(entity player, int f);
 .float totalfrags;
 
 float team1_score, team2_score, team3_score, team4_score;
@@ -143,7 +129,6 @@ const int W_TICSPERFRAME = 2;
 
 void weapon_defaultspawnfunc(entity this, Weapon e);
 
-float gameover;
 float intermission_running;
 float intermission_exittime;
 float alreadychangedlevel;
@@ -174,7 +159,6 @@ bool nJoinAllowed(entity this, entity ignore);
 .float noalign;                // if set to 1, the item or spawnpoint won't be dropped to the floor
 
 .vector death_origin;
-.vector killer_origin;
 
 float default_player_alpha;
 float default_weapon_alpha;
@@ -253,6 +237,8 @@ float lockteams;
 .float parm_idlesince;
 float sv_maxidle;
 float sv_maxidle_spectatorsareidle;
+int sv_maxidle_slots;
+bool sv_maxidle_slots_countbots;
 
 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end);
 
@@ -273,7 +259,7 @@ bool independent_players;
 
 string clientstuff;
 .float phase;
-.int pressedkeys = _STAT(PRESSED_KEYS);
+.int pressedkeys;
 
 .string fog;
 
@@ -393,7 +379,6 @@ const float ACTIVE_TOGGLE   = 3;
 .float player_blocked;
 .float weapon_blocked; // weapon use disabled
 
-.float frozen = _STAT(FROZEN); // for freeze attacks
 .float revive_progress = _STAT(REVIVE_PROGRESS);
 .float revival_time; // time at which player was last revived
 .float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
@@ -429,16 +414,56 @@ const int MIF_GUIDED_CONFUSABLE = MIF_GUIDED_HEAT | MIF_GUIDED_AI;
 #define MISSILE_IS_GUIDED(m) ((m.missile_flags & MIF_GUIDED_ALL) ? true : false)
 #define MISSILE_IS_TRACKING(m) ((m.missile_flags & MIF_GUIDED_TRACKING) ? true : false)
 
-
 ////
 
-.entity player_stats;
-//.float playerid;
-.string playernick;
-.float elos;
-.float ranks;
-
 .string cvar_cl_physics;
 
-.bool init_for_player_needed;
 .void(entity this, entity player) init_for_player;
+
+IntrusiveList g_monsters;
+STATIC_INIT(g_monsters) { g_monsters = IL_NEW(); }
+
+IntrusiveList g_waypoints;
+STATIC_INIT(g_waypoints) { g_waypoints = IL_NEW(); }
+
+IntrusiveList g_vehicles;
+STATIC_INIT(g_vehicles) { g_vehicles = IL_NEW(); }
+
+IntrusiveList g_turrets;
+STATIC_INIT(g_turrets) { g_turrets = IL_NEW(); }
+
+IntrusiveList g_mines;
+STATIC_INIT(g_mines) { g_mines = IL_NEW(); }
+
+IntrusiveList g_projectiles;
+STATIC_INIT(g_projectiles) { g_projectiles = IL_NEW(); }
+
+IntrusiveList g_items;
+STATIC_INIT(g_items) { g_items = IL_NEW(); }
+
+IntrusiveList g_initforplayer;
+STATIC_INIT(g_initforplayer) { g_initforplayer = IL_NEW(); }
+
+IntrusiveList g_clones;
+STATIC_INIT(g_clones) { g_clones = IL_NEW(); }
+
+IntrusiveList g_assault_destructibles;
+STATIC_INIT(g_assault_destructibles) { g_assault_destructibles = IL_NEW(); }
+
+IntrusiveList g_assault_objectivedecreasers;
+STATIC_INIT(g_assault_objectivedecreasers) { g_assault_objectivedecreasers = IL_NEW(); }
+
+IntrusiveList g_assault_objectives;
+STATIC_INIT(g_assault_objectives) { g_assault_objectives = IL_NEW(); }
+
+IntrusiveList g_spawnpoints;
+STATIC_INIT(g_spawnpoints) { g_spawnpoints = IL_NEW(); }
+
+IntrusiveList g_bot_targets;
+STATIC_INIT(g_bot_targets) { g_bot_targets = IL_NEW(); }
+
+IntrusiveList g_bot_dodge;
+STATIC_INIT(g_bot_dodge) { g_bot_dodge = IL_NEW(); }
+
+IntrusiveList g_damagedbycontents;
+STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); }
index 4fbccc7b02c160b736b09cf82718ea338acdd1b7..a604a2bade1ef61b8ac45bb1eeac7d068ed3b406 100644 (file)
@@ -1,15 +1,15 @@
 #include "g_damage.qh"
 
-#include "bot/bot.qh"
+#include "bot/api.qh"
 #include "g_hook.qh"
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 #include "scores.qh"
 #include "spawnpoints.qh"
 #include "../common/state.qh"
 #include "../common/physics/player.qh"
 #include "../common/t_items.qh"
 #include "../common/vehicles/all.qh"
-#include "../common/items/all.qc"
+#include "../common/items/_mod.qh"
 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
 #include "weapons/accuracy.qh"
 #include "weapons/csqcprojectile.qh"
 #include "../common/playerstats.qh"
 #include "../common/teams.qh"
 #include "../common/util.qh"
-#include "../common/weapons/all.qh"
+#include <common/weapons/_all.qh>
 #include "../lib/csqcmodel/sv_model.qh"
 #include "../lib/warpzone/common.qh"
 
-void UpdateFrags(entity player, float f)
+void UpdateFrags(entity player, int f)
 {
        PlayerTeamScore_AddScore(player, f);
 }
@@ -111,6 +111,8 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype)
                UpdateFrags(attacker, f);
 }
 
+.entity kh_next;
+
 string AppendItemcodes(string s, entity player)
 {
        int w = PS(player).m_weapon.m_id;
@@ -262,6 +264,18 @@ float Obituary_WeaponDeath(
        return false;
 }
 
+bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target)
+{
+       if(deathtype == DEATH_FIRE.m_id)
+       {
+               Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : targ.ping));
+               Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker.netname, kill_count_to_target, attacker.health, attacker.armorvalue, (IS_BOT_CLIENT(attacker) ? -1 : attacker.ping));
+               return true;
+       }
+
+       return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target);
+}
+
 .int buffs = _STAT(BUFFS); // TODO: remove
 entity buff_FirstFromFlags(int _buffs);
 void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
@@ -275,7 +289,6 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
 
        // Set final information for the death
        targ.death_origin = targ.origin;
-       if(targ != attacker) { targ.killer_origin = attacker.origin; }
        string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
 
        #ifdef NOTIFICATIONS_DEBUG
@@ -411,7 +424,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
                                        (IS_BOT_CLIENT(attacker) ? -1 : attacker.ping)
                                );
                        }
-                       else
+                       else if(!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target))
                        {
                                Send_Notification(
                                        NOTIF_ONE,
@@ -503,7 +516,7 @@ void Ice_Think(entity this)
 {
        if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this)
        {
-               remove(this);
+               delete(this);
                return;
        }
        setorigin(this, this.owner.origin - '0 0 16');
@@ -524,6 +537,8 @@ void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypo
        targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
        targ.health = ((frozen_type == 3) ? targ_maxhealth : 1);
        targ.revive_speed = freeze_time;
+       if(targ.bot_attack)
+               IL_REMOVE(g_bot_targets, targ);
        targ.bot_attack = false;
 
        entity ice = new(ice);
@@ -556,11 +571,16 @@ void Unfreeze (entity targ)
                return;
 
        if(STAT(FROZEN, targ) && STAT(FROZEN, targ) != 3) // only reset health if target was frozen
+       {
                targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health);
+               targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
+       }
 
        STAT(FROZEN, targ) = 0;
        targ.revive_progress = 0;
        targ.revival_time = time;
+       if(!targ.bot_attack)
+               IL_PUSH(g_bot_targets, targ);
        targ.bot_attack = true;
 
        WaypointSprite_Kill(targ.waypointsprite_attached);
@@ -569,7 +589,7 @@ void Unfreeze (entity targ)
 
        // remove the ice block
        if(targ.iceblock)
-               remove(targ.iceblock);
+               delete(targ.iceblock);
        targ.iceblock = NULL;
 }
 
@@ -788,7 +808,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d
                        else
                                victim = targ;
 
-                       if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim))
+                       if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
                        {
                                if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim))
                                {
@@ -833,11 +853,11 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d
 
        // apply push
        if (targ.damageforcescale)
-       if (vlen(force))
+       if (force)
        if (!IS_PLAYER(targ) || time >= targ.spawnshieldtime || targ == attacker)
        {
                vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
-               if(targ.movetype == MOVETYPE_PHYSICS)
+               if(targ.move_movetype == MOVETYPE_PHYSICS)
                {
                        entity farcent = new(farce);
                        farcent.enemy = targ;
@@ -852,14 +872,12 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d
                else
                {
                        targ.velocity = targ.velocity + farce;
-                       targ.move_velocity = targ.velocity;
                }
                UNSET_ONGROUND(targ);
-               targ.move_flags &= ~FL_ONGROUND;
                UpdateCSQCProjectile(targ);
        }
        // apply damage
-       if (damage != 0 || (targ.damageforcescale && vlen(force)))
+       if (damage != 0 || (targ.damageforcescale && force))
        if (targ.event_damage)
                targ.event_damage (targ, inflictor, attacker, damage, deathtype, hitloc, force);
 
@@ -1034,7 +1052,7 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in
                                        //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
                                        //      print(" (", ftos(a), ")\n");
                                        //}
-                                       if(finaldmg || vlen(force))
+                                       if(finaldmg || force)
                                        {
                                                if(targ.iscreature)
                                                {
@@ -1253,14 +1271,14 @@ void fireburner_think(entity this)
        // for players, this is done in the regular loop
        if(wasfreed(this.owner))
        {
-               remove(this);
+               delete(this);
                return;
        }
        Fire_ApplyEffect(this.owner);
        if(!Fire_IsBurning(this.owner))
        {
                this.owner.fire_burner = NULL;
-               remove(this);
+               delete(this);
                return;
        }
        Fire_ApplyDamage(this.owner);
index e2c5ea2b35d7a3f665df6a222cb9535e599c1c98..019c8fc9a8efc92bc5d280f2352d796acd99f574 100644 (file)
@@ -7,7 +7,7 @@
     #include <common/constants.qh>
     #include <common/teams.qh>
     #include <common/util.qh>
-    #include <common/weapons/all.qh>
+    #include <common/weapons/_all.qh>
     #include "weapons/accuracy.qh"
     #include "weapons/csqcprojectile.qh"
     #include "weapons/selection.qh"
@@ -17,7 +17,7 @@
     #include "defs.qh"
     #include <common/notifications/all.qh>
     #include <common/deathtypes/all.qh>
-    #include "mutators/all.qh"
+    #include "mutators/_mod.qh"
     #include <common/turrets/sv_turrets.qh>
     #include <common/vehicles/all.qh>
     #include <lib/csqcmodel/sv_model.qh>
@@ -52,7 +52,7 @@ float damage_gooddamage;
 
 float IsFlying(entity a);
 
-void UpdateFrags(entity player, float f);
+void UpdateFrags(entity player, int f);
 
 // NOTE: f=0 means still count as a (positive) kill, but count no frags for it
 void W_SwitchWeapon_Force(Player this, Weapon w);
index b22a81e0427d6145fa238a11cc385fb3f2eab1ce..cccba42bb42128fff96050498f317875c050c360 100644 (file)
@@ -5,7 +5,7 @@
 #include "weapons/weaponsystem.qh"
 #include "weapons/selection.qh"
 #include "weapons/tracing.qh"
-#include "cl_player.qh"
+#include "player.qh"
 #include "command/common.qh"
 #include "round_handler.qh"
 #include "../common/state.qh"
@@ -13,7 +13,8 @@
 #include "../common/vehicles/all.qh"
 #include "../common/constants.qh"
 #include "../common/util.qh"
-#include "../common/weapons/all.qh"
+#include <common/net_linked.qh>
+#include <common/weapons/_all.qh>
 #include "../lib/warpzone/common.qh"
 #include "../lib/warpzone/server.qh"
 
@@ -74,10 +75,10 @@ void RemoveGrapplingHook(entity pl)
 {
        if(pl.hook == NULL)
                return;
-       remove(pl.hook);
+       delete(pl.hook);
        pl.hook = NULL;
-       if(pl.movetype == MOVETYPE_FLY)
-               pl.movetype = MOVETYPE_WALK;
+       if(pl.move_movetype == MOVETYPE_FLY)
+               set_movetype(pl, MOVETYPE_WALK);
 
        //pl.disableclientprediction = false;
 }
@@ -87,7 +88,7 @@ void GrapplingHookReset(entity this)
        if(this.realowner.hook == this)
                RemoveGrapplingHook(this.owner);
        else // in any case:
-               remove(this);
+               delete(this);
 }
 
 void GrapplingHookThink(entity this);
@@ -101,7 +102,7 @@ void GrapplingHook_Stop(entity this)
        this.nextthink = time;
        settouch(this, func_null);
        this.velocity = '0 0 0';
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.hook_length = -1;
 }
 
@@ -143,7 +144,7 @@ void GrapplingHookThink(entity this)
                error("Owner lost the hook!\n");
                return;
        }
-       if(LostMovetypeFollow(this) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
+       if(LostMovetypeFollow(this) || gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
        {
                RemoveGrapplingHook(this.realowner);
                return;
@@ -220,8 +221,8 @@ void GrapplingHookThink(entity this)
                                this.hook_length = newlength;
                        }
 
-                       if(pull_entity.movetype == MOVETYPE_FLY)
-                               pull_entity.movetype = MOVETYPE_WALK;
+                       if(pull_entity.move_movetype == MOVETYPE_FLY)
+                               set_movetype(pull_entity, MOVETYPE_WALK);
 
                        if(this.realowner.hook_state & HOOK_RELEASING)
                        {
@@ -238,7 +239,7 @@ void GrapplingHookThink(entity this)
                                dv = ((v - v0) * dir) * dir;
                                if(tarzan >= 2)
                                {
-                                       if(this.aiment.movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
+                                       if(this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
                                        {
                                                entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
                                                v = v - dv * 0.5;
@@ -280,7 +281,7 @@ void GrapplingHookThink(entity this)
                        if(spd < 50)
                                spd = 0;
                        this.realowner.velocity = dir*spd;
-                       this.realowner.movetype = MOVETYPE_FLY;
+                       set_movetype(this.realowner, MOVETYPE_FLY);
 
                        UNSET_ONGROUND(this.realowner);
                }
@@ -301,18 +302,18 @@ void GrapplingHookThink(entity this)
        }
 }
 
-void GrapplingHookTouch (entity this)
+void GrapplingHookTouch(entity this, entity toucher)
 {
-       if(other.movetype == MOVETYPE_FOLLOW)
+       if(toucher.move_movetype == MOVETYPE_FOLLOW)
                return;
-       PROJECTILE_TOUCH(this);
+       PROJECTILE_TOUCH(this, toucher);
 
        GrapplingHook_Stop(this);
 
-       if(other)
-               if(other.movetype != MOVETYPE_NONE)
+       if(toucher)
+               if(toucher.move_movetype != MOVETYPE_NONE)
                {
-                       SetMovetypeFollow(this, other);
+                       SetMovetypeFollow(this, toucher);
                        WarpZone_RefSys_BeginAddingIncrementally(this, this.aiment);
                }
 
@@ -370,8 +371,10 @@ void FireGrapplingHook(entity actor)
        missile.reset = GrapplingHookReset;
        missile.classname = "grapplinghook";
        missile.flags = FL_PROJECTILE;
+       IL_PUSH(g_projectiles, missile);
+       IL_PUSH(g_bot_dodge, missile);
 
-       missile.movetype = ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY);
+       set_movetype(missile, ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY));
        PROJECTILE_MAKETRIGGER(missile);
 
        //setmodel (missile, MDL_HOOK); // precision set below
@@ -396,6 +399,8 @@ void FireGrapplingHook(entity actor)
        missile.takedamage = DAMAGE_AIM;
        missile.damageforcescale = 0;
        missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
+       if(missile.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, missile);
 
        missile.hook_start = missile.hook_end = missile.origin;
 
index 41366f5ea2a5f1c9a84d9e386774740a774f261e..425716d2da7cb92dc3001d2383947b7a4eab86dd 100644 (file)
@@ -37,7 +37,7 @@ flags:
 void dynlight_think(entity this)
 {
        if(!this.owner)
-               remove(this);
+               delete(this);
 
        this.nextthink = time + 0.1;
 }
@@ -48,7 +48,7 @@ void dynlight_find_aiment(entity this)
                objerror (this, "dynlight: no target to follow");
 
        targ = find(NULL, targetname, this.target);
-       this.movetype = MOVETYPE_FOLLOW;
+       set_movetype(this, MOVETYPE_FOLLOW);
        this.aiment = targ;
        this.owner = targ;
        this.punchangle = targ.angles;
@@ -123,7 +123,7 @@ spawnfunc(dynlight)
        if (this.target)
 //     if (!(this.spawnflags & DFOLLOW))
        {
-               this.movetype = MOVETYPE_NOCLIP;
+               set_movetype(this, MOVETYPE_NOCLIP);
                if (!this.speed)
                        this.speed = 100;
                InitializeEntity(this, dynlight_find_path, INITPRIO_FINDTARGET);
index 38e72c7329977c77e6d022c33908f5cc1742c5fd..3ca062b78430e338f003d53f4f80b3496dbe42cb 100644 (file)
@@ -1,6 +1,7 @@
 #include "g_models.qh"
 
 #include "g_subs.qh"
+#include <common/net_linked.qh>
 #include "../common/triggers/subs.qh"
 #include "../common/triggers/triggers.qh"
 
@@ -152,11 +153,11 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf)
                        WriteCoord(MSG_ENTITY, this.movedir.z);
                        WriteByte(MSG_ENTITY, floor(this.lip * 255));
                }
-               WriteShort(MSG_ENTITY, this.fade_start);
-               WriteShort(MSG_ENTITY, this.fade_end);
-               WriteShort(MSG_ENTITY, this.alpha_max);
-               WriteShort(MSG_ENTITY, this.alpha_min);
-               WriteShort(MSG_ENTITY, this.inactive);
+               WriteByte(MSG_ENTITY, this.fade_start);
+               WriteByte(MSG_ENTITY, this.fade_end);
+               WriteByte(MSG_ENTITY, this.alpha_max);
+               WriteByte(MSG_ENTITY, this.alpha_min);
+               WriteByte(MSG_ENTITY, this.inactive);
                WriteShort(MSG_ENTITY, this.fade_vertical_offset);
        }
 
@@ -165,7 +166,7 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf)
 
 
 #define G_MODEL_INIT(ent,sol) \
-       if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) ent.movetype = MOVETYPE_PHYSICS; \
+       if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) set_movetype(ent, MOVETYPE_PHYSICS); \
        if(!ent.scale) ent.scale = ent.modelscale; \
        SetBrushEntityModel(ent); \
        ent.use = g_model_setcolormaptoactivator; \
@@ -173,7 +174,7 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf)
        if(!ent.solid) ent.solid = (sol); else if(ent.solid < 0) ent.solid = SOLID_NOT;
 
 #define G_CLIENTMODEL_INIT(ent,sol) \
-       if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) ent.movetype = MOVETYPE_PHYSICS; \
+       if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) set_movetype(ent, MOVETYPE_PHYSICS); \
        if(!ent.scale) ent.scale = ent.modelscale; \
        SetBrushEntityModel(ent); \
        ent.use = g_clientmodel_use; \
index 1b12174b990bd4f73869ab59323169bcab30cc60..e8e50c7184134148406c439b092c96e2b5161137 100644 (file)
@@ -8,7 +8,7 @@
 
 spawnfunc(info_null)
 {
-       remove(this);
+       delete(this);
        // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
 }
 
@@ -51,9 +51,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
        {
                // take players back into the past
                FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag));
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
-                       if(it != forent)
-                               antilag_takeback(it, it, time - lag);
+               IL_EACH(g_monsters, it != forent,
+               {
+                       antilag_takeback(it, it, time - lag);
                });
        }
 
@@ -67,9 +67,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
        if (lag)
        {
                FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it)));
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
-                       if (it != forent)
-                               antilag_restore(it, it);
+               IL_EACH(g_monsters, it != forent,
+               {
+                       antilag_restore(it, it);
                });
        }
 
@@ -141,10 +141,10 @@ float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomon
 
                if(c == 50)
                {
-                       LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
-                       LOG_TRACE("  Nudging gets us nowhere at ", vtos(pos), "\n");
-                       LOG_TRACE("  trace_endpos is ", vtos(trace_endpos), "\n");
-                       LOG_TRACE("  trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
+                       LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2));
+                       LOG_TRACE("  Nudging gets us nowhere at ", vtos(pos));
+                       LOG_TRACE("  trace_endpos is ", vtos(trace_endpos));
+                       LOG_TRACE("  trace distance is ", ftos(vlen(pos - trace_endpos)));
                }
 
                stopentity = trace_ent;
@@ -234,7 +234,7 @@ vector findbetterlocation (vector org, float mindist)
        return org;
 }
 
-float LOD_customize(entity this)
+bool LOD_customize(entity this, entity client)
 {
        if(autocvar_loddebug)
        {
@@ -249,10 +249,10 @@ float LOD_customize(entity this)
        }
 
        // TODO csqc network this so it only gets sent once
-       vector near_point = NearestPointOnBox(this, other.origin);
-       if(vdist(near_point - other.origin, <, this.loddistance1))
+       vector near_point = NearestPointOnBox(this, client.origin);
+       if(vdist(near_point - client.origin, <, this.loddistance1))
                this.modelindex = this.lodmodelindex0;
-       else if(!this.lodmodelindex2 || vdist(near_point - other.origin, <, this.loddistance2))
+       else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2))
                this.modelindex = this.lodmodelindex1;
        else
                this.modelindex = this.lodmodelindex2;
@@ -281,7 +281,7 @@ void LODmodel_attach(entity this)
                if(e)
                {
                        this.lodmodel1 = e.model;
-                       remove(e);
+                       delete(e);
                }
        }
        if(this.lodtarget2 != "")
@@ -290,7 +290,7 @@ void LODmodel_attach(entity this)
                if(e)
                {
                        this.lodmodel2 = e.model;
-                       remove(e);
+                       delete(e);
                }
        }
 
@@ -417,7 +417,7 @@ void InitTrigger(entity this)
        SetMovedir(this);
        this.solid = SOLID_TRIGGER;
        SetBrushEntityModel(this);
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.modelindex = 0;
        this.model = "";
 }
@@ -429,7 +429,7 @@ void InitSolidBSPTrigger(entity this)
        SetMovedir(this);
        this.solid = SOLID_BSP;
        SetBrushEntityModel(this);
-       this.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
+       set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0
 //     this.modelindex = 0;
        this.model = "";
 }
@@ -440,7 +440,7 @@ bool InitMovingBrushTrigger(entity this)
 // to mean no restrictions, so use a yaw of 360 instead.
        this.solid = SOLID_BSP;
        SetBrushEntityModel(this);
-       this.movetype = MOVETYPE_PUSH;
+       set_movetype(this, MOVETYPE_PUSH);
        if(this.modelindex == 0)
        {
                objerror(this, "InitMovingBrushTrigger: no brushes found!");
index 4f9fc471f55609698c5bb490be56fcd7a1741edb..1f5537cea52f92ffa5e7e1f3284edd20cadcea04 100644 (file)
@@ -138,7 +138,7 @@ float angc (float a1, float a2);
 .float loddistance1;
 .float loddistance2;
 
-float LOD_customize(entity this);
+bool LOD_customize(entity this, entity client);
 
 void LOD_uncustomize(entity this);
 
index 56bc0b34d8c6ddb5b910258f8e77dbd8976682a4..4ca9ef77c8afb16b1b5fab0190bc1cbd1770e8f5 100644 (file)
@@ -2,10 +2,10 @@
 
 #include "anticheat.qh"
 #include "antilag.qh"
-#include "bot/bot.qh"
+#include "bot/api.qh"
 #include "campaign.qh"
 #include "cheats.qh"
-#include "cl_client.qh"
+#include "client.qh"
 #include "command/common.qh"
 #include "command/getreplies.qh"
 #include "command/sv_cmd.qh"
 #include "g_hook.qh"
 #include "ipban.qh"
 #include "mapvoting.qh"
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 #include "race.qh"
 #include "scores.qh"
 #include "teamplay.qh"
 #include "weapons/weaponstats.qh"
 #include "../common/constants.qh"
+#include <common/net_linked.qh>
 #include "../common/deathtypes/all.qh"
 #include "../common/mapinfo.qh"
-#include "../common/monsters/all.qh"
+#include "../common/monsters/_mod.qh"
 #include "../common/monsters/sv_monsters.qh"
 #include "../common/vehicles/all.qh"
 #include "../common/notifications/all.qh"
@@ -32,8 +33,8 @@
 #include "../common/triggers/trigger/secret.qh"
 #include "../common/triggers/target/music.qh"
 #include "../common/util.qh"
-#include "../common/items/all.qh"
-#include "../common/weapons/all.qh"
+#include "../common/items/_mod.qh"
+#include <common/weapons/_all.qh>
 #include "../common/state.qh"
 
 const float LATENCY_THINKRATE = 10;
@@ -56,9 +57,9 @@ void PingPLReport_Think(entity this)
        {
                WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
                WriteByte(MSG_BROADCAST, this.cnt);
-               WriteShort(MSG_BROADCAST, max(1, e.ping));
-               WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255));
-               WriteByte(MSG_BROADCAST, ceil(e.ping_movementloss * 255));
+               WriteShort(MSG_BROADCAST, bound(1, e.ping, 65535));
+               WriteByte(MSG_BROADCAST, min(ceil(e.ping_packetloss * 255), 255));
+               WriteByte(MSG_BROADCAST, min(ceil(e.ping_movementloss * 255), 255));
 
                // record latency times for clients throughout the match so we can report it to playerstats
                if(time > (e.latency_time + LATENCY_THINKRATE))
@@ -320,17 +321,20 @@ void cvar_changes_init()
                BADCVAR("g_invasion_point_limit");
                BADCVAR("g_keyhunt_point_leadlimit");
                BADCVAR("g_nexball_goalleadlimit");
+               BADCVAR("g_new_toys_use_pickupsound");
+               BADCVAR("g_physics_predictall");
+               BADCVAR("g_piggyback");
                BADCVAR("g_tdm_point_leadlimit");
                BADCVAR("g_tdm_point_limit");
                BADCVAR("leadlimit_and_fraglimit");
                BADCVAR("leadlimit_override");
                BADCVAR("pausable");
-               BADCVAR("sv_allow_fullbright");
                BADCVAR("sv_checkforpacketsduringsleep");
                BADCVAR("sv_intermission_cdtrack");
                BADCVAR("sv_minigames");
                BADCVAR("sv_namechangetimer");
                BADCVAR("sv_precacheplayermodels");
+               BADCVAR("sv_stepheight");
                BADCVAR("sv_timeout");
                BADPREFIX("crypto_");
                BADPREFIX("gameversion_");
@@ -342,9 +346,11 @@ void cvar_changes_init()
                BADPREFIX("net_");
                BADPREFIX("prvm_");
                BADPREFIX("skill_");
+               BADPREFIX("sv_allow_");
                BADPREFIX("sv_cullentities_");
                BADPREFIX("sv_maxidle_");
                BADPREFIX("sv_minigames_");
+               BADPREFIX("sv_radio_");
                BADPREFIX("sv_timeout_");
                BADPREFIX("sv_vote_");
                BADPREFIX("timelimit_");
@@ -369,6 +375,7 @@ void cvar_changes_init()
                BADCVAR("g_balance_teams_scorefactor");
                BADCVAR("g_ban_sync_trusted_servers");
                BADCVAR("g_ban_sync_uri");
+               BADCVAR("g_buffs");
                BADCVAR("g_ca_teams_override");
                BADCVAR("g_ctf_ignore_frags");
                BADCVAR("g_domination_point_limit");
@@ -391,6 +398,8 @@ void cvar_changes_init()
                BADCVAR("g_maxplayers");
                BADCVAR("g_mirrordamage");
                BADCVAR("g_nexball_goallimit");
+               BADCVAR("g_norecoil");
+               BADCVAR("g_physics_clientselect");
                BADCVAR("g_powerups");
                BADCVAR("g_spawnshieldtime");
                BADCVAR("g_start_delay");
@@ -443,6 +452,7 @@ void cvar_changes_init()
                BADCVAR("g_grappling_hook");
                BADCVAR("g_jetpack");
 
+#undef BADPRESUFFIX
 #undef BADPREFIX
 #undef BADCVAR
 
@@ -540,7 +550,7 @@ spawnfunc(__init_dedicated_server)
        cvar_string = cvar_string_normal;
        cvar_set = cvar_set_normal;
 
-       remove = remove_unsafely;
+       delete_fn = remove_unsafely;
 
        entity e = spawn();
        setthink(e, GotoFirstMap);
@@ -555,6 +565,8 @@ spawnfunc(__init_dedicated_server)
        static_init_late();
        static_init_precache();
 
+       IL_PUSH(g_spawnpoints, e); // just incase
+
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
 }
@@ -646,9 +658,6 @@ spawnfunc(worldspawn)
                }
        }
 
-       float fd, l;
-       string s;
-
        cvar = cvar_normal;
        cvar_string = cvar_string_normal;
        cvar_set = cvar_set_normal;
@@ -657,12 +666,10 @@ spawnfunc(worldspawn)
                error("world already spawned - you may have EXACTLY ONE worldspawn!");
        world_already_spawned = true;
 
-       remove = remove_safely; // during spawning, watch what you remove!
+       delete_fn = remove_safely; // during spawning, watch what you remove!
 
        cvar_changes_init(); // do this very early now so it REALLY matches the server config
 
-       compressShortVector_init();
-
        maxclients = 0;
        for (entity head = nextent(NULL); head; head = nextent(head))
        {
@@ -753,7 +760,7 @@ spawnfunc(worldspawn)
        // character set: ASCII 33-126 without the following characters: : ; ' " \ $
        if(autocvar_sv_eventlog)
        {
-               s = sprintf("%d.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
+               string s = sprintf("%d.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
                matchid = strzone(s);
 
                GameLogEcho(strcat(":gamestart:", GetGametype(), "_", GetMapname(), ":", s));
@@ -806,12 +813,13 @@ spawnfunc(worldspawn)
 
        if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
        {
-               fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
+               int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
                if(fd != -1)
                {
+                       string s;
                        while((s = fgets(fd)))
                        {
-                               l = tokenize_console(s);
+                               int l = tokenize_console(s);
                                if(l < 2)
                                        continue;
                                if(argv(0) == "cd")
@@ -853,7 +861,7 @@ spawnfunc(worldspawn)
        monsterlist_reply = strzone(getmonsterlist());
        for(int i = 0; i < 10; ++i)
        {
-               s = getrecords(i);
+               string s = getrecords(i);
                if (s)
                        records_reply[i] = strzone(s);
        }
@@ -872,7 +880,7 @@ spawnfunc(worldspawn)
        // fill sv_curl_serverpackages from .serverpackage files
        if (autocvar_sv_curl_serverpackages_auto)
        {
-               s = "csprogs-" WATERMARK ".txt";
+               string s = "csprogs-" WATERMARK ".txt";
                // remove automatically managed files from the list to prevent duplicates
                for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
                {
@@ -884,7 +892,7 @@ spawnfunc(worldspawn)
                }
                // add automatically managed files to the list
                #define X(match) MACRO_BEGIN { \
-                       fd = search_begin(match, true, false); \
+                       int fd = search_begin(match, true, false); \
                        if (fd >= 0) \
                        { \
                                for (int i = 0, j = search_getsize(fd); i < j; ++i) \
@@ -910,7 +918,8 @@ spawnfunc(worldspawn)
        if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
                modname = cvar_string("g_mod_config");
        // extra mutators that deserve to count as mod
-       MUTATOR_CALLHOOK(SetModname);
+       MUTATOR_CALLHOOK(SetModname, modname);
+       modname = M_ARGV(0, string);
 
        // save it for later
        modname = strzone(modname);
@@ -923,7 +932,7 @@ spawnfunc(worldspawn)
 spawnfunc(light)
 {
        //makestatic (this); // Who the f___ did that?
-       remove(this);
+       delete(this);
 }
 
 string GetGametype()
@@ -970,10 +979,10 @@ float MapHasRightSize(string map)
                LOG_TRACE("checkwp "); LOG_TRACE(map);
                if(!fexists(strcat("maps/", map, ".waypoints")))
                {
-                       LOG_TRACE(": no waypoints\n");
+                       LOG_TRACE(": no waypoints");
                        return false;
                }
-               LOG_TRACE(": has waypoints\n");
+               LOG_TRACE(": has waypoints");
        }
 
        // open map size restriction file
@@ -988,18 +997,18 @@ float MapHasRightSize(string map)
                fclose(fh);
                if(player_count < mapmin)
                {
-                       LOG_TRACE("not enough\n");
+                       LOG_TRACE("not enough");
                        return false;
                }
                if(player_count > mapmax)
                {
-                       LOG_TRACE("too many\n");
+                       LOG_TRACE("too many");
                        return false;
                }
-               LOG_TRACE("right size\n");
+               LOG_TRACE("right size");
                return true;
        }
-       LOG_TRACE(": not found\n");
+       LOG_TRACE(": not found");
        return true;
 }
 
@@ -1038,7 +1047,7 @@ float Map_Check(float position, float pass)
                return 0;
        }
        else
-               LOG_TRACE( "Couldn't select '", filename, "'..\n" );
+               LOG_DEBUG( "Couldn't select '", filename, "'..." );
 
        return 0;
 }
@@ -1071,7 +1080,7 @@ float() MaplistMethod_Iterate = // usual method
 {
        float pass, i;
 
-       LOG_TRACE("Trying MaplistMethod_Iterate\n");
+       LOG_TRACE("Trying MaplistMethod_Iterate");
 
        for(pass = 1; pass <= 2; ++pass)
        {
@@ -1088,7 +1097,7 @@ float() MaplistMethod_Iterate = // usual method
 
 float() MaplistMethod_Repeat = // fallback method
 {
-       LOG_TRACE("Trying MaplistMethod_Repeat\n");
+       LOG_TRACE("Trying MaplistMethod_Repeat");
 
        if(Map_Check(Map_Current, 2))
                return Map_Current;
@@ -1099,7 +1108,7 @@ float() MaplistMethod_Random = // random map selection
 {
        float i, imax;
 
-       LOG_TRACE("Trying MaplistMethod_Random\n");
+       LOG_TRACE("Trying MaplistMethod_Random");
 
        imax = 42;
 
@@ -1119,7 +1128,7 @@ float(float exponent) MaplistMethod_Shuffle = // more clever shuffling
 {
        float i, j, imax, insertpos;
 
-       LOG_TRACE("Trying MaplistMethod_Shuffle\n");
+       LOG_TRACE("Trying MaplistMethod_Shuffle");
 
        imax = 42;
 
@@ -1131,7 +1140,7 @@ float(float exponent) MaplistMethod_Shuffle = // more clever shuffling
                insertpos = pow(random(), 1 / exponent);       // ]0, 1]
                insertpos = insertpos * (Map_Count - 1);       // ]0, Map_Count - 1]
                insertpos = ceil(insertpos) + 1;               // {2, 3, 4, ..., Map_Count}
-               LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos), "\n");
+               LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos));
 
                // insert the current map there
                newlist = "";
@@ -1335,32 +1344,32 @@ entity FindIntermission()
        local   float cyc;
 
 // look for info_intermission first
-       spot = find (NULL, classname, "info_intermission");
+       spot = find(NULL, classname, "info_intermission");
        if (spot)
        {       // pick a random one
                cyc = random() * 4;
                while (cyc > 1)
                {
-                       spot = find (spot, classname, "info_intermission");
+                       spot = find(spot, classname, "info_intermission");
                        if (!spot)
-                               spot = find (spot, classname, "info_intermission");
+                               spot = find(spot, classname, "info_intermission");
                        cyc = cyc - 1;
                }
                return spot;
        }
 
 // then look for the start position
-       spot = find (NULL, classname, "info_player_start");
+       spot = find(NULL, classname, "info_player_start");
        if (spot)
                return spot;
 
 // testinfo_player_start is only found in regioned levels
-       spot = find (NULL, classname, "testplayerstart");
+       spot = find(NULL, classname, "testplayerstart");
        if (spot)
                return spot;
 
 // then look for the start position
-       spot = find (NULL, classname, "info_player_deathmatch");
+       spot = find(NULL, classname, "info_player_deathmatch");
        if (spot)
                return spot;
 
@@ -1486,9 +1495,6 @@ void FixIntermissionClient(entity e)
                e.autoscreenshot = time + 0.8;  // used for autoscreenshot
                e.health = -2342;
                // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not)
-               e.solid = SOLID_NOT;
-               e.movetype = MOVETYPE_NONE;
-               e.takedamage = DAMAGE_NO;
                for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
                    .entity weaponentity = weaponentities[slot];
@@ -1504,7 +1510,7 @@ void FixIntermissionClient(entity e)
                        stuffcmd(e, "\nscr_printspeed 1000000\n");
                        RandomSelection_Init();
                        FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, LAMBDA(
-                               RandomSelection_Add(NULL, 0, it, 1, 1);
+                               RandomSelection_AddString(it, 1, 1);
                        ));
                        if (RandomSelection_chosen_string != "")
                        {
@@ -1526,7 +1532,7 @@ void NextLevel()
 
        intermission_running = 1;
 
-// enforce a wait time before allowing changelevel
+       // enforce a wait time before allowing changelevel
        if(player_count > 0)
                intermission_exittime = time + autocvar_sv_mapchange_delay;
        else
@@ -1771,7 +1777,8 @@ float WinningCondition_RanOutOfSpawns()
                }
        ));
 
-       FOREACH_ENTITY_CLASS("info_player_deathmatch", true, LAMBDA(
+       IL_EACH(g_spawnpoints, true,
+       {
                switch(it.team)
                {
                        case NUM_TEAM_1: team1_score = 1; break;
@@ -1779,7 +1786,7 @@ float WinningCondition_RanOutOfSpawns()
                        case NUM_TEAM_3: team3_score = 1; break;
                        case NUM_TEAM_4: team4_score = 1; break;
                }
-       ));
+       });
 
        ClearWinners();
        if(team1_score + team2_score + team3_score + team4_score == 0)
@@ -1832,7 +1839,7 @@ void CheckRules_World()
 
        SetDefaultAlpha();
 
-       if (gameover)   // someone else quit the game already
+       if (intermission_running) // someone else quit the game already
        {
                if(player_count == 0) // Nobody there? Then let's go to the next map
                        MapVote_Start();
@@ -1983,11 +1990,80 @@ string GotoMap(string m)
                return "Map switch will happen after scoreboard.";
 }
 
+bool autocvar_sv_gameplayfix_multiplethinksperframe;
+void RunThink(entity this)
+{
+       // don't let things stay in the past.
+       // it is possible to start that way by a trigger with a local time.
+       if(this.nextthink <= 0 || this.nextthink > time + frametime)
+               return;
+
+       float oldtime = time; // do we need to save this?
+
+       for (int iterations = 0; iterations < 128 && !wasfreed(this); iterations++)
+       {
+               time = max(oldtime, this.nextthink);
+               this.nextthink = 0;
+
+               if(getthink(this))
+                       getthink(this)(this);
+               // mods often set nextthink to time to cause a think every frame,
+               // we don't want to loop in that case, so exit if the new nextthink is
+               // <= the time the qc was told, also exit if it is past the end of the
+               // frame
+               if(this.nextthink <= time || this.nextthink > oldtime + frametime || !autocvar_sv_gameplayfix_multiplethinksperframe)
+                       break;
+       }
+
+       time = oldtime;
+}
+
+bool autocvar_sv_freezenonclients;
+bool autocvar_sv_gameplayfix_delayprojectiles;
+void Physics_Frame()
+{
+       if(autocvar_sv_freezenonclients)
+               return;
+
+       FOREACH_ENTITY_FLOAT(pure_data, false,
+       {
+               if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH || it.move_movetype == MOVETYPE_PHYSICS)
+                       continue;
+
+               set_movetype(it, it.move_movetype);
+
+               if(it.move_movetype == MOVETYPE_NONE)
+                       continue;
+
+               if(it.move_qcphysics)
+                       Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
+
+               if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
+               {
+                       // handle thinking here
+                       if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime)
+                               RunThink(it);
+               }
+       });
+
+       if(autocvar_sv_gameplayfix_delayprojectiles >= 0)
+               return;
+
+       FOREACH_ENTITY_FLOAT(move_qcphysics, true,
+       {
+               if(IS_CLIENT(it) || is_pure(it) || it.classname == "" || it.move_movetype == MOVETYPE_NONE)
+                       continue;
+               Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
+       });
+}
 
+void systems_update();
 void EndFrame()
 {
        anticheat_endframe();
 
+       Physics_Frame();
+
        FOREACH_CLIENT(IS_REAL_CLIENT(it), {
                entity e = IS_SPEC(it) ? it.enemy : it;
                if (e.typehitsound) {
@@ -2008,13 +2084,16 @@ void EndFrame()
                it.damage_dealt = 0;
                antilag_record(it, CS(it), altime);
        });
-       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
+       IL_EACH(g_monsters, true,
+       {
                antilag_record(it, it, altime);
        });
        FOREACH_CLIENT(PS(it), {
                PlayerState s = PS(it);
                s.ps_push(s, it);
        });
+       systems_update();
+       IL_ENDFRAME();
 }
 
 
@@ -2090,7 +2169,7 @@ void Shutdown()
        if(world_initialized > 0)
        {
                world_initialized = 0;
-               LOG_TRACE("Saving persistent data...\n");
+               LOG_TRACE("Saving persistent data...");
                Ban_SaveBans();
 
                // playerstats with unfinished match
@@ -2113,7 +2192,7 @@ void Shutdown()
                CheatShutdown(); // must be after cheatcount check
                db_close(ServerProgsDB);
                db_close(TemporaryDB);
-               LOG_TRACE("Saving persistent data... done!\n");
+               LOG_TRACE("Saving persistent data... done!");
                // tell the bot system the game is ending now
                bot_endgame();
 
diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc
new file mode 100644 (file)
index 0000000..9b36010
--- /dev/null
@@ -0,0 +1,606 @@
+#include "impulse.qh"
+#include "round_handler.qh"
+
+#include "bot/api.qh"
+
+#include "weapons/throwing.qh"
+#include "command/common.qh"
+#include "cheats.qh"
+#include "weapons/selection.qh"
+#include "weapons/tracing.qh"
+#include "weapons/weaponsystem.qh"
+
+#include <common/state.qh>
+
+#include "../common/minigames/sv_minigames.qh"
+
+#include <common/weapons/_all.qh>
+#include "../common/vehicles/sv_vehicles.qh"
+
+#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
+
+.entity vehicle;
+
+#define IMPULSE(id) _IMPULSE(IMP_##id)
+#define _IMPULSE(id) \
+       void id##_handle(entity this); \
+       STATIC_INIT_LATE(id) \
+       { \
+               id.impulse_handle = id##_handle; \
+       } \
+       void id##_handle(entity this)
+
+/**
+ * Impulse map:
+ *
+ * 0 reserved (no input)
+ *
+ * 99: loaded
+ *
+ * 140: moving clone
+ * 141: ctf speedrun
+ * 142: fixed clone
+ * 143: emergency teleport
+ * 148: unfairly eliminate
+ *
+ * TODO:
+ * 200 to 209: prev weapon shortcuts
+ * 210 to 219: best weapon shortcuts
+ * 220 to 229: next weapon shortcuts
+ * 230 to 253: individual weapons (up to 24)
+ */
+
+// weapon switching impulses
+
+#define X(slot) \
+       IMPULSE(weapon_group_##slot) \
+       { \
+               if (IS_DEAD(this)) \
+               { \
+                       this.impulse = IMP_weapon_group_##slot.impulse; \
+                       return; \
+               } \
+               W_NextWeaponOnImpulse(this, slot); \
+       }
+X(1)
+X(2)
+X(3)
+X(4)
+X(5)
+X(6)
+X(7)
+X(8)
+X(9)
+X(0)
+#undef X
+
+// custom order weapon cycling
+
+#define X(slot, dir) \
+       IMPULSE(weapon_priority_##slot##_##dir) \
+       { \
+               if (this.vehicle) return; \
+               if (IS_DEAD(this)) \
+               { \
+                       this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
+                       return; \
+               } \
+               noref int prev = -1; \
+               noref int best =  0; \
+               noref int next = +1; \
+               W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \
+       }
+X(0, prev)
+X(1, prev)
+X(2, prev)
+X(3, prev)
+X(4, prev)
+X(5, prev)
+X(6, prev)
+X(7, prev)
+X(8, prev)
+X(9, prev)
+
+X(0, best)
+X(1, best)
+X(2, best)
+X(3, best)
+X(4, best)
+X(5, best)
+X(6, best)
+X(7, best)
+X(8, best)
+X(9, best)
+
+X(0, next)
+X(1, next)
+X(2, next)
+X(3, next)
+X(4, next)
+X(5, next)
+X(6, next)
+X(7, next)
+X(8, next)
+X(9, next)
+#undef X
+
+// direct weapons
+
+#define X(i) \
+       IMPULSE(weapon_byid_##i) \
+       { \
+               if (this.vehicle) return; \
+               if (IS_DEAD(this)) \
+               { \
+                       this.impulse = IMP_weapon_byid_##i.impulse; \
+                       return; \
+               } \
+               W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \
+       }
+X(0)
+X(1)
+X(2)
+X(3)
+X(4)
+X(5)
+X(6)
+X(7)
+X(8)
+X(9)
+X(10)
+X(11)
+X(12)
+X(13)
+X(14)
+X(15)
+X(16)
+X(17)
+X(18)
+X(19)
+X(20)
+X(21)
+X(22)
+X(23)
+#undef X
+
+IMPULSE(weapon_next_byid)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this))
+       {
+               this.impulse = IMP_weapon_next_byid.impulse;
+               return;
+       }
+       W_NextWeapon(this, 0);
+}
+
+IMPULSE(weapon_prev_byid)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this))
+       {
+               this.impulse = IMP_weapon_prev_byid.impulse;
+               return;
+       }
+       W_PreviousWeapon(this, 0);
+}
+
+IMPULSE(weapon_next_bygroup)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this))
+       {
+               this.impulse = IMP_weapon_next_bygroup.impulse;
+               return;
+       }
+       W_NextWeapon(this, 1);
+}
+
+IMPULSE(weapon_prev_bygroup)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this))
+       {
+               this.impulse = IMP_weapon_prev_bygroup.impulse;
+               return;
+       }
+       W_PreviousWeapon(this, 1);
+}
+
+IMPULSE(weapon_next_bypriority)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this))
+       {
+               this.impulse = IMP_weapon_next_bypriority.impulse;
+               return;
+       }
+       W_NextWeapon(this, 2);
+}
+
+IMPULSE(weapon_prev_bypriority)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this))
+       {
+               this.impulse = IMP_weapon_prev_bypriority.impulse;
+               return;
+       }
+       W_PreviousWeapon(this, 2);
+}
+
+IMPULSE(weapon_last)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this)) return;
+       W_LastWeapon(this);
+}
+
+IMPULSE(weapon_best)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this)) return;
+       W_SwitchWeapon(this, w_getbestweapon(this));
+}
+
+IMPULSE(weapon_drop)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this)) return;
+       W_ThrowWeapon(this, weaponentities[0], W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
+}
+
+IMPULSE(weapon_reload)
+{
+       if (this.vehicle) return;
+       if (IS_DEAD(this)) return;
+       if (forbidWeaponUse(this)) return;
+       Weapon w = PS(this).m_weapon;
+       entity actor = this;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               w.wr_reload(w, actor, weaponentity);
+       }
+}
+
+void ImpulseCommands(entity this)
+{
+       if (gameover) return;
+
+       int imp = this.impulse;
+       if (!imp) return;
+       this.impulse = 0;
+
+       if (MinigameImpulse(this, imp)) return;
+
+       if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
+
+       // allow only weapon change impulses when not in round time
+       if (round_handler_IsActive() && !round_handler_IsRoundStarted())
+       {
+               #define X(id) case IMP_##id.impulse:
+               switch (imp)
+               {
+                       X(weapon_group_0)
+                       X(weapon_group_1)
+                       X(weapon_group_2)
+                       X(weapon_group_3)
+                       X(weapon_group_4)
+                       X(weapon_group_5)
+                       X(weapon_group_6)
+                       X(weapon_group_7)
+                       X(weapon_group_8)
+                       X(weapon_group_9)
+                       X(weapon_next_byid)
+                       X(weapon_prev_byid)
+                       X(weapon_next_bygroup)
+                       X(weapon_prev_bygroup)
+                       X(weapon_next_bypriority)
+                       X(weapon_prev_bypriority)
+                       X(weapon_last)
+                       X(weapon_best)
+                       X(weapon_reload)
+                       X(weapon_priority_0_prev)
+            X(weapon_priority_1_prev)
+            X(weapon_priority_2_prev)
+            X(weapon_priority_3_prev)
+            X(weapon_priority_4_prev)
+            X(weapon_priority_5_prev)
+            X(weapon_priority_6_prev)
+            X(weapon_priority_7_prev)
+            X(weapon_priority_8_prev)
+            X(weapon_priority_9_prev)
+            X(weapon_priority_0_next)
+                       X(weapon_priority_1_next)
+                       X(weapon_priority_2_next)
+                       X(weapon_priority_3_next)
+                       X(weapon_priority_4_next)
+                       X(weapon_priority_5_next)
+                       X(weapon_priority_6_next)
+                       X(weapon_priority_7_next)
+                       X(weapon_priority_8_next)
+                       X(weapon_priority_9_next)
+                       X(weapon_priority_0_best)
+            X(weapon_priority_1_best)
+            X(weapon_priority_2_best)
+            X(weapon_priority_3_best)
+            X(weapon_priority_4_best)
+            X(weapon_priority_5_best)
+            X(weapon_priority_6_best)
+            X(weapon_priority_7_best)
+            X(weapon_priority_8_best)
+            X(weapon_priority_9_best)
+            X(weapon_byid_0)
+            X(weapon_byid_1)
+            X(weapon_byid_2)
+            X(weapon_byid_3)
+            X(weapon_byid_4)
+            X(weapon_byid_5)
+            X(weapon_byid_6)
+            X(weapon_byid_7)
+            X(weapon_byid_8)
+            X(weapon_byid_9)
+            X(weapon_byid_10)
+            X(weapon_byid_11)
+            X(weapon_byid_12)
+            X(weapon_byid_13)
+            X(weapon_byid_14)
+            X(weapon_byid_15)
+            X(weapon_byid_16)
+            X(weapon_byid_17)
+            X(weapon_byid_18)
+            X(weapon_byid_19)
+            X(weapon_byid_20)
+            X(weapon_byid_21)
+            X(weapon_byid_22)
+            X(weapon_byid_23)
+                       break;
+                       default: return;
+               }
+#undef X
+       }
+
+       if (vehicle_impulse(this, imp)) return;
+
+       if (CheatImpulse(this, imp)) return;
+
+       FOREACH(IMPULSES, it.impulse == imp, {
+               void(entity) f = it.impulse_handle;
+               if (!f) continue;
+               f(this);
+               return;
+       });
+}
+
+IMPULSE(use)
+{
+       PlayerUseKey(this);
+}
+
+IMPULSE(waypoint_personal_here)
+{
+       entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "personal waypoint spawned at location\n");
+}
+
+IMPULSE(waypoint_personal_crosshair)
+{
+       WarpZone_crosshair_trace(this);
+       entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "personal waypoint spawned at crosshair\n");
+}
+
+IMPULSE(waypoint_personal_death)
+{
+       if (!this.death_origin) return;
+       entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "personal waypoint spawned at death location\n");
+}
+
+IMPULSE(waypoint_here_follow)
+{
+       if (!teamplay) return;
+       if (IS_DEAD(this)) return;
+       if (!MUTATOR_CALLHOOK(HelpMePing, this))
+       {
+               entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
+               if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
+               else WaypointSprite_Ping(wp);
+       }
+       sprint(this, "HELP ME attached\n");
+}
+
+IMPULSE(waypoint_here_here)
+{
+       entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "HERE spawned at location\n");
+}
+
+IMPULSE(waypoint_here_crosshair)
+{
+       WarpZone_crosshair_trace(this);
+       entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "HERE spawned at crosshair\n");
+}
+
+IMPULSE(waypoint_here_death)
+{
+       if (!this.death_origin) return;
+       entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "HERE spawned at death location\n");
+}
+
+IMPULSE(waypoint_danger_here)
+{
+       entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "DANGER spawned at location\n");
+}
+
+IMPULSE(waypoint_danger_crosshair)
+{
+       WarpZone_crosshair_trace(this);
+       entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "DANGER spawned at crosshair\n");
+}
+
+IMPULSE(waypoint_danger_death)
+{
+       if (!this.death_origin) return;
+       entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
+       if (wp) WaypointSprite_Ping(wp);
+       sprint(this, "DANGER spawned at death location\n");
+}
+
+IMPULSE(waypoint_clear_personal)
+{
+       WaypointSprite_ClearPersonal(this);
+       if (this.personal)
+       {
+               delete(this.personal);
+               this.personal = NULL;
+       }
+       sprint(this, "personal waypoint cleared\n");
+}
+
+IMPULSE(waypoint_clear)
+{
+       WaypointSprite_ClearOwned(this);
+       if (this.personal)
+       {
+               delete(this.personal);
+               this.personal = NULL;
+       }
+       sprint(this, "all waypoints cleared\n");
+}
+
+IMPULSE(navwaypoint_spawn)
+{
+       if (!autocvar_g_waypointeditor) return;
+       waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
+       bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
+}
+
+IMPULSE(navwaypoint_remove)
+{
+       if (!autocvar_g_waypointeditor) return;
+       entity e = navigation_findnearestwaypoint(this, false);
+       if (!e) return;
+       if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
+       bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
+       waypoint_remove(e);
+}
+
+IMPULSE(navwaypoint_relink)
+{
+       if (!autocvar_g_waypointeditor) return;
+       waypoint_schedulerelinkall();
+}
+
+IMPULSE(navwaypoint_save)
+{
+       if (!autocvar_g_waypointeditor) return;
+       waypoint_saveall();
+}
+
+IMPULSE(navwaypoint_unreachable)
+{
+       if (!autocvar_g_waypointeditor) return;
+       IL_EACH(g_waypoints, true,
+       {
+               it.colormod = '0.5 0.5 0.5';
+               it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
+       });
+       entity e2 = navigation_findnearestwaypoint(this, false);
+       navigation_markroutes(this, e2);
+
+       int j, m;
+
+       j = 0;
+       m = 0;
+       IL_EACH(g_waypoints, it.wpcost >= 10000000,
+       {
+               LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
+               it.colormod_z = 8;
+               it.effects |= EF_NODEPTHTEST | EF_BLUE;
+               ++j;
+               ++m;
+       });
+       if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
+       navigation_markroutes_inverted(e2);
+
+       j = 0;
+       IL_EACH(g_waypoints, it.wpcost >= 10000000,
+       {
+               LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
+               it.colormod_x = 8;
+               if (!(it.effects & EF_NODEPTHTEST))  // not already reported before
+                       ++m;
+               it.effects |= EF_NODEPTHTEST | EF_RED;
+               ++j;
+       });
+       if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
+       if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
+
+       j = 0;
+       IL_EACH(g_spawnpoints, true,
+       {
+               vector org = it.origin;
+               tracebox(it.origin, PL_MIN_CONST, PL_MAX_CONST, it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
+               setorigin(it, trace_endpos);
+               if (navigation_findnearestwaypoint(it, false))
+               {
+                       setorigin(it, org);
+                       it.effects &= ~EF_NODEPTHTEST;
+                       it.model = "";
+               }
+               else
+               {
+                       setorigin(it, org);
+                       LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
+                       it.effects |= EF_NODEPTHTEST;
+                       _setmodel(it, this.model);
+                       it.frame = this.frame;
+                       it.skin = this.skin;
+                       it.colormod = '8 0.5 8';
+                       setsize(it, '0 0 0', '0 0 0');
+                       ++j;
+               }
+       });
+       if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
+
+       j = 0;
+       IL_EACH(g_items, true,
+       {
+               it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
+               it.colormod = '0.5 0.5 0.5';
+       });
+       IL_EACH(g_items, true,
+       {
+               if (navigation_findnearestwaypoint(it, false)) continue;
+               LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
+               it.effects |= EF_NODEPTHTEST | EF_RED;
+               it.colormod_x = 8;
+               ++j;
+       });
+       if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
+
+       j = 0;
+       IL_EACH(g_items, true,
+       {
+               if (navigation_findnearestwaypoint(it, true)) continue;
+               LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
+               it.effects |= EF_NODEPTHTEST | EF_BLUE;
+               it.colormod_z = 8;
+               ++j;
+       });
+       if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);
+}
diff --git a/qcsrc/server/impulse.qh b/qcsrc/server/impulse.qh
new file mode 100644 (file)
index 0000000..50edc2c
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void ImpulseCommands(entity this);
index 8cc37597938b17bbba95d75f972935366c2ed9d0..50c258f927a8295766d6317d92b6dc138ef2d35d 100644 (file)
@@ -163,7 +163,7 @@ void OnlineBanList_URI_Get_Callback(float id, float status, string data)
 
                LOG_TRACE("received ban list item ", ftos(i / 4), ": ip=", ip);
                LOG_TRACE(" timeleft=", ftos(timeleft), " reason=", reason);
-               LOG_TRACE(" serverip=", serverip, "\n");
+               LOG_TRACE(" serverip=", serverip);
 
                timeleft -= 1.5 * autocvar_g_ban_sync_timeout;
                if(timeleft < 0)
@@ -243,7 +243,7 @@ void OnlineBanList_Think(entity this)
        return;
 
 LABEL(killme)
-       remove(this);
+       delete(this);
 }
 
 const float BAN_MAX = 256;
@@ -507,10 +507,10 @@ float Ban_Insert(string ip, float bantime, string reason, float dosync)
                        if(time + bantime > ban_expire[i])
                        {
                                ban_expire[i] = time + bantime;
-                               LOG_TRACE(ip, "'s ban has been prolonged to ", ftos(bantime), " seconds from now\n");
+                               LOG_TRACE(ip, "'s ban has been prolonged to ", ftos(bantime), " seconds from now");
                        }
                        else
-                               LOG_TRACE(ip, "'s ban is still active until ", ftos(ban_expire[i] - time), " seconds from now\n");
+                               LOG_TRACE(ip, "'s ban is still active until ", ftos(ban_expire[i] - time), " seconds from now");
 
                        // and enforce
                        reason = Ban_Enforce(i, reason);
@@ -552,7 +552,7 @@ float Ban_Insert(string ip, float bantime, string reason, float dosync)
        }
        // okay, insert our new victim as i
        Ban_Delete(i);
-       LOG_TRACE(ip, " has been banned for ", ftos(bantime), " seconds\n");
+       LOG_TRACE(ip, " has been banned for ", ftos(bantime), " seconds");
        ban_expire[i] = time + bantime;
        ban_ip[i] = strzone(ip);
        ban_count = max(ban_count, i + 1);
index 317b897b0c8a98d922b9ff49c4f30a48271d15b9..d33fe87c2e1a477189000119b501b52c6f69c88e 100644 (file)
@@ -1,7 +1,7 @@
 #include "item_key.qh"
 
 #include "../common/triggers/subs.qh"
-#include "../common/monsters/all.qh"
+#include "../common/monsters/_mod.qh"
 #include "../common/notifications/all.qh"
 #include "../common/util.qh"
 #include "../lib/warpzone/util_server.qh"
@@ -68,23 +68,23 @@ item_key
 /**
  * Key touch handler.
  */
-void item_key_touch(entity this)
+void item_key_touch(entity this, entity toucher)
 {
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
 
        // player already picked up this key
-       if (other.itemkeys & this.itemkeys)
+       if (toucher.itemkeys & this.itemkeys)
                return;
 
-       other.itemkeys |= this.itemkeys;
-       play2(other, this.noise);
+       toucher.itemkeys |= this.itemkeys;
+       play2(toucher, this.noise);
 
-       centerprint(other, this.message);
+       centerprint(toucher, this.message);
 
        string oldmsg = this.message;
        this.message = "";
-       SUB_UseTargets(this, other, other); // TODO: should we be using other for the trigger here?
+       SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for the trigger here?
        this.message = oldmsg;
 };
 
@@ -99,9 +99,9 @@ void spawn_item_key(entity this)
                this.noalign = 1;
 
        if (this.noalign)
-               this.movetype = MOVETYPE_NONE;
+               set_movetype(this, MOVETYPE_NONE);
        else
-               this.movetype = MOVETYPE_TOSS;
+               set_movetype(this, MOVETYPE_TOSS);
 
        precache_sound(this.noise);
 
@@ -160,7 +160,7 @@ spawnfunc(item_key)
        // reject this entity if more than one key was set!
        if (this.itemkeys>0 && (this.itemkeys & (this.itemkeys-1)) != 0) {
                objerror(this, "item_key.itemkeys must contain only 1 bit set specifying the key it represents!");
-               remove(this);
+               delete(this);
                return;
        }
 
@@ -202,7 +202,7 @@ spawnfunc(item_key)
 
                if (this.netname == "") {
                        objerror(this, "item_key doesn't have a default name for this key and a custom one was not specified!");
-                       remove(this);
+                       delete(this);
                        return;
                }
                break;
@@ -217,7 +217,7 @@ spawnfunc(item_key)
                _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model!
        } else if (this.model == "") {
                objerror(this, "item_key doesn't have a default model for this key and a custom one was not specified!");
-               remove(this);
+               delete(this);
                return;
        }
 
index b3a496f41e988c1ef19dce7d448e12772daecd0c..b5a6014462ea141adf7379172adf8fb9a853cd2c 100644 (file)
@@ -4,6 +4,7 @@
 #include "command/cmd.qh"
 #include "command/getreplies.qh"
 #include "../common/constants.qh"
+#include <common/net_linked.qh>
 #include "../common/mapinfo.qh"
 #include "../common/playerstats.qh"
 #include "../common/util.qh"
@@ -14,7 +15,6 @@
 float mapvote_nextthink;
 float mapvote_keeptwotime;
 float mapvote_timeout;
-string mapvote_message;
 const float MAPVOTE_SCREENSHOT_DIRS_COUNT = 4;
 string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
 float mapvote_screenshot_dirs_count;
@@ -41,10 +41,10 @@ entity mapvote_ent;
  * Returns the gamtype ID from its name, if type_name isn't a real gametype it
  * checks for sv_vote_gametype_(type_name)_type
  */
-float GameTypeVote_Type_FromString(string type_name)
+Gametype GameTypeVote_Type_FromString(string type_name)
 {
-       float type = MapInfo_Type_FromString(type_name);
-       if ( type == 0 )
+       Gametype type = MapInfo_Type_FromString(type_name);
+       if (type == NULL)
                type = MapInfo_Type_FromString(cvar_string(
                        strcat("sv_vote_gametype_",type_name,"_type")));
        return type;
@@ -54,22 +54,22 @@ int GameTypeVote_AvailabilityStatus(string type_name)
 {
        int flag = GTV_FORBIDDEN;
 
-       float type = MapInfo_Type_FromString(type_name);
-       if ( type == 0 )
+       Gametype type = MapInfo_Type_FromString(type_name);
+       if ( type == NULL )
        {
                type = MapInfo_Type_FromString(cvar_string(
                        strcat("sv_vote_gametype_",type_name,"_type")));
                flag |= GTV_CUSTOM;
        }
 
-       if( type == 0 )
+       if( type == NULL )
                return flag;
 
        if ( autocvar_nextmap != "" )
        {
-               if ( !MapInfo_Get_ByName(autocvar_nextmap, false, 0) )
+               if ( !MapInfo_Get_ByName(autocvar_nextmap, false, NULL) )
                        return flag;
-               if (!(MapInfo_Map_supportedGametypes & type))
+               if (!(MapInfo_Map_supportedGametypes & type.m_flags))
                        return flag;
        }
 
@@ -83,7 +83,7 @@ float GameTypeVote_GetMask()
        n = min(MAPVOTE_COUNT, n);
        gametype_mask = 0;
        for(j = 0; j < n; ++j)
-               gametype_mask |= GameTypeVote_Type_FromString(argv(j));
+               gametype_mask |= GameTypeVote_Type_FromString(argv(j)).m_flags;
        return gametype_mask;
 }
 
@@ -92,7 +92,7 @@ string GameTypeVote_MapInfo_FixName(string m)
        if ( autocvar_sv_vote_gametype )
        {
                MapInfo_Enumerate();
-               MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+               _MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
        }
        return MapInfo_FixName(m);
 }
@@ -255,7 +255,6 @@ void MapVote_Init()
        mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
        if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
                mapvote_keeptwotime = 0;
-       mapvote_message = "Choose a map and press its key!";
 
        MapVote_Spawn();
 }
@@ -271,14 +270,14 @@ void MapVote_SendPicture(entity to, int id)
 
 void MapVote_WriteMask()
 {
-       float i;
        if ( mapvote_count < 24 )
        {
-               float mask,power;
-               mask = 0;
-               for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
-                       if(mapvote_maps_flags[i] & GTV_AVAILABLE )
-                               mask |= power;
+               int mask = 0;
+               for(int j = 0; j < mapvote_count; ++j)
+               {
+                       if(mapvote_maps_flags[j] & GTV_AVAILABLE)
+                               mask |= BIT(j);
+               }
 
                if(mapvote_count < 8)
                        WriteByte(MSG_ENTITY, mask);
@@ -289,8 +288,8 @@ void MapVote_WriteMask()
        }
        else
        {
-               for ( i = 0; i < mapvote_count; ++i )
-                       WriteByte(MSG_ENTITY, mapvote_maps_flags[i]);
+               for (int j = 0; j < mapvote_count; ++j)
+                       WriteByte(MSG_ENTITY, mapvote_maps_flags[j]);
        }
 }
 
@@ -336,6 +335,8 @@ void GameTypeVote_SendOption(int i)
                                strcat("sv_vote_gametype_",type_name,"_name")));
                        WriteString(MSG_ENTITY, cvar_string(
                                strcat("sv_vote_gametype_",type_name,"_description")));
+                       WriteString(MSG_ENTITY, cvar_string(
+                               strcat("sv_vote_gametype_",type_name,"_type")));
                }
        }
 }
@@ -522,7 +523,7 @@ float MapVote_CheckRules_2()
        for(i = 0; i < mapvote_count_real; ++i)
                if ( mapvote_maps_flags[i] & GTV_AVAILABLE )
                {
-                       RandomSelection_Add(NULL, i, string_null, 1, mapvote_selections[i]);
+                       RandomSelection_AddFloat(i, 1, mapvote_selections[i]);
                        if ( gametypevote &&  mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) )
                        {
                                currentVotes = mapvote_selections[i];
@@ -542,7 +543,7 @@ float MapVote_CheckRules_2()
        for(i = 0; i < mapvote_count_real; ++i)
                if(i != firstPlace)
                if ( mapvote_maps_flags[i] & GTV_AVAILABLE )
-                       RandomSelection_Add(NULL, i, string_null, 1, mapvote_selections[i]);
+                       RandomSelection_AddFloat(i, 1, mapvote_selections[i]);
        secondPlace = RandomSelection_chosen_float;
        secondPlaceVotes = RandomSelection_best_priority;
        //dprint("Second place: ", ftos(secondPlace), "\n");
@@ -559,7 +560,6 @@ float MapVote_CheckRules_2()
                {
                        float didntvote;
                        MapVote_TouchMask();
-                       mapvote_message = "Now decide between the TOP TWO!";
                        mapvote_keeptwotime = 0;
                        result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
                        result = strcat(result, ":", ftos(firstPlaceVotes));
@@ -590,10 +590,8 @@ float MapVote_CheckRules_2()
 
 void MapVote_Tick()
 {
-       float keeptwo;
        float totalvotes;
 
-       keeptwo = mapvote_keeptwotime;
        MapVote_CheckRules_1(); // count
        if(MapVote_CheckRules_2()) // decide
                return;
@@ -695,12 +693,12 @@ void MapVote_Think()
        MapVote_Tick();
 }
 
-float GameTypeVote_SetGametype(float type)
+float GameTypeVote_SetGametype(Gametype type)
 {
        if (MapInfo_CurrentGametype() == type)
                return true;
 
-       float tsave = MapInfo_CurrentGametype();
+       Gametype tsave = MapInfo_CurrentGametype();
 
        MapInfo_SwitchGameType(type);
 
@@ -752,7 +750,7 @@ float GameTypeVote_Finished(float pos)
 float GameTypeVote_AddVotable(string nextMode)
 {
        float j;
-       if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == 0 )
+       if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == NULL )
                return false;
        for(j = 0; j < mapvote_count; ++j)
                if(mapvote_maps[j] == nextMode)
index b7d26decf53d883dd5f099f7444f6e9a7ed144bb..4d235da694539179a69b46a67bf4e87dd0d42ee2 100644 (file)
@@ -1,6 +1,6 @@
 #include "matrix.qh"
 
-#include "cl_player.qh"
+#include "player.qh"
 
 var void MX_Handle(int buf, string ancestor)
 {
index 2c206cf1a78112959ba04c9efefd4c966223235f..39c42a55695f3e585ecf9f440fe89435d382342b 100644 (file)
@@ -4,13 +4,14 @@
 #include "constants.qh"
 #include "g_hook.qh"
 #include "ipban.qh"
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 #include "../common/t_items.qh"
 #include "weapons/accuracy.qh"
 #include "weapons/csqcprojectile.qh"
 #include "weapons/selection.qh"
-#include "../common/command/generic.qh"
+#include "../common/command/_mod.qh"
 #include "../common/constants.qh"
+#include <common/net_linked.qh>
 #include "../common/deathtypes/all.qh"
 #include "../common/mapinfo.qh"
 #include "../common/notifications/all.qh"
 #include "../common/triggers/subs.qh"
 #include "../common/util.qh"
 #include "../common/turrets/sv_turrets.qh"
-#include "../common/weapons/all.qh"
+#include <common/weapons/_all.qh>
 #include "../common/vehicles/sv_vehicles.qh"
 #include "../common/vehicles/vehicle.qh"
-#include "../common/items/all.qc"
+#include "../common/items/_mod.qh"
 #include "../common/state.qh"
 #include "../common/effects/qc/globalsound.qh"
 #include "../lib/csqcmodel/sv_model.qh"
@@ -33,20 +34,25 @@ void crosshair_trace(entity pl)
 {
        traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
 }
+.bool ctrace_solidchanged;
 void crosshair_trace_plusvisibletriggers(entity pl)
 {
-       entity first;
-       entity e;
-       first = findchainfloat(solid, SOLID_TRIGGER);
-
-       for (e = first; e; e = e.chain)
-               if (e.model != "")
-                       e.solid = SOLID_BSP;
+       FOREACH_ENTITY_FLOAT(solid, SOLID_TRIGGER,
+       {
+               if(it.model != "")
+               {
+                       it.solid = SOLID_BSP;
+                       it.ctrace_solidchanged = true;
+               }
+       });
 
        crosshair_trace(pl);
 
-       for (e = first; e; e = e.chain)
-               e.solid = SOLID_TRIGGER;
+       FOREACH_ENTITY_FLOAT(ctrace_solidchanged, true,
+       {
+               it.solid = SOLID_TRIGGER;
+               it.ctrace_solidchanged = false;
+       });
 }
 void WarpZone_crosshair_trace(entity pl)
 {
@@ -92,7 +98,7 @@ void GameLogEcho(string s)
     }
     if (autocvar_sv_eventlog_console)
     {
-        LOG_INFO(s, "\n");
+        dedicated_print(strcat(s, "\n"));
     }
 }
 
@@ -167,7 +173,7 @@ entity findnearest(vector point, .string field, string value, vector axismod)
             {
                 LOG_TRACE("Nearest point (");
                 LOG_TRACE(nearest_entity[0].netname);
-                LOG_TRACE(") is not visible, using a visible one.\n");
+                LOG_TRACE(") is not visible, using a visible one.");
             }
             return nearest_entity[i];
         }
@@ -176,7 +182,7 @@ entity findnearest(vector point, .string field, string value, vector axismod)
     if (num_nearest == 0)
         return NULL;
 
-    LOG_TRACE("Not seeing any location point, using nearest as fallback.\n");
+    LOG_TRACE("Not seeing any location point, using nearest as fallback.");
     /* DEBUGGING CODE:
     dprint("Candidates were: ");
     for(j = 0; j < num_nearest; ++j)
@@ -223,15 +229,23 @@ string formatmessage(entity this, string msg)
        n = 7;
 
        ammoitems = "batteries";
-       if(this.items & ITEM_Plasma.m_itemid) ammoitems = ITEM_Plasma.m_name;
-       if(this.items & ITEM_Cells.m_itemid) ammoitems = ITEM_Cells.m_name;
-       if(this.items & ITEM_Rockets.m_itemid) ammoitems = ITEM_Rockets.m_name;
-       if(this.items & ITEM_Shells.m_itemid) ammoitems = ITEM_Shells.m_name;
+       switch((PS(this).m_weapon).ammo_field)
+       {
+               case ammo_shells:  ammoitems = ITEM_Shells.m_name;      break;
+               case ammo_nails:   ammoitems = ITEM_Bullets.m_name;     break;
+               case ammo_rockets: ammoitems = ITEM_Rockets.m_name;     break;
+               case ammo_cells:   ammoitems = ITEM_Cells.m_name;       break;
+               case ammo_plasma:  ammoitems = ITEM_Plasma.m_name;      break;
+               case ammo_fuel:    ammoitems = ITEM_JetpackFuel.m_name; break;
+       }
 
        WarpZone_crosshair_trace(this);
        cursor = trace_endpos;
        cursor_ent = trace_ent;
 
+       MUTATOR_CALLHOOK(PreFormatMessage, this, msg);
+       msg = M_ARGV(1, string);
+
        while (1) {
                if (n < 1)
                        break; // too many replacements
@@ -452,7 +466,7 @@ void GetCvars(entity this, int f)
 string playername(entity p)
 {
     string t;
-    if (teamplay && !intermission_running && IS_PLAYER(p))
+    if (teamplay && !gameover && IS_PLAYER(p))
     {
         t = Team_ColorCode(p.team);
         return strcat(t, strdecolorize(p.netname));
@@ -881,7 +895,7 @@ void InitializeEntitiesRun()
 {
     entity startoflist = initialize_entity_first;
     initialize_entity_first = NULL;
-    remove = remove_except_protected;
+    delete_fn = remove_except_protected;
     for (entity e = startoflist; e; e = e.initialize_entity_next)
     {
                e.remove_except_protected_forbidden = 1;
@@ -912,7 +926,7 @@ void InitializeEntitiesRun()
         }
         e = next;
     }
-    remove = remove_unsafely;
+    delete_fn = remove_unsafely;
 }
 
 .float(entity) isEliminated;
@@ -1031,8 +1045,7 @@ bool SUB_NoImpactCheck(entity this, entity toucher)
        // these stop the projectile from moving, so...
        if(trace_dphitcontents == 0)
        {
-               //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
-               LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n", etof(this), this.classname, vtos(this.origin));
+               LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin);
                checkclient(this);
        }
     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
@@ -1045,11 +1058,11 @@ bool SUB_NoImpactCheck(entity this, entity toucher)
         traceline(this.origin - tic, this.origin + tic, MOVE_NORMAL, this);
         if (trace_fraction >= 1)
         {
-            LOG_TRACE("Odd... did not hit...?\n");
+            LOG_TRACE("Odd... did not hit...?");
         }
         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
         {
-            LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
+            LOG_TRACE("Detected and prevented the sky-grapple bug.");
             return true;
         }
     }
@@ -1073,10 +1086,10 @@ bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher
                else if(this.classname == "spike")
                {
                        W_Crylink_Dequeue(this);
-                       remove(this);
+                       delete(this);
                }
                else
-                       remove(this);
+                       delete(this);
                return true;
        }
        if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
@@ -1140,7 +1153,6 @@ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundma
 {
     float m, i;
     vector start, org, delta, end, enddown, mstart;
-    entity sp;
 
     m = e.dphitcontentsmask;
     e.dphitcontentsmask = goodcontents | badcontents;
@@ -1196,16 +1208,26 @@ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundma
             continue;
 
        // rule 4: we must "see" some spawnpoint or item
-       for(sp = NULL; (sp = find(sp, classname, "info_player_deathmatch")); )
-               if(checkpvs(mstart, sp))
-                       if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
-                               break;
+    entity sp = NULL;
+    IL_EACH(g_spawnpoints, checkpvs(mstart, it),
+    {
+       if((traceline(mstart, it.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
+       {
+               sp = it;
+               break;
+       }
+    });
        if(!sp)
        {
-               for(sp = NULL; (sp = findflags(sp, flags, FL_ITEM)); )
-                       if(checkpvs(mstart, sp))
-                               if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
-                                       break;
+               IL_EACH(g_items, checkpvs(mstart, it),
+               {
+                       if((traceline(mstart, it.origin + (it.mins + it.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
+                       {
+                               sp = it;
+                               break;
+                       }
+               });
+
                if(!sp)
                        continue;
        }
@@ -1240,7 +1262,7 @@ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundma
     {
         setorigin(e, start);
         e.angles = vectoangles(end - start);
-        LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
+        LOG_TRACE("Needed ", ftos(i + 1), " attempts");
         return true;
     }
     else
@@ -1319,7 +1341,7 @@ void detach_sameorigin(entity e)
 
 void follow_sameorigin(entity e, entity to)
 {
-    e.movetype = MOVETYPE_FOLLOW; // make the hole follow
+    set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow
     e.aiment = to; // make the hole follow bmodel
     e.punchangle = to.angles; // the original angles of bmodel
     e.view_ofs = e.origin - to.origin; // relative origin
@@ -1328,7 +1350,7 @@ void follow_sameorigin(entity e, entity to)
 
 void unfollow_sameorigin(entity e)
 {
-    e.movetype = MOVETYPE_NONE;
+    set_movetype(e, MOVETYPE_NONE);
 }
 
 entity gettaginfo_relative_ent;
@@ -1350,7 +1372,7 @@ vector gettaginfo_relative(entity e, float tag)
 void SetMovetypeFollow(entity ent, entity e)
 {
        // FIXME this may not be warpzone aware
-       ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
+       set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow
        ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
        ent.aiment = e; // make the hole follow bmodel
        ent.punchangle = e.angles; // the original angles of bmodel
@@ -1361,14 +1383,14 @@ void SetMovetypeFollow(entity ent, entity e)
 }
 void UnsetMovetypeFollow(entity ent)
 {
-       ent.movetype = MOVETYPE_FLY;
+       set_movetype(ent, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(ent);
        ent.aiment = NULL;
 }
 float LostMovetypeFollow(entity ent)
 {
 /*
-       if(ent.movetype != MOVETYPE_FOLLOW)
+       if(ent.move_movetype != MOVETYPE_FOLLOW)
                if(ent.aiment)
                        error("???");
 */
index a8304b0e3aff44fa74e91510d4c82b7f1e042332..eb0f3a910a80cb568c57d52212460d37057f2637 100644 (file)
@@ -121,7 +121,7 @@ void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomo
 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER.m_id) || ((dt) == DEATH_SLIME.m_id) || ((dt) == DEATH_LAVA.m_id) || ((dt) == DEATH_SWAMP.m_id))
 
-#define PROJECTILE_TOUCH(this) MACRO_BEGIN if (WarpZone_Projectile_Touch(this)) return; MACRO_END
+#define PROJECTILE_TOUCH(e,t) MACRO_BEGIN if (WarpZone_Projectile_Touch(e,t)) return; MACRO_END
 
 #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
 
@@ -182,18 +182,18 @@ float g_pickup_armormedium_anyway;
 float g_pickup_armorbig;
 float g_pickup_armorbig_max;
 float g_pickup_armorbig_anyway;
-float g_pickup_armorlarge;
-float g_pickup_armorlarge_max;
-float g_pickup_armorlarge_anyway;
+float g_pickup_armormega;
+float g_pickup_armormega_max;
+float g_pickup_armormega_anyway;
 float g_pickup_healthsmall;
 float g_pickup_healthsmall_max;
 float g_pickup_healthsmall_anyway;
 float g_pickup_healthmedium;
 float g_pickup_healthmedium_max;
 float g_pickup_healthmedium_anyway;
-float g_pickup_healthlarge;
-float g_pickup_healthlarge_max;
-float g_pickup_healthlarge_anyway;
+float g_pickup_healthbig;
+float g_pickup_healthbig_max;
+float g_pickup_healthbig_anyway;
 float g_pickup_healthmega;
 float g_pickup_healthmega_max;
 float g_pickup_healthmega_anyway;
@@ -256,6 +256,8 @@ void readlevelcvars()
        g_jetpack = cvar("g_jetpack");
        sv_maxidle = cvar("sv_maxidle");
        sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
+       sv_maxidle_slots = cvar("sv_maxidle_slots");
+       sv_maxidle_slots_countbots = cvar("sv_maxidle_slots_countbots");
        sv_autotaunt = cvar("sv_autotaunt");
        sv_taunt = cvar("sv_taunt");
 
@@ -310,18 +312,18 @@ void readlevelcvars()
        g_pickup_armorbig = cvar("g_pickup_armorbig");
        g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
        g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
-       g_pickup_armorlarge = cvar("g_pickup_armorlarge");
-       g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
-       g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
+       g_pickup_armormega = cvar("g_pickup_armormega");
+       g_pickup_armormega_max = cvar("g_pickup_armormega_max");
+       g_pickup_armormega_anyway = cvar("g_pickup_armormega_anyway");
        g_pickup_healthsmall = cvar("g_pickup_healthsmall");
        g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
        g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
        g_pickup_healthmedium = cvar("g_pickup_healthmedium");
        g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
        g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
-       g_pickup_healthlarge = cvar("g_pickup_healthlarge");
-       g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
-       g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
+       g_pickup_healthbig = cvar("g_pickup_healthbig");
+       g_pickup_healthbig_max = cvar("g_pickup_healthbig_max");
+       g_pickup_healthbig_anyway = cvar("g_pickup_healthbig_anyway");
        g_pickup_healthmega = cvar("g_pickup_healthmega");
        g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
        g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
index 3d2321896cb5374a8e045bb256c8c4ceba909b50..f0108dec37f6f31717305564d5678040008018d4 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <server/mutators/all.qc>
+#include <server/mutators/loader.qc>
+
+#include <server/mutators/mutator/_mod.inc>
index 8feb1f37d8c2f29a6245bf1e5cc76c845ec06720..9888c94666bfd085a8abe873054f7de7563ea469 100644 (file)
@@ -1,2 +1,4 @@
 // generated file; do not modify
-#include <server/mutators/all.qh>
+#include <server/mutators/loader.qh>
+
+#include <server/mutators/mutator/_mod.qh>
diff --git a/qcsrc/server/mutators/all.inc b/qcsrc/server/mutators/all.inc
deleted file mode 100644 (file)
index 1a80b44..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "mutator/gamemode_assault.qc"
-#include "mutator/gamemode_ca.qc"
-#include "mutator/gamemode_ctf.qc"
-#include "mutator/gamemode_cts.qc"
-#include "mutator/gamemode_deathmatch.qc"
-#include "mutator/gamemode_domination.qc"
-#include "mutator/gamemode_freezetag.qc"
-#include "mutator/gamemode_invasion.qc"
-#include "mutator/gamemode_keepaway.qc"
-#include "mutator/gamemode_keyhunt.qc"
-#include "mutator/gamemode_lms.qc"
-#include "mutator/gamemode_race.qc"
-#include "mutator/gamemode_tdm.qc"
diff --git a/qcsrc/server/mutators/all.qc b/qcsrc/server/mutators/all.qc
deleted file mode 100644 (file)
index d742c77..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "all.qh"
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-    #include <lib/warpzone/anglestransform.qh>
-    #include <lib/warpzone/common.qh>
-    #include <lib/warpzone/util_server.qh>
-    #include <lib/warpzone/server.qh>
-    #include <common/constants.qh>
-    #include <common/stats.qh>
-    #include <common/teams.qh>
-    #include <common/util.qh>
-    #include <common/command/markup.qh>
-    #include <common/command/rpn.qh>
-    #include <common/command/generic.qh>
-    #include <common/command/command.qh>
-    #include <common/net_notice.qh>
-    #include <common/animdecide.qh>
-    #include <common/monsters/all.qh>
-    #include <common/monsters/sv_monsters.qh>
-    #include <common/monsters/spawn.qh>
-    #include <common/weapons/config.qh>
-    #include <common/weapons/all.qh>
-    #include "../weapons/accuracy.qh"
-    #include "../weapons/common.qh"
-    #include "../weapons/csqcprojectile.qh"
-    #include "../weapons/hitplot.qh"
-    #include "../weapons/selection.qh"
-    #include "../weapons/spawning.qh"
-    #include "../weapons/throwing.qh"
-    #include "../weapons/tracing.qh"
-    #include "../weapons/weaponstats.qh"
-    #include "../weapons/weaponsystem.qh"
-    #include <common/t_items.qh>
-    #include "../autocvars.qh"
-    #include "../constants.qh"
-    #include "../defs.qh"
-    #include <common/notifications/all.qh>
-    #include <common/deathtypes/all.qh>
-    #include "all.qh"
-    #include <common/turrets/sv_turrets.qh>
-    #include <common/vehicles/all.qh>
-    #include "../campaign.qh"
-    #include <common/campaign_common.qh>
-    #include <common/mapinfo.qh>
-    #include "../command/common.qh"
-    #include "../command/banning.qh"
-    #include "../command/radarmap.qh"
-    #include "../command/vote.qh"
-    #include "../command/getreplies.qh"
-    #include "../command/cmd.qh"
-    #include "../command/sv_cmd.qh"
-    #include <common/csqcmodel_settings.qh>
-    #include <lib/csqcmodel/common.qh>
-    #include <lib/csqcmodel/sv_model.qh>
-    #include "../anticheat.qh"
-    #include "../cheats.qh"
-    #include <common/playerstats.qh>
-    #include "../portals.qh"
-    #include "../g_hook.qh"
-    #include "../scores.qh"
-    #include "../spawnpoints.qh"
-    #include "../mapvoting.qh"
-    #include "../ipban.qh"
-    #include "../race.qh"
-    #include "../antilag.qh"
-    #include "../playerdemo.qh"
-    #include "../round_handler.qh"
-    #include "../item_key.qh"
-    #include "../pathlib/pathlib.qh"
-    #include <common/vehicles/all.qh>
-#endif
-
-#include "all.qh"
-
-STATIC_INIT_LATE(Gametype) {
-    Gametype g = MapInfo_Type(MapInfo_CurrentGametype());
-    if (g) {
-        for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) {
-            string s = car(_s);
-            FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break));
-        }
-    }
-}
-
-#define IMPLEMENTATION
-#include "all.inc"
-#undef IMPLEMENTATION
diff --git a/qcsrc/server/mutators/all.qh b/qcsrc/server/mutators/all.qh
deleted file mode 100644 (file)
index 4a1f2b3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-#include "mutator.qh"
-#include "gamemode.qh"
-
-#include "all.inc"
index 6fbce15b95a8658b80749138d3dfcc6e2dd8079b..09e1cf70bd212497b69907be4ec13c31e97d33ca 100644 (file)
@@ -16,6 +16,14 @@ MUTATOR_HOOKABLE(MakePlayerObserver, EV_MakePlayerObserver)
     /**/
 MUTATOR_HOOKABLE(PutClientInServer, EV_PutClientInServer);
 
+/**
+ * return true to prevent a spectator/observer to spawn as player
+ */
+ #define EV_ForbidSpawn(i, o) \
+    /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /**/
+MUTATOR_HOOKABLE(ForbidSpawn, EV_ForbidSpawn);
+
 /** called when a player spawns as player, after shared setup, before his weapon is chosen (so items may be changed in here) */
 #define EV_PlayerSpawn(i, o) \
        /** player spawning */ i(entity, MUTATOR_ARGV_0_entity) \
@@ -55,9 +63,20 @@ MUTATOR_HOOKABLE(ClientDisconnect, EV_ClientDisconnect);
     /**/
 MUTATOR_HOOKABLE(PlayerDies, EV_PlayerDies);
 
+/** allows overriding the frag centerprint messages */
+#define EV_FragCenterMessage(i, o) \
+    /** attacker       */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** target         */ i(entity, MUTATOR_ARGV_1_entity) \
+    /** deathtype      */ i(float, MUTATOR_ARGV_2_float) \
+    /** attacker kcount*/ i(int,  MUTATOR_ARGV_3_int) \
+    /** targ killcount */ i(int,  MUTATOR_ARGV_4_int) \
+    /**/
+MUTATOR_HOOKABLE(FragCenterMessage, EV_FragCenterMessage);
+
 /** called when a player dies to e.g. remove stuff he was carrying */
 #define EV_PlayHitsound(i, o) \
     /** victim */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** attacker */ i(entity, MUTATOR_ARGV_1_entity) \
     /**/
 MUTATOR_HOOKABLE(PlayHitsound, EV_PlayHitsound);
 
@@ -77,6 +96,14 @@ MUTATOR_HOOKABLE(WeaponSound, EV_WeaponSound);
     /**/
 MUTATOR_HOOKABLE(ItemModel, EV_ItemModel);
 
+/** called when an item sound is about to be played, allows custom paths etc. */
+#define EV_ItemSound(i, o) \
+    /** sound       */ i(string, MUTATOR_ARGV_0_string) \
+    /** output      */ i(string, MUTATOR_ARGV_1_string) \
+    /**/               o(string, MUTATOR_ARGV_1_string) \
+    /**/
+MUTATOR_HOOKABLE(ItemSound, EV_ItemSound);
+
 /** called when someone was fragged by "self", and is expected to change frag_score to adjust scoring for the kill */
 #define EV_GiveFragsForKill(i, o) \
     /** attacker   */ i(entity, MUTATOR_ARGV_0_entity) \
@@ -89,14 +116,14 @@ MUTATOR_HOOKABLE(GiveFragsForKill, EV_GiveFragsForKill);
 /** called when the match ends */
 MUTATOR_HOOKABLE(MatchEnd, EV_NO_ARGS);
 
-/** should adjust number to contain the team count */
-#define EV_GetTeamCount(i, o) \
-    /** number of teams    */ i(float, MUTATOR_ARGV_0_float) \
+/** allows adjusting allowed teams */
+#define EV_CheckAllowedTeams(i, o) \
+    /** mask of teams      */ i(float, MUTATOR_ARGV_0_float) \
     /**/                      o(float, MUTATOR_ARGV_0_float) \
     /** team entity name   */ i(string, MUTATOR_ARGV_1_string) \
     /**/                      o(string, MUTATOR_ARGV_1_string) \
     /**/
-MUTATOR_HOOKABLE(GetTeamCount, EV_GetTeamCount);
+MUTATOR_HOOKABLE(CheckAllowedTeams, EV_CheckAllowedTeams);
 
 /** copies variables for spectating "spectatee" to "this" */
 #define EV_SpectateCopy(i, o) \
@@ -115,6 +142,14 @@ MUTATOR_HOOKABLE(SpectateCopy, EV_SpectateCopy);
     /**/
 MUTATOR_HOOKABLE(FormatMessage, EV_FormatMessage);
 
+/** called before any formatting is applied, handy for tweaking the message before scripts get ahold of it */
+#define EV_PreFormatMessage(i, o) \
+    /** player        */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** message       */ i(string, MUTATOR_ARGV_1_string) \
+    /**/                 o(string, MUTATOR_ARGV_1_string) \
+    /**/
+MUTATOR_HOOKABLE(PreFormatMessage, EV_PreFormatMessage);
+
 /** returns true if throwing the current weapon shall not be allowed */
 #define EV_ForbidThrowCurrentWeapon(i, o) \
     /** player        */ i(entity, MUTATOR_ARGV_0_entity) \
@@ -271,6 +306,15 @@ MUTATOR_HOOKABLE(MonsterMove, EV_MonsterMove);
 /** called when a monster looks for another target */
 MUTATOR_HOOKABLE(MonsterFindTarget, EV_NO_ARGS);
 
+/**
+ * called when validating a monster's target
+ */
+#define EV_MonsterValidTarget(i, o) \
+    /** monster */       i(entity, MUTATOR_ARGV_0_entity) \
+    /** target */        i(entity, MUTATOR_ARGV_1_entity) \
+    /**/
+MUTATOR_HOOKABLE(MonsterValidTarget, EV_MonsterValidTarget);
+
 /** called to change a random monster to a miniboss */
 #define EV_MonsterCheckBossFlag(i, o) \
     /** monster */ i(entity, MUTATOR_ARGV_0_entity) \
@@ -327,10 +371,11 @@ MUTATOR_HOOKABLE(PlayerDamage_Calculate, EV_PlayerDamage_Calculate);
 #define EV_PlayerDamaged(i, o) \
     /** attacker  */ i(entity, MUTATOR_ARGV_0_entity) \
     /** target    */ i(entity, MUTATOR_ARGV_1_entity) \
-    /** health    */ i(int,    MUTATOR_ARGV_2_int) \
-    /** armor     */ i(int,    MUTATOR_ARGV_3_int) \
+    /** health    */ i(float,    MUTATOR_ARGV_2_float) \
+    /** armor     */ i(float,    MUTATOR_ARGV_3_float) \
     /** location  */ i(vector, MUTATOR_ARGV_4_vector) \
     /** deathtype */ i(int,    MUTATOR_ARGV_5_int) \
+    /** potential_damage     */ i(float,    MUTATOR_ARGV_6_float) \
     /**/
 MUTATOR_HOOKABLE(PlayerDamaged, EV_PlayerDamaged);
 
@@ -350,7 +395,7 @@ MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo);
     /**/
 MUTATOR_HOOKABLE(W_Reload, EV_W_Reload);
 
-/** called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items. */
+/** called at the end of player_powerups() in client.qc, used for manipulating the values which are set by powerup items. */
 #define EV_PlayerPowerups(i, o) \
     /** player */    i(entity, MUTATOR_ARGV_0_entity) \
     /** old items */ i(int, MUTATOR_ARGV_1_int) \
@@ -452,6 +497,7 @@ MUTATOR_HOOKABLE(SV_StartFrame, EV_NO_ARGS);
 
 #define EV_SetModname(i, o) \
     /** name of the mutator/mod if it warrants showing as such in the server browser */ \
+    /**/ i(string, MUTATOR_ARGV_0_string) \
     /**/ o(string, MUTATOR_ARGV_0_string) \
     /**/
 MUTATOR_HOOKABLE(SetModname, EV_SetModname);
@@ -499,7 +545,7 @@ MUTATOR_HOOKABLE(BotShouldAttack, EV_BotShouldAttack);
 MUTATOR_HOOKABLE(PortalTeleport, EV_PortalTeleport);
 
 /**
- * called whenever a player uses impulse 33 (help me) in cl_impulse.qc
+ * called whenever a player uses impulse 33 (help me) in impulse.qc
  * normally help me ping uses .waypointsprite_attachedforcarrier,
  * but if your mutator uses something different then you can handle it
  * in a special manner using this hook
@@ -513,10 +559,10 @@ MUTATOR_HOOKABLE(HelpMePing, EV_HelpMePing);
  * called when a vehicle initializes
  * return true to remove the vehicle
  */
-#define EV_VehicleSpawn(i, o) \
+#define EV_VehicleInit(i, o) \
     /** vehicle */ i(entity, MUTATOR_ARGV_0_entity) \
     /**/
-MUTATOR_HOOKABLE(VehicleSpawn, EV_VehicleSpawn);
+MUTATOR_HOOKABLE(VehicleInit, EV_VehicleInit);
 
 /**
  * called when a player enters a vehicle
@@ -656,7 +702,7 @@ MUTATOR_HOOKABLE(SetChangeParms, EV_NO_ARGS);
 MUTATOR_HOOKABLE(DecodeLevelParms, EV_NO_ARGS);
 
 #define EV_GetRecords(i, o) \
-    /** page */           i(int, MUTATOR_ARGV_1_int) \
+    /** page */           i(int, MUTATOR_ARGV_0_int) \
     /** record list */    i(string, MUTATOR_ARGV_1_string) \
     /**/                  o(string, MUTATOR_ARGV_1_string) \
     /**/
@@ -748,7 +794,7 @@ MUTATOR_HOOKABLE(CheckRules_World, EV_CheckRules_World);
 MUTATOR_HOOKABLE(WantWeapon, EV_WantWeapon);
 
 #define EV_AddPlayerScore(i, o) \
-    /** score field */  i(int, MUTATOR_ARGV_0_int) \
+    /** score field */  i(PlayerScoreField, MUTATOR_ARGV_0_entity) \
     /** score */        i(float, MUTATOR_ARGV_1_float) \
     /**/                o(float, MUTATOR_ARGV_1_float) \
     /** player */       i(entity, MUTATOR_ARGV_2_entity) \
@@ -837,3 +883,37 @@ MUTATOR_HOOKABLE(Player_ChangeTeam, EV_Player_ChangeTeam);
        /** data */     i(string, MUTATOR_ARGV_2_string) \
     /**/
 MUTATOR_HOOKABLE(URI_GetCallback, EV_URI_GetCallback);
+
+/**
+ * return true to prevent weapon use for a player
+ */
+ #define EV_ForbidWeaponUse(i, o) \
+    /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /**/
+MUTATOR_HOOKABLE(ForbidWeaponUse, EV_ForbidWeaponUse);
+
+/** called when creating a clone of the player (usually for corpses that stay after the player has re-spawned) */
+#define EV_CopyBody(i, o) \
+    /** player */               i(entity, MUTATOR_ARGV_0_entity) \
+    /** newly created clone */  i(entity, MUTATOR_ARGV_1_entity) \
+    /** keepvelocity? */        i(bool, MUTATOR_ARGV_2_bool) \
+    /**/
+MUTATOR_HOOKABLE(CopyBody, EV_CopyBody);
+
+/** called when sending a chat message, ret argument can be changed to prevent the message */
+#define EV_ChatMessage(i, o) \
+    /** sender */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** ret */ i(int, MUTATOR_ARGV_1_int) \
+    /**/ o(int, MUTATOR_ARGV_1_int) \
+    /**/
+MUTATOR_HOOKABLE(ChatMessage, EV_ChatMessage);
+
+/** return true to prevent sending a chat (private, team or regular) message from reaching a certain player */
+#define EV_ChatMessageTo(i, o) \
+    /** destination player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** sender */ i(entity, MUTATOR_ARGV_1_entity) \
+    /**/
+MUTATOR_HOOKABLE(ChatMessageTo, EV_ChatMessageTo);
+
+/** return true to just restart the match, for modes that don't support readyrestart */
+MUTATOR_HOOKABLE(ReadyRestart_Deny, EV_NO_ARGS);
index ee80b44b06df6213c745d39e0c2b3dc70bb9c116..9a473affdf8bc1f8105cdbc9cef20fca5d8a1cea 100644 (file)
@@ -1,28 +1,87 @@
 #pragma once
 
-#include <server/cl_client.qh>
-#include <server/cl_player.qh>
-#include <server/cl_impulse.qh>
-#include <server/cheats.qh>
-#include <server/g_damage.qh>
 #include <server/g_world.qh>
 #include <server/round_handler.qh>
 #include <server/scores.qh>
 #include <server/scores_rules.qh>
 #include <server/teamplay.qh>
 
-#include <server/bot/bot.qh>
-#include <server/bot/navigation.qh>
-#include <server/bot/waypoints.qh>
-#include <server/bot/havocbot/roles.qh>
+#include "mutator.qh"
 
-#include <server/bot/havocbot/havocbot.qh>
+// TODO: trim
 
+#include <lib/warpzone/anglestransform.qh>
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/util_server.qh>
+#include <lib/warpzone/server.qh>
+#include <common/constants.qh>
+#include <common/stats.qh>
+#include <common/teams.qh>
+#include <common/util.qh>
+#include <common/command/_mod.qh>
+#include <common/net_notice.qh>
+#include <common/animdecide.qh>
+#include <common/monsters/_mod.qh>
+#include <common/monsters/sv_monsters.qh>
+#include <common/monsters/sv_spawn.qh>
+#include <common/weapons/config.qh>
+#include <common/weapons/_all.qh>
+#include <server/weapons/accuracy.qh>
+#include <server/weapons/common.qh>
+#include <server/weapons/csqcprojectile.qh>
+#include <server/weapons/hitplot.qh>
+#include <server/weapons/selection.qh>
+#include <server/weapons/spawning.qh>
+#include <server/weapons/throwing.qh>
+#include <server/weapons/tracing.qh>
+#include <server/weapons/weaponstats.qh>
+#include <server/weapons/weaponsystem.qh>
+#include <common/t_items.qh>
+#include <server/autocvars.qh>
+#include <server/constants.qh>
+#include <server/defs.qh>
+#include <common/notifications/all.qh>
+#include <common/deathtypes/all.qh>
+#include <common/turrets/sv_turrets.qh>
+#include <common/vehicles/all.qh>
+#include <server/campaign.qh>
+#include <common/campaign_common.qh>
+#include <common/mapinfo.qh>
+#include <server/command/common.qh>
+#include <server/command/banning.qh>
+#include <server/command/radarmap.qh>
 #include <server/command/vote.qh>
+#include <server/command/getreplies.qh>
+#include <server/command/cmd.qh>
+#include <server/command/sv_cmd.qh>
+#include <common/csqcmodel_settings.qh>
+#include <lib/csqcmodel/common.qh>
+#include <lib/csqcmodel/sv_model.qh>
+#include <server/anticheat.qh>
+#include <server/cheats.qh>
+#include <common/playerstats.qh>
+#include <server/portals.qh>
+#include <server/g_hook.qh>
+#include <server/spawnpoints.qh>
+#include <server/mapvoting.qh>
+#include <server/ipban.qh>
+#include <server/antilag.qh>
+#include <server/playerdemo.qh>
+#include <server/item_key.qh>
+#include <server/pathlib/pathlib.qh>
+#include <common/vehicles/all.qh>
+
+#include <server/client.qh>
+#include <server/player.qh>
+#include <server/impulse.qh>
+#include <server/cheats.qh>
+#include <server/g_damage.qh>
 
-#include <common/monsters/all.qh>
+#include <server/bot/api.qh>
 
-#include <server/command/common.qh>
+#include <server/command/_mod.qh>
+
+#include <common/monsters/_mod.qh>
 
 #include <server/weapons/tracing.qh>
 #include <server/weapons/weaponsystem.qh>
diff --git a/qcsrc/server/mutators/loader.qc b/qcsrc/server/mutators/loader.qc
new file mode 100644 (file)
index 0000000..1784e72
--- /dev/null
@@ -0,0 +1,11 @@
+#include "loader.qh"
+
+STATIC_INIT_LATE(Gametype) {
+    Gametype g = MapInfo_CurrentGametype();
+    if (g) {
+        for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) {
+            string s = car(_s);
+            FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break));
+        }
+    }
+}
diff --git a/qcsrc/server/mutators/loader.qh b/qcsrc/server/mutators/loader.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 40fad29b34426e03fae679d508eab6968ca327ba..e051cd6978dc6da2619fccfa06efc6e1ef2d61be 100644 (file)
@@ -2,24 +2,18 @@
 
 #include <common/mutators/base.qh>
 
-#include <server/cl_client.qh>
-#include <server/cl_player.qh>
-#include <server/cl_impulse.qh>
+#include <server/client.qh>
+#include <server/player.qh>
+#include <server/impulse.qh>
 #include <server/cheats.qh>
 #include <server/g_damage.qh>
 #include <server/round_handler.qh>
 #include <server/scores.qh>
 #include <server/scores_rules.qh>
 
-#include <server/bot/bot.qh>
-#include <server/bot/navigation.qh>
-#include <server/bot/waypoints.qh>
+#include <server/bot/api.qh>
 
-#include <server/bot/havocbot/havocbot.qh>
-#include <server/bot/havocbot/roles.qh>
-
-#include <server/command/vote.qh>
-#include <server/command/common.qh>
+#include <server/command/_mod.qh>
 
 #include <server/weapons/common.qh>
 #include <server/weapons/tracing.qh>
@@ -33,7 +27,7 @@
 #include <common/stats.qh>
 #include <common/teams.qh>
 
-#include <common/monsters/all.qh>
+#include <common/monsters/_mod.qh>
 
 #include <lib/warpzone/anglestransform.qh>
 #include <lib/warpzone/server.qh>
index 938cdd748bd60be5aebc661e36bd64fa868d7184..39987414348d0d3a37556741a320eb755e3a1cb3 100644 (file)
@@ -1,72 +1,7 @@
 #include "gamemode_assault.qh"
-#ifndef GAMEMODE_ASSAULT_H
-#define GAMEMODE_ASSAULT_H
 
-void assault_ScoreRules();
-void ActivateTeamplay();
-
-REGISTER_MUTATOR(as, false)
-{
-       ActivateTeamplay();
-       have_team_spawns = -1; // request team spawns
-
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               assault_ScoreRules();
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back assault_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-// sprites
-.entity assault_decreaser;
-.entity assault_sprite;
-
-// legacy bot defs
-const int HAVOCBOT_AST_ROLE_NONE = 0;
-const int HAVOCBOT_AST_ROLE_DEFENSE = 2;
-const int HAVOCBOT_AST_ROLE_OFFENSE = 4;
-
-.int havocbot_role_flags;
-.float havocbot_attack_time;
-
-.void(entity this) havocbot_role;
-.void(entity this) havocbot_previous_role;
-
-void(entity this) havocbot_role_ast_defense;
-void(entity this) havocbot_role_ast_offense;
-.entity havocbot_ast_target;
-
-void(entity bot) havocbot_ast_reset_role;
-
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-// scoreboard stuff
-const float ST_ASSAULT_OBJECTIVES = 1;
-const float SP_ASSAULT_OBJECTIVES = 4;
-
-// predefined spawnfuncs
-void target_objective_decrease_activate(entity this);
-#endif
-
-#ifdef IMPLEMENTATION
 .entity sprite;
+#define AS_ROUND_DELAY 5
 
 // random functions
 void assault_objective_use(entity this, entity actor, entity trigger)
@@ -76,13 +11,10 @@ void assault_objective_use(entity this, entity actor, entity trigger)
        //print("^2Activated objective ", this.targetname, "=", etos(this), "\n");
        //print("Activator is ", actor.classname, "\n");
 
-       for (entity e = NULL; (e = find(e, target, this.targetname)); )
+       IL_EACH(g_assault_objectivedecreasers, it.target == this.targetname,
        {
-               if (e.classname == "target_objective_decrease")
-               {
-                       target_objective_decrease_activate(e);
-               }
-       }
+               target_objective_decrease_activate(it);
+       });
 }
 
 vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current)
@@ -140,18 +72,14 @@ void assault_objective_decrease_use(entity this, entity actor, entity trigger)
 
 void assault_setenemytoobjective(entity this)
 {
-       entity objective;
-       for(objective = NULL; (objective = find(objective, targetname, this.target)); )
+       IL_EACH(g_assault_objectives, it.targetname == this.target,
        {
-               if(objective.classname == "target_objective")
-               {
-                       if(this.enemy == NULL)
-                               this.enemy = objective;
-                       else
-                               objerror(this, "more than one objective as target - fix the map!");
-                       break;
-               }
-       }
+               if(this.enemy == NULL)
+                       this.enemy = it;
+               else
+                       objerror(this, "more than one objective as target - fix the map!");
+               break;
+       });
 
        if(this.enemy == NULL)
                objerror(this, "no objective as target - fix the map!");
@@ -167,32 +95,32 @@ bool assault_decreaser_sprite_visible(entity this, entity player, entity view)
 
 void target_objective_decrease_activate(entity this)
 {
-       entity ent, spr;
+       entity spr;
        this.owner = NULL;
-       for(ent = NULL; (ent = find(ent, target, this.targetname)); )
+       FOREACH_ENTITY_STRING(target, this.targetname,
        {
-               if(ent.assault_sprite != NULL)
+               if(it.assault_sprite != NULL)
                {
-                       WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime);
-                       if(ent.classname == "func_assault_destructible")
-                               ent.sprite = NULL; // TODO: just unsetting it?!
+                       WaypointSprite_Disown(it.assault_sprite, waypointsprite_deadlifetime);
+                       if(it.classname == "func_assault_destructible")
+                               it.sprite = NULL; // TODO: just unsetting it?!
                }
 
-               spr = WaypointSprite_SpawnFixed(WP_Assault, 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE);
+               spr = WaypointSprite_SpawnFixed(WP_AssaultDefend, 0.5 * (it.absmin + it.absmax), it, assault_sprite, RADARICON_OBJECTIVE);
                spr.assault_decreaser = this;
                spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible;
                spr.classname = "sprite_waypoint";
                WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY);
-               if(ent.classname == "func_assault_destructible")
+               if(it.classname == "func_assault_destructible")
                {
                        WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultDestroy, WP_AssaultDestroy);
-                       WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
-                       WaypointSprite_UpdateHealth(spr, ent.health);
-                       ent.sprite = spr;
+                       WaypointSprite_UpdateMaxHealth(spr, it.max_health);
+                       WaypointSprite_UpdateHealth(spr, it.health);
+                       it.sprite = spr;
                }
                else
                        WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultPush, WP_AssaultPush);
-       }
+       });
 }
 
 void target_objective_decrease_findtarget(entity this)
@@ -217,7 +145,8 @@ void assault_roundstart_use(entity this, entity actor, entity trigger)
        SUB_UseTargets(this, this, trigger);
 
        //(Re)spawn all turrets
-       FOREACH_ENTITY_CLASS("turret_main", true, LAMBDA(
+       IL_EACH(g_turrets, true,
+       {
                // Swap turret teams
                if(it.team == NUM_TEAM_1)
                        it.team = NUM_TEAM_2;
@@ -226,7 +155,7 @@ void assault_roundstart_use(entity this, entity actor, entity trigger)
 
                // Doubles as teamchange
                turret_respawn(it);
-       ));
+       });
 }
 void assault_roundstart_use_this(entity this)
 {
@@ -251,20 +180,10 @@ void assault_wall_think(entity this)
 
 // trigger new round
 // reset objectives, toggle spawnpoints, reset triggers, ...
-void vehicles_clearreturn(entity veh);
-void vehicles_spawn(entity this);
 void assault_new_round(entity this)
 {
        //bprint("ASSAULT: new round\n");
 
-       // Eject players from vehicles
-    FOREACH_CLIENT(IS_PLAYER(it) && it.vehicle, vehicles_exit(it.vehicle, VHEF_RELEASE));
-
-    FOREACH_ENTITY_FLAGS(vehicle_flags, VHF_ISVEHICLE, LAMBDA(
-        vehicles_clearreturn(it);
-        vehicles_spawn(it);
-    ));
-
        // up round counter
        this.winning = this.winning + 1;
 
@@ -274,20 +193,37 @@ void assault_new_round(entity this)
        else
                assault_attacker_team = NUM_TEAM_1;
 
-       FOREACH_ENTITY(IS_NOT_A_CLIENT(it), LAMBDA(
+       FOREACH_ENTITY_FLOAT(pure_data, false,
+       {
+               if(IS_CLIENT(it))
+                       continue;
+
                if (it.team_saved == NUM_TEAM_1) it.team_saved = NUM_TEAM_2;
                else if (it.team_saved == NUM_TEAM_2) it.team_saved = NUM_TEAM_1;
-       ));
+       });
 
        // reset the level with a countdown
-       cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
+       cvar_set("timelimit", ftos(ceil(time - AS_ROUND_DELAY - game_starttime) / 60));
        ReadyRestart_force(); // sets game_starttime
 }
 
+entity as_round;
+.entity ent_winning;
+void as_round_think()
+{
+       gameover = false;
+       assault_new_round(as_round.ent_winning);
+       delete(as_round);
+       as_round = NULL;
+}
+
 // Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
 // they win. Otherwise the defending team wins once the timelimit passes.
 int WinningCondition_Assault()
 {
+       if(as_round)
+               return WINNING_NO;
+
        WinningConditionHelper(NULL); // set worldstatus
 
        int status = WINNING_NO;
@@ -307,7 +243,7 @@ int WinningCondition_Assault()
        {
                if(ent.winning) // round end has been triggered by attacking team
                {
-                       bprint("ASSAULT: round completed...\n");
+                       bprint("Assault: round completed.\n");
                        SetWinners(team, assault_attacker_team);
 
                        TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
@@ -318,7 +254,17 @@ int WinningCondition_Assault()
                        }
                        else
                        {
-                               assault_new_round(ent);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ASSAULT_OBJ_DESTROYED, ceil(time - game_starttime));
+                               as_round = new(as_round);
+                               as_round.think = as_round_think;
+                               as_round.ent_winning = ent;
+                               as_round.nextthink = time + AS_ROUND_DELAY;
+                               gameover = true;
+
+                               // make sure timelimit isn't hit while the game is blocked
+                               if(autocvar_timelimit > 0)
+                               if(time + AS_ROUND_DELAY >= game_starttime + autocvar_timelimit * 60)
+                                       cvar_set("timelimit", ftos(autocvar_timelimit + AS_ROUND_DELAY / 60));
                        }
                }
        }
@@ -329,7 +275,7 @@ int WinningCondition_Assault()
 // spawnfuncs
 spawnfunc(info_player_attacker)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.team = NUM_TEAM_1; // red, gets swapped every round
        spawnfunc_info_player_deathmatch(this);
@@ -337,7 +283,7 @@ spawnfunc(info_player_attacker)
 
 spawnfunc(info_player_defender)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.team = NUM_TEAM_2; // blue, gets swapped every round
        spawnfunc_info_player_deathmatch(this);
@@ -345,9 +291,10 @@ spawnfunc(info_player_defender)
 
 spawnfunc(target_objective)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.classname = "target_objective";
+       IL_PUSH(g_assault_objectives, this);
        this.use = assault_objective_use;
        this.reset = assault_objective_reset;
        this.reset(this);
@@ -356,9 +303,10 @@ spawnfunc(target_objective)
 
 spawnfunc(target_objective_decrease)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.classname = "target_objective_decrease";
+       IL_PUSH(g_assault_objectivedecreasers, this);
 
        if(!this.dmg)
                this.dmg = 101;
@@ -375,10 +323,11 @@ spawnfunc(target_objective_decrease)
 spawnfunc(func_breakable);
 spawnfunc(func_assault_destructible)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.spawnflags = 3;
        this.classname = "func_assault_destructible";
+       IL_PUSH(g_assault_destructibles, this);
 
        if(assault_attacker_team == NUM_TEAM_1)
                this.team = NUM_TEAM_2;
@@ -390,7 +339,7 @@ spawnfunc(func_assault_destructible)
 
 spawnfunc(func_assault_wall)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.classname = "func_assault_wall";
        this.mdl = this.model;
@@ -403,7 +352,7 @@ spawnfunc(func_assault_wall)
 
 spawnfunc(target_assault_roundend)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        this.winning = 0; // round not yet won by attackers
        this.classname = "target_assault_roundend";
@@ -414,7 +363,7 @@ spawnfunc(target_assault_roundend)
 
 spawnfunc(target_assault_roundstart)
 {
-       if (!g_assault) { remove(this); return; }
+       if (!g_assault) { delete(this); return; }
 
        assault_attacker_team = NUM_TEAM_1;
        this.classname = "target_assault_roundstart";
@@ -426,67 +375,46 @@ spawnfunc(target_assault_roundstart)
 // legacy bot code
 void havocbot_goalrating_ast_targets(entity this, float ratingscale)
 {
-       entity ad, best, wp, tod;
-       float radius, found, bestvalue;
-       vector p;
-
-       ad = findchain(classname, "func_assault_destructible");
-
-       for (; ad; ad = ad.chain)
+       IL_EACH(g_assault_destructibles, it.bot_attack,
        {
-               if (ad.target == "")
-                       continue;
-
-               if (!ad.bot_attack)
+               if (it.target == "")
                        continue;
 
-               found = false;
-               for(tod = NULL; (tod = find(tod, targetname, ad.target)); )
+               bool found = false;
+               entity destr = it;
+               IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target,
                {
-                       if(tod.classname == "target_objective_decrease")
+                       if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE)
                        {
-                               if(tod.enemy.health > 0 && tod.enemy.health < ASSAULT_VALUE_INACTIVE)
-                               {
-                               //      dprint(etos(ad),"\n");
-                                       found = true;
-                                       break;
-                               }
+                               found = true;
+                               break;
                        }
-               }
+               });
 
                if(!found)
-               {
-               ///     dprint("target not found\n");
                        continue;
-               }
-               /// dprint("target #", etos(ad), " found\n");
 
-
-               p = 0.5 * (ad.absmin + ad.absmax);
-       //      dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n");
-       //      te_knightspike(p);
-       //      te_lightning2(NULL, '0 0 0', p);
+               vector p = 0.5 * (it.absmin + it.absmax);
 
                // Find and rate waypoints around it
                found = false;
-               best = NULL;
-               bestvalue = 99999999999;
-               for(radius=0; radius<1500 && !found; radius+=500)
+               entity best = NULL;
+               float bestvalue = 99999999999;
+               entity des = it;
+               for(float radius = 0; radius < 1500 && !found; radius += 500)
                {
-                       for(wp=findradius(p, radius); wp; wp=wp.chain)
+                       FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
                        {
-                               if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
-                               if(wp.classname=="waypoint")
-                               if(checkpvs(wp.origin, ad))
+                               if(checkpvs(it.origin, des))
                                {
                                        found = true;
-                                       if(wp.cnt<bestvalue)
+                                       if(it.cnt < bestvalue)
                                        {
-                                               best = wp;
-                                               bestvalue = wp.cnt;
+                                               best = it;
+                                               bestvalue = it.cnt;
                                        }
                                }
-                       }
+                       });
                }
 
                if(best)
@@ -500,14 +428,14 @@ void havocbot_goalrating_ast_targets(entity this, float ratingscale)
 
                        this.havocbot_attack_time = 0;
 
-                       if(checkpvs(this.view_ofs,ad))
+                       if(checkpvs(this.view_ofs,it))
                        if(checkpvs(this.view_ofs,best))
                        {
                        //      dprint("increasing attack time for this target\n");
                                this.havocbot_attack_time = time + 2;
                        }
                }
-       }
+       });
 }
 
 void havocbot_role_ast_offense(entity this)
@@ -625,7 +553,7 @@ MUTATOR_HOOKFUNCTION(as, TurretSpawn)
                turret.team = 5; // this gets reversed when match starts?
 }
 
-MUTATOR_HOOKFUNCTION(as, VehicleSpawn)
+MUTATOR_HOOKFUNCTION(as, VehicleInit)
 {
        entity veh = M_ARGV(0, entity);
 
@@ -647,7 +575,7 @@ MUTATOR_HOOKFUNCTION(as, PlayHitsound)
        return (frag_victim.classname == "func_assault_destructible");
 }
 
-MUTATOR_HOOKFUNCTION(as, GetTeamCount)
+MUTATOR_HOOKFUNCTION(as, CheckAllowedTeams)
 {
        // assault always has 2 teams
        c1 = c2 = 0;
@@ -680,13 +608,21 @@ MUTATOR_HOOKFUNCTION(as, OnEntityPreSpawn)
        }
 }
 
+MUTATOR_HOOKFUNCTION(as, ReadyRestart_Deny)
+{
+       // readyrestart not supported (yet)
+       return true;
+}
+
 // scoreboard setup
 void assault_ScoreRules()
 {
-       ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true);
+       int teams = 0;
+       teams |= BIT(0);
+       teams |= BIT(1); // always red vs blue
+
+       ScoreRules_basics(teams, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true);
        ScoreInfo_SetLabel_TeamScore(  ST_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
        ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES,    "objectives",      SFL_SORT_PRIO_PRIMARY);
        ScoreRules_basics_end();
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..8fd40748543bbcdf5a708845720791f6e6ac5f37 100644 (file)
@@ -1,3 +1,62 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+void assault_ScoreRules();
+
+REGISTER_MUTATOR(as, false)
+{
+       ActivateTeamplay();
+       have_team_spawns = -1; // request team spawns
+
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               assault_ScoreRules();
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back assault_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+// sprites
+.entity assault_decreaser;
+.entity assault_sprite;
+
+// legacy bot defs
+const int HAVOCBOT_AST_ROLE_NONE = 0;
+const int HAVOCBOT_AST_ROLE_DEFENSE = 2;
+const int HAVOCBOT_AST_ROLE_OFFENSE = 4;
+
+.int havocbot_role_flags;
+.float havocbot_attack_time;
+
+.void(entity this) havocbot_role;
+.void(entity this) havocbot_previous_role;
+
+void(entity this) havocbot_role_ast_defense;
+void(entity this) havocbot_role_ast_offense;
+
+void(entity bot) havocbot_ast_reset_role;
+
+void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
+
+// scoreboard stuff
+const float ST_ASSAULT_OBJECTIVES = 1;
+
+// predefined spawnfuncs
+void target_objective_decrease_activate(entity this);
index 901a04f2a72cd1889b22daa3916267a2693b33a4..561a30d222a40b223d14d41a702403efba0f2a14 100644 (file)
@@ -1,71 +1,5 @@
 #include "gamemode_ca.qh"
-#ifndef GAMEMODE_CA_H
-#define GAMEMODE_CA_H
 
-int autocvar_g_ca_point_limit;
-int autocvar_g_ca_point_leadlimit;
-float autocvar_g_ca_round_timelimit;
-bool autocvar_g_ca_team_spawns;
-int autocvar_g_ca_teams;
-int autocvar_g_ca_teams_override;
-float autocvar_g_ca_warmup;
-
-
-int ca_teams;
-bool allowed_to_spawn;
-
-const int ST_CA_ROUNDS = 1;
-
-bool CA_CheckTeams();
-bool CA_CheckWinner();
-void CA_RoundStart();
-bool ca_isEliminated(entity e);
-
-void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override);
-
-REGISTER_MUTATOR(ca, false)
-{
-       MUTATOR_ONADD
-       {
-               // game loads at time 1
-               if (time > 1) error("This is a game type and it cannot be added at runtime.");
-
-               allowed_to_spawn = true;
-
-               ca_teams = autocvar_g_ca_teams_override;
-               if (ca_teams < 2) ca_teams = autocvar_g_ca_teams;
-               ca_teams = bound(2, ca_teams, 4);
-
-        ScoreRules_basics(ca_teams, SFL_SORT_PRIO_PRIMARY, 0, true);
-        ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY);
-        ScoreRules_basics_end();
-
-               round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart);
-               round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
-
-               EliminatedPlayers_Init(ca_isEliminated);
-
-               ActivateTeamplay();
-               SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, autocvar_timelimit_override, -1);
-
-               if (autocvar_g_ca_team_spawns)
-                       have_team_spawns = -1; // request team spawns
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-// should be removed in the future, as other code should not have to care
-.float caplayer; // 0.5 if scheduled to join the next round
-#endif
-
-#ifdef IMPLEMENTATION
 float autocvar_g_ca_damage2score_multiplier;
 bool autocvar_g_ca_spectate_enemies;
 
@@ -117,7 +51,7 @@ float CA_GetWinnerTeam()
 void nades_Clear(entity player);
 
 #define CA_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
-#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
+#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == NumTeams(ca_teams))
 float CA_CheckWinner()
 {
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
@@ -179,9 +113,15 @@ bool CA_CheckTeams()
                prev_missing_teams_mask = -1;
                return false;
        }
-       int missing_teams_mask = (!redalive) + (!bluealive) * 2;
-       if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
-       if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+       int missing_teams_mask = 0;
+       if(ca_teams & BIT(0))
+               missing_teams_mask += (!redalive) * 1;
+       if(ca_teams & BIT(1))
+               missing_teams_mask += (!bluealive) * 2;
+       if(ca_teams & BIT(2))
+               missing_teams_mask += (!yellowalive) * 4;
+       if(ca_teams & BIT(3))
+               missing_teams_mask += (!pinkalive) * 8;
        if(prev_missing_teams_mask != missing_teams_mask)
        {
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
@@ -226,6 +166,18 @@ MUTATOR_HOOKFUNCTION(ca, PlayerSpawn)
                eliminatedPlayers.SendFlags |= 1;
 }
 
+MUTATOR_HOOKFUNCTION(ca, ForbidSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       // spectators / observers that weren't playing can join; they are
+       // immediately forced to observe in the PutClientInServer hook
+       // this way they are put in a team and can play in the next round
+       if (!allowed_to_spawn && player.caplayer)
+               return true;
+       return false;
+}
+
 MUTATOR_HOOKFUNCTION(ca, PutClientInServer)
 {
        entity player = M_ARGV(0, entity);
@@ -254,9 +206,10 @@ MUTATOR_HOOKFUNCTION(ca, reset_map_players)
                {
                        TRANSMUTE(Player, it);
                        it.caplayer = 1;
-                       WITHSELF(it, PutClientInServer());
+                       PutClientInServer(it);
                }
        });
+       bot_relinkplayerlist();
        return true;
 }
 
@@ -274,7 +227,7 @@ MUTATOR_HOOKFUNCTION(ca, reset_map_global)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ca, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(ca, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(0, float) = ca_teams;
 }
@@ -307,12 +260,14 @@ void ca_LastPlayerForTeam_Notify(entity this)
 MUTATOR_HOOKFUNCTION(ca, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        ca_LastPlayerForTeam_Notify(frag_target);
        if (!allowed_to_spawn)
                frag_target.respawn_flags =  RESPAWN_SILENT;
        if (!warmup_stage)
                eliminatedPlayers.SendFlags |= 1;
+       if(IS_BOT_CLIENT(frag_target))
+               bot_clear(frag_target);
        return true;
 }
 
@@ -325,23 +280,20 @@ MUTATOR_HOOKFUNCTION(ca, ClientDisconnect)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ca, ForbidPlayerScore_Clear)
-{
-       return true;
-}
-
 MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver)
 {
     entity player = M_ARGV(0, entity);
 
        if (!IS_DEAD(player))
                ca_LastPlayerForTeam_Notify(player);
-       if (player.killindicator_teamchange == -2)
+       if (player.killindicator_teamchange == -2) // player wants to spectate
                player.caplayer = 0;
        if (player.caplayer)
                player.frags = FRAGS_LMS_LOSER;
        if (!warmup_stage)
                eliminatedPlayers.SendFlags |= 1;
+       if (!player.caplayer)
+               return false;  // allow team reset
        return true;  // prevent team reset
 }
 
@@ -439,11 +391,11 @@ MUTATOR_HOOKFUNCTION(ca, SpectateSet)
 MUTATOR_HOOKFUNCTION(ca, SpectateNext)
 {
     entity client = M_ARGV(0, entity);
-    entity targ = M_ARGV(1, entity);
 
        if (!autocvar_g_ca_spectate_enemies && client.caplayer)
        {
-               targ = CA_SpectateNext(client, targ);
+               entity targ = M_ARGV(1, entity);
+               M_ARGV(1, entity) = CA_SpectateNext(client, targ);
                return true;
        }
 }
@@ -468,6 +420,8 @@ MUTATOR_HOOKFUNCTION(ca, SpectatePrev)
                }
        }
 
+       M_ARGV(1, entity) = targ;
+
        return MUT_SPECPREV_FOUND;
 }
 
@@ -513,5 +467,3 @@ MUTATOR_HOOKFUNCTION(ca, SetWeaponArena)
        // most weapons arena
        if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") M_ARGV(0, string) = "most";
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..5aba748fd5348207e81b7dacdbde39288fbde814 100644 (file)
@@ -1,3 +1,74 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+int autocvar_g_ca_point_limit;
+int autocvar_g_ca_point_leadlimit;
+float autocvar_g_ca_round_timelimit;
+bool autocvar_g_ca_team_spawns;
+//int autocvar_g_ca_teams;
+int autocvar_g_ca_teams_override;
+float autocvar_g_ca_warmup;
+
+
+int ca_teams;
+bool allowed_to_spawn;
+
+const int ST_CA_ROUNDS = 1;
+
+bool CA_CheckTeams();
+bool CA_CheckWinner();
+void CA_RoundStart();
+bool ca_isEliminated(entity e);
+
+void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override);
+
+REGISTER_MUTATOR(ca, false)
+{
+       MUTATOR_ONADD
+       {
+               // game loads at time 1
+               if (time > 1) error("This is a game type and it cannot be added at runtime.");
+
+               allowed_to_spawn = true;
+
+               ca_teams = autocvar_g_ca_teams_override;
+               if (ca_teams < 2)
+                       ca_teams = cvar("g_ca_teams"); // read the cvar directly as it gets written earlier in the same frame
+               ca_teams = bound(2, ca_teams, 4);
+
+               int teams = 0;
+               if(ca_teams >= 1) teams |= BIT(0);
+               if(ca_teams >= 2) teams |= BIT(1);
+               if(ca_teams >= 3) teams |= BIT(2);
+               if(ca_teams >= 4) teams |= BIT(3);
+
+               ca_teams = teams; // now set it?
+
+        ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
+        ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY);
+        ScoreRules_basics_end();
+
+               round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart);
+               round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+
+               EliminatedPlayers_Init(ca_isEliminated);
+
+               ActivateTeamplay();
+               SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, autocvar_timelimit_override, -1);
+
+               if (autocvar_g_ca_team_spawns)
+                       have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+// should be removed in the future, as other code should not have to care
+.float caplayer; // 0.5 if scheduled to join the next round
index f9872e7490ed45b665b718490ee42f63dbaa7a07..d1c97bd2856f90d2e227f1deb65f1e314a676227 100644 (file)
@@ -1,6 +1,5 @@
 #include "gamemode_ctf.qh"
 
-#ifdef IMPLEMENTATION
 #ifndef CSQC
 void ctf_Initialize();
 
@@ -136,10 +135,14 @@ void ctf_CaptureRecord(entity flag, entity player)
        string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
 
        // notify about shit
-       if(ctf_oneflag) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); }
-       else if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_TIME), player.netname, (cap_time * 100)); }
-       else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
-       else { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
+       if(ctf_oneflag)
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname);
+       else if(!ctf_captimerecord)
+               Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_TIME), player.netname, (cap_time * 100));
+       else if(cap_time < cap_record)
+               Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100));
+       else
+               Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100));
 
        // write that shit in the database
        if(!ctf_oneflag) // but not in 1-flag mode
@@ -152,12 +155,49 @@ void ctf_CaptureRecord(entity flag, entity player)
        }
 }
 
+bool ctf_Immediate_Return_Allowed(entity flag, entity toucher)
+{
+       int num_perteam = 0;
+       FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(toucher, it), { ++num_perteam; });
+
+       // automatically return if there's only 1 player on the team
+       return ((autocvar_g_ctf_flag_return || num_perteam <= 1 || (autocvar_g_ctf_flag_return_carrying && toucher.flagcarried))
+               && flag.team);
+}
+
+bool ctf_Return_Customize(entity this, entity client)
+{
+       // only to the carrier
+       return boolean(client == this.owner);
+}
+
 void ctf_FlagcarrierWaypoints(entity player)
 {
        WaypointSprite_Spawn(WP_FlagCarrier, 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, player.team, player, wps_flagcarrier, true, RADARICON_FLAG);
        WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2);
        WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id));
        WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team));
+
+       if(player.flagcarried && CTF_SAMETEAM(player, player.flagcarried))
+       {
+               if(!player.wps_enemyflagcarrier)
+               {
+                       entity wp = WaypointSprite_Spawn(((ctf_oneflag) ? WP_FlagCarrier : WP_FlagCarrierEnemy), 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, 0, player, wps_enemyflagcarrier, true, RADARICON_FLAG);
+                       wp.colormod = WPCOLOR_ENEMYFC(player.team);
+                       setcefc(wp, ctf_Stalemate_Customize);
+
+                       if(IS_REAL_CLIENT(player) && !ctf_stalemate)
+                               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_VISIBLE);
+               }
+
+               if(!player.wps_flagreturn)
+               {
+                       entity owp = WaypointSprite_SpawnFixed(WP_FlagReturn, player.flagcarried.ctf_spawnorigin + FLAG_WAYPOINT_OFFSET, player, wps_flagreturn, RADARICON_FLAG);
+                       owp.colormod = '0 0.8 0.8';
+                       //WaypointSprite_UpdateTeamRadar(player.wps_flagreturn, RADARICON_FLAG, ((player.team) ? colormapPaletteColor(player.team - 1, false) : '1 1 1'));
+                       setcefc(owp, ctf_Return_Customize);
+               }
+       }
 }
 
 void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnrate)
@@ -233,7 +273,7 @@ bool ctf_CaptureShield_CheckStatus(entity p)
        if(ctf_captureshield_max_ratio <= 0)
                return false;
 
-       s = PlayerScore_Add(p, SP_CTF_CAPS, 0);
+       s  = PlayerScore_Add(p, SP_CTF_CAPS,    0);
        s2 = PlayerScore_Add(p, SP_CTF_PICKUPS, 0);
        s3 = PlayerScore_Add(p, SP_CTF_RETURNS, 0);
        s4 = PlayerScore_Add(p, SP_CTF_FCKILLS, 0);
@@ -247,7 +287,7 @@ bool ctf_CaptureShield_CheckStatus(entity p)
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                if(DIFF_TEAM(it, p))
                        continue;
-               se = PlayerScore_Add(it, SP_CTF_CAPS, 0);
+               se  = PlayerScore_Add(it, SP_CTF_CAPS,    0);
                se2 = PlayerScore_Add(it, SP_CTF_PICKUPS, 0);
                se3 = PlayerScore_Add(it, SP_CTF_RETURNS, 0);
                se4 = PlayerScore_Add(it, SP_CTF_FCKILLS, 0);
@@ -278,24 +318,24 @@ void ctf_CaptureShield_Update(entity player, bool wanted_status)
        }
 }
 
-bool ctf_CaptureShield_Customize(entity this)
+bool ctf_CaptureShield_Customize(entity this, entity client)
 {
-       if(!other.ctf_captureshielded) { return false; }
-       if(CTF_SAMETEAM(this, other)) { return false; }
+       if(!client.ctf_captureshielded) { return false; }
+       if(CTF_SAMETEAM(this, client)) { return false; }
 
        return true;
 }
 
-void ctf_CaptureShield_Touch(entity this)
+void ctf_CaptureShield_Touch(entity this, entity toucher)
 {
-       if(!other.ctf_captureshielded) { return; }
-       if(CTF_SAMETEAM(this, other)) { return; }
+       if(!toucher.ctf_captureshielded) { return; }
+       if(CTF_SAMETEAM(this, toucher)) { return; }
 
        vector mymid = (this.absmin + this.absmax) * 0.5;
-       vector othermid = (other.absmin + other.absmax) * 0.5;
+       vector theirmid = (toucher.absmin + toucher.absmax) * 0.5;
 
-       Damage(other, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
-       if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); }
+       Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(theirmid - mymid) * ctf_captureshield_force);
+       if(IS_REAL_CLIENT(toucher)) { Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); }
 }
 
 void ctf_CaptureShield_Spawn(entity flag)
@@ -307,7 +347,7 @@ void ctf_CaptureShield_Spawn(entity flag)
        settouch(shield, ctf_CaptureShield_Touch);
        setcefc(shield, ctf_CaptureShield_Customize);
        shield.effects = EF_ADDITIVE;
-       shield.movetype = MOVETYPE_NOCLIP;
+       set_movetype(shield, MOVETYPE_NOCLIP);
        shield.solid = SOLID_TRIGGER;
        shield.avelocity = '7 0 11';
        shield.scale = 0.5;
@@ -328,7 +368,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype)
        player = (player ? player : flag.pass_sender);
 
        // main
-       flag.movetype = MOVETYPE_TOSS;
+       set_movetype(flag, MOVETYPE_TOSS);
        flag.takedamage = DAMAGE_YES;
        flag.angles = '0 0 0';
        flag.health = flag.max_flag_health;
@@ -337,7 +377,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype)
        flag.ctf_status = FLAG_DROPPED;
 
        // messages and sounds
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_LOST) : INFO_CTF_LOST_NEUTRAL), player.netname);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_LOST), player.netname);
        _sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("dropped", player.team, player);
 
@@ -387,7 +427,7 @@ void ctf_Handle_Retrieve(entity flag, entity player)
                setattachment(flag, player, "");
                setorigin(flag, FLAG_CARRY_OFFSET);
        }
-       flag.movetype = MOVETYPE_NONE;
+       set_movetype(flag, MOVETYPE_NONE);
        flag.takedamage = DAMAGE_NO;
        flag.solid = SOLID_NOT;
        flag.angles = '0 0 0';
@@ -399,11 +439,11 @@ void ctf_Handle_Retrieve(entity flag, entity player)
 
        FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA(
                if(it == sender)
-                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_SENT) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname);
+                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_SENT), player.netname);
                else if(it == player)
-                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_RECEIVED) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname);
+                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_RECEIVED), sender.netname);
                else if(SAME_TEAM(it, sender))
-                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_OTHER) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname);
+                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_OTHER), sender.netname, player.netname);
        ));
 
        // create new waypoint
@@ -453,7 +493,7 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype)
                        ctf_CalculatePassVelocity(flag, targ_origin, player.origin, false);
 
                        // main
-                       flag.movetype = MOVETYPE_FLY;
+                       set_movetype(flag, MOVETYPE_FLY);
                        flag.takedamage = DAMAGE_NO;
                        flag.pass_sender = player;
                        flag.pass_target = receiver;
@@ -498,6 +538,9 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype)
        if(player.wps_enemyflagcarrier)
                WaypointSprite_Kill(player.wps_enemyflagcarrier);
 
+       if(player.wps_flagreturn)
+               WaypointSprite_Kill(player.wps_flagreturn);
+
        // captureshield
        ctf_CaptureShield_Update(player, 0); // shield player from picking up flag
 }
@@ -537,7 +580,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype)
        player.throw_count = 0;
 
        // messages and sounds
-       Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT(enemy_flag, CENTER_CTF_CAPTURE) : CENTER_CTF_CAPTURE_NEUTRAL));
+       Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_NUM(enemy_flag.team, CENTER_CTF_CAPTURE));
        ctf_CaptureRecord(enemy_flag, player);
        _sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE);
 
@@ -581,12 +624,12 @@ void ctf_Handle_Return(entity flag, entity player)
        // messages and sounds
        if(IS_MONSTER(player))
        {
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN_MONSTER), player.monster_name);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN_MONSTER), player.monster_name);
        }
        else if(flag.team)
        {
-               Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT(flag, CENTER_CTF_RETURN));
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN), player.netname);
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_RETURN));
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN), player.netname);
        }
        _sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("return", flag.team, player);
@@ -638,7 +681,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
        }
 
        // flag setup
-       flag.movetype = MOVETYPE_NONE;
+       set_movetype(flag, MOVETYPE_NONE);
        flag.takedamage = DAMAGE_NO;
        flag.solid = SOLID_NOT;
        flag.angles = '0 0 0';
@@ -652,13 +695,17 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
        }
 
        // messages and sounds
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_PICKUP) : INFO_CTF_PICKUP_NEUTRAL), player.netname);
-       if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); }
-       if(!flag.team) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); }
-       else if(CTF_DIFFTEAM(player, flag)) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT(flag, CENTER_CTF_PICKUP)); }
-       else { Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); }
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_PICKUP), player.netname);
+       if(ctf_stalemate)
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER);
+       if(!flag.team)
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL);
+       else if(CTF_DIFFTEAM(player, flag))
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_PICKUP));
+       else
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team));
 
-       Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname);
+       Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
 
        if(!flag.team)
                FOREACH_CLIENT(IS_PLAYER(it) && it != player && DIFF_TEAM(it, player), LAMBDA(Send_Notification(NOTIF_ONE, it, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname)));
@@ -667,7 +714,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
                FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA(
                        if(CTF_SAMETEAM(flag, it))
                        if(SAME_TEAM(player, it))
-                               Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
+                               Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
                        else
                                Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname);
                ));
@@ -690,7 +737,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
                {
                        pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1);
                        pickup_dropped_score = floor((autocvar_g_ctf_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_ctf_score_pickup_dropped_early * pickup_dropped_score) + 0.5);
-                       LOG_TRACE("pickup_dropped_score is ", ftos(pickup_dropped_score), "\n");
+                       LOG_TRACE("pickup_dropped_score is ", ftos(pickup_dropped_score));
                        PlayerTeamScore_AddScore(player, pickup_dropped_score);
                        ctf_EventLog("pickup", flag.team, player);
                        break;
@@ -731,14 +778,17 @@ void ctf_CheckFlagReturn(entity flag, int returntype)
                {
                        switch(returntype)
                        {
-                               case RETURN_DROPPED: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DROPPED) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break;
-                               case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DAMAGED) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break;
-                               case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_SPEEDRUN) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break;
-                               case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_NEEDKILL) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break;
-
+                               case RETURN_DROPPED:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DROPPED)); break;
+                               case RETURN_DAMAGE:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DAMAGED)); break;
+                               case RETURN_SPEEDRUN:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_SPEEDRUN), ctf_captimerecord); break;
+                               case RETURN_NEEDKILL:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_NEEDKILL)); break;
                                default:
                                case RETURN_TIMEOUT:
-                                       { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_TIMEOUT) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; }
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_TIMEOUT)); break;
                        }
                        _sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE);
                        ctf_EventLog("returned", flag.team, NULL);
@@ -747,15 +797,14 @@ void ctf_CheckFlagReturn(entity flag, int returntype)
        }
 }
 
-bool ctf_Stalemate_Customize(entity this)
+bool ctf_Stalemate_Customize(entity this, entity client)
 {
        // make spectators see what the player would see
-       entity e, wp_owner;
-       e = WaypointSprite_getviewentity(other);
-       wp_owner = this.owner;
+       entity e = WaypointSprite_getviewentity(client);
+       entity wp_owner = this.owner;
 
        // team waypoints
-       if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; }
+       //if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; }
        if(SAME_TEAM(wp_owner, e)) { return false; }
        if(!IS_PLAYER(e)) { return false; }
 
@@ -832,9 +881,7 @@ void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage
        if(ITEM_DAMAGE_NEEDKILL(deathtype))
        {
                if(autocvar_g_ctf_flag_return_damage_delay)
-               {
-                       this.ctf_flagdamaged = true;
-               }
+                       this.ctf_flagdamaged_byworld = true;
                else
                {
                        this.health = 0;
@@ -864,20 +911,10 @@ void ctf_FlagThink(entity this)
 
        // sanity checks
        if(this.mins != CTF_FLAG.m_mins || this.maxs != CTF_FLAG.m_maxs) { // reset the flag boundaries in case it got squished
-               LOG_TRACE("wtf the flag got squashed?\n");
+               LOG_TRACE("wtf the flag got squashed?");
                tracebox(this.origin, CTF_FLAG.m_mins, CTF_FLAG.m_maxs, this.origin, MOVE_NOMONSTERS, this);
                if(!trace_startsolid || this.noalign) // can we resize it without getting stuck?
-                       setsize(this, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); }
-
-       switch(this.ctf_status) // reset flag angles in case warpzones adjust it
-       {
-               case FLAG_DROPPED:
-               {
-                       this.angles = '0 0 0';
-                       break;
-               }
-
-               default: break;
+                       setsize(this, CTF_FLAG.m_mins, CTF_FLAG.m_maxs);
        }
 
        // main think method
@@ -898,6 +935,8 @@ void ctf_FlagThink(entity this)
 
                case FLAG_DROPPED:
                {
+                       this.angles = '0 0 0'; // reset flag angles in case warpzones adjust it
+
                        if(autocvar_g_ctf_flag_dropped_floatinwater)
                        {
                                vector midpoint = ((this.absmin + this.absmax) * 0.5);
@@ -908,9 +947,9 @@ void ctf_FlagThink(entity this)
                                        if(pointcontents(midpoint + FLAG_FLOAT_OFFSET) == CONTENT_WATER)
                                                { this.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; }
                                        else
-                                               { this.movetype = MOVETYPE_FLY; }
+                                               { set_movetype(this, MOVETYPE_FLY); }
                                }
-                               else if(this.movetype == MOVETYPE_FLY) { this.movetype = MOVETYPE_TOSS; }
+                               else if(this.move_movetype == MOVETYPE_FLY) { set_movetype(this, MOVETYPE_TOSS); }
                        }
                        if(autocvar_g_ctf_flag_return_dropped)
                        {
@@ -921,7 +960,7 @@ void ctf_FlagThink(entity this)
                                        return;
                                }
                        }
-                       if(this.ctf_flagdamaged)
+                       if(this.ctf_flagdamaged_byworld)
                        {
                                this.health -= ((this.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE);
                                ctf_CheckFlagReturn(this, RETURN_NEEDKILL);
@@ -973,7 +1012,7 @@ void ctf_FlagThink(entity this)
                        if((this.pass_target == NULL)
                                || (IS_DEAD(this.pass_target))
                                || (this.pass_target.flagcarried)
-                               || (vdist(this.origin - targ_origin, <, autocvar_g_ctf_pass_radius))
+                               || (vdist(this.origin - targ_origin, >, autocvar_g_ctf_pass_radius))
                                || ((trace_fraction < 1) && (trace_ent != this.pass_target))
                                || (time > this.ctf_droptime + autocvar_g_ctf_pass_timelimit))
                        {
@@ -990,7 +1029,7 @@ void ctf_FlagThink(entity this)
 
                default: // this should never happen
                {
-                       LOG_TRACE("ctf_FlagThink(): Flag exists with no status?\n");
+                       LOG_TRACE("ctf_FlagThink(): Flag exists with no status?");
                        return;
                }
        }
@@ -1012,12 +1051,9 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher))
                        flag.health = 0;
                        ctf_CheckFlagReturn(flag, RETURN_NEEDKILL);
                }
-               if(!flag.ctf_flagdamaged) { return; }
+               if(!flag.ctf_flagdamaged_byworld) { return; }
        }
 
-       int num_perteam = 0;
-       FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(toucher, it), LAMBDA(++num_perteam));
-
        // special touch behaviors
        if(STAT(FROZEN, toucher)) { return; }
        else if(IS_VEHICLE(toucher))
@@ -1069,7 +1105,7 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher))
 
                case FLAG_DROPPED:
                {
-                       if(CTF_SAMETEAM(toucher, flag) && (autocvar_g_ctf_flag_return || num_perteam <= 1 || (autocvar_g_ctf_flag_return_carrying && toucher.flagcarried)) && flag.team) // automatically return if there's only 1 player on the team
+                       if(CTF_SAMETEAM(toucher, flag) && ctf_Immediate_Return_Allowed(flag, toucher))
                                ctf_Handle_Return(flag, toucher); // toucher just returned his own flag
                        else if(is_not_monster && (!toucher.flagcarried) && ((toucher != flag.ctf_dropper) || (time > flag.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
                                ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
@@ -1078,7 +1114,7 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher))
 
                case FLAG_CARRY:
                {
-                       LOG_TRACE("Someone touched a flag even though it was being carried?\n");
+                       LOG_TRACE("Someone touched a flag even though it was being carried?");
                        break;
                }
 
@@ -1087,7 +1123,12 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher))
                        if((IS_PLAYER(toucher)) && !IS_DEAD(toucher) && (toucher != flag.pass_sender))
                        {
                                if(DIFF_TEAM(toucher, flag.pass_sender))
-                                       ctf_Handle_Return(flag, toucher);
+                               {
+                                       if(ctf_Immediate_Return_Allowed(flag, toucher))
+                                               ctf_Handle_Return(flag, toucher);
+                                       else if(is_not_monster && (!toucher.flagcarried))
+                                               ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED);
+                               }
                                else
                                        ctf_Handle_Retrieve(flag, toucher);
                        }
@@ -1109,6 +1150,7 @@ void ctf_RespawnFlag(entity flag)
        if((flag.owner) && (flag.owner.flagcarried == flag))
        {
                WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
+               WaypointSprite_Kill(flag.owner.wps_flagreturn);
                WaypointSprite_Kill(flag.wps_flagcarrier);
 
                flag.owner.flagcarried = NULL;
@@ -1127,7 +1169,7 @@ void ctf_RespawnFlag(entity flag)
        setattachment(flag, NULL, "");
        setorigin(flag, flag.ctf_spawnorigin);
 
-       flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS);
+       set_movetype(flag, ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS));
        flag.takedamage = DAMAGE_NO;
        flag.health = flag.max_flag_health;
        flag.solid = SOLID_TRIGGER;
@@ -1143,7 +1185,7 @@ void ctf_RespawnFlag(entity flag)
        flag.ctf_dropper = NULL;
        flag.ctf_pickuptime = 0;
        flag.ctf_droptime = 0;
-       flag.ctf_flagdamaged = 0;
+       flag.ctf_flagdamaged_byworld = false;
 
        ctf_CheckStalemate();
 }
@@ -1156,6 +1198,13 @@ void ctf_Reset(entity this)
        ctf_RespawnFlag(this);
 }
 
+bool ctf_FlagBase_Customize(entity this, entity client)
+{
+       if(client.flagcarried && CTF_SAMETEAM(client, client.flagcarried))
+               return false;
+       return true;
+}
+
 void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map by ctf_FlagSetup()
 {
        // bot waypoints
@@ -1177,6 +1226,7 @@ void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map
        entity wp = WaypointSprite_SpawnFixed(basename, this.origin + FLAG_WAYPOINT_OFFSET, this, wps_flagbase, RADARICON_FLAG);
        wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 1 1');
        WaypointSprite_UpdateTeamRadar(this.wps_flagbase, RADARICON_FLAG, ((this.team) ? colormapPaletteColor(this.team - 1, false) : '1 1 1'));
+       setcefc(wp, ctf_FlagBase_Customize);
 
        // captureshield setup
        ctf_CaptureShield_Spawn(this);
@@ -1197,6 +1247,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e
        flag.classname = "item_flag_team";
        flag.target = "###item###"; // wut?
        flag.flags = FL_ITEM | FL_NOTARGET;
+       IL_PUSH(g_items, flag);
        flag.solid = SOLID_TRIGGER;
        flag.takedamage = DAMAGE_NO;
        flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale;
@@ -1208,6 +1259,8 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e
        flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP;
        flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable;
        flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable;
+       if(flag.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, flag);
        flag.velocity = '0 0 0';
        flag.mangle = flag.angles;
        flag.reset = ctf_Reset;
@@ -1280,13 +1333,13 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e
        {
                flag.dropped_origin = flag.origin;
                flag.noalign = true;
-               flag.movetype = MOVETYPE_NONE;
+               set_movetype(flag, MOVETYPE_NONE);
        }
        else // drop to floor, automatically find a platform and set that as spawn origin
        {
                flag.noalign = false;
                droptofloor(flag);
-               flag.movetype = MOVETYPE_TOSS;
+               set_movetype(flag, MOVETYPE_NONE);
        }
 
        InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION);
@@ -1490,23 +1543,19 @@ void havocbot_goalrating_ctf_droppedflags(entity this, float ratingscale, vector
 
 void havocbot_goalrating_ctf_carrieritems(entity this, float ratingscale, vector org, float sradius)
 {
-       entity head;
-       float t;
-       head = findchainfloat(bot_pickup, true);
-       while (head)
+       IL_EACH(g_items, it.bot_pickup,
        {
                // gather health and armor only
-               if (head.solid)
-               if (head.health || head.armorvalue)
-               if (vdist(head.origin - org, <, sradius))
+               if (it.solid)
+               if (it.health || it.armorvalue)
+               if (vdist(it.origin - org, <, sradius))
                {
                        // get the value of the item
-                       t = head.bot_pickupevalfunc(this, head) * 0.0001;
+                       float t = it.bot_pickupevalfunc(this, it) * 0.0001;
                        if (t > 0)
-                               navigation_routerating(this, head, t * ratingscale, 500);
+                               navigation_routerating(this, it, t * ratingscale, 500);
                }
-               head = head.chain;
-       }
+       });
 }
 
 void havocbot_ctf_reset_role(entity this)
@@ -1776,6 +1825,11 @@ void havocbot_role_ctf_retriever(entity this)
        mf = havocbot_ctf_find_flag(this);
        if(mf.ctf_status==FLAG_BASE)
        {
+               if(this.goalcurrent == mf)
+               {
+                       navigation_clearroute(this);
+                       this.bot_strategytime = 0;
+               }
                havocbot_ctf_reset_role(this);
                return;
        }
@@ -1927,47 +1981,47 @@ void havocbot_role_ctf_defense(entity this)
 
 void havocbot_role_ctf_setrole(entity bot, int role)
 {
-       LOG_TRACE(strcat(bot.netname," switched to "));
+       string s = "(null)";
        switch(role)
        {
                case HAVOCBOT_CTF_ROLE_CARRIER:
-                       LOG_TRACE("carrier");
+                       s = "carrier";
                        bot.havocbot_role = havocbot_role_ctf_carrier;
                        bot.havocbot_role_timeout = 0;
                        bot.havocbot_cantfindflag = time + 10;
                        bot.bot_strategytime = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_DEFENSE:
-                       LOG_TRACE("defense");
+                       s = "defense";
                        bot.havocbot_role = havocbot_role_ctf_defense;
                        bot.havocbot_role_timeout = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_MIDDLE:
-                       LOG_TRACE("middle");
+                       s = "middle";
                        bot.havocbot_role = havocbot_role_ctf_middle;
                        bot.havocbot_role_timeout = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_OFFENSE:
-                       LOG_TRACE("offense");
+                       s = "offense";
                        bot.havocbot_role = havocbot_role_ctf_offense;
                        bot.havocbot_role_timeout = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_RETRIEVER:
-                       LOG_TRACE("retriever");
+                       s = "retriever";
                        bot.havocbot_previous_role = bot.havocbot_role;
                        bot.havocbot_role = havocbot_role_ctf_retriever;
                        bot.havocbot_role_timeout = time + 10;
                        bot.bot_strategytime = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_ESCORT:
-                       LOG_TRACE("escort");
+                       s = "escort";
                        bot.havocbot_previous_role = bot.havocbot_role;
                        bot.havocbot_role = havocbot_role_ctf_escort;
                        bot.havocbot_role_timeout = time + 30;
                        bot.bot_strategytime = 0;
                        break;
        }
-       LOG_TRACE("\n");
+       LOG_TRACE(bot.netname, " switched to ", s);
 }
 
 
@@ -1987,7 +2041,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
                                                   | CTF_YELLOW_FLAG_CARRYING   | CTF_YELLOW_FLAG_TAKEN         | CTF_YELLOW_FLAG_LOST
                                                   | CTF_PINK_FLAG_CARRYING     | CTF_PINK_FLAG_TAKEN           | CTF_PINK_FLAG_LOST
                                                   | CTF_NEUTRAL_FLAG_CARRYING  | CTF_NEUTRAL_FLAG_TAKEN        | CTF_NEUTRAL_FLAG_LOST
-                                                  | CTF_FLAG_NEUTRAL | CTF_SHIELDED);
+                                                  | CTF_FLAG_NEUTRAL | CTF_SHIELDED | CTF_STALEMATE);
 
        // scan through all the flags and notify the client about them
        for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
@@ -2021,6 +2075,9 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
        if(player.ctf_captureshielded)
                player.ctf_flagstatus |= CTF_SHIELDED;
 
+       if(ctf_stalemate)
+               player.ctf_flagstatus |= CTF_STALEMATE;
+
        // update the health of the flag carrier waypointsprite
        if(player.wps_flagcarrier)
                WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id));
@@ -2238,14 +2295,13 @@ MUTATOR_HOOKFUNCTION(ctf, VehicleEnter)
 
        if(player.flagcarried)
        {
-               player.flagcarried.nodrawtoclient = player; // hide the flag from the driver
-
                if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch)
                {
                        ctf_Handle_Throw(player, NULL, DROP_NORMAL);
                }
                else
                {
+                       player.flagcarried.nodrawtoclient = player; // hide the flag from the driver
                        setattachment(player.flagcarried, veh, "");
                        setorigin(player.flagcarried, VEHICLE_FLAG_OFFSET);
                        player.flagcarried.scale = VEHICLE_FLAG_SCALE;
@@ -2276,7 +2332,7 @@ MUTATOR_HOOKFUNCTION(ctf, AbortSpeedrun)
 
        if(player.flagcarried)
        {
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((player.flagcarried.team) ? APP_TEAM_ENT(player.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL));
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(player.flagcarried.team, INFO_CTF_FLAGRETURN_ABORTRUN));
                ctf_RespawnFlag(player.flagcarried);
                return true;
        }
@@ -2294,7 +2350,7 @@ MUTATOR_HOOKFUNCTION(ctf, MatchEnd)
                        case FLAG_PASSING:
                        {
                                // lock the flag, game is over
-                               flag.movetype = MOVETYPE_NONE;
+                               set_movetype(flag, MOVETYPE_NONE);
                                flag.takedamage = DAMAGE_NO;
                                flag.solid = SOLID_NOT;
                                flag.nextthink = false; // stop thinking
@@ -2322,7 +2378,7 @@ MUTATOR_HOOKFUNCTION(ctf, HavocBot_ChooseRole)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ctf, GetTeamCount)
+MUTATOR_HOOKFUNCTION(ctf, CheckAllowedTeams)
 {
        //M_ARGV(0, float) = ctf_teams;
        M_ARGV(1, string) = "ctf_team";
@@ -2382,10 +2438,10 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand)
                {
                        switch(argv(1))
                        {
-                               case "red": _team = NUM_TEAM_1; break;
-                               case "blue": _team = NUM_TEAM_2; break;
-                               case "yellow": if(ctf_teams >= 3) _team = NUM_TEAM_3; break;
-                               case "pink": if(ctf_teams >= 4) _team = NUM_TEAM_4; break;
+                               case "red":    if(ctf_teams & BIT(0)) _team = NUM_TEAM_1; break;
+                               case "blue":   if(ctf_teams & BIT(1)) _team = NUM_TEAM_2; break;
+                               case "yellow": if(ctf_teams & BIT(2)) _team = NUM_TEAM_3; break;
+                               case "pink":   if(ctf_teams & BIT(3)) _team = NUM_TEAM_4; break;
                        }
                }
 
@@ -2408,7 +2464,7 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand)
 MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        if(frag_target.flagcarried)
                ctf_Handle_Throw(frag_target, NULL, DROP_THROW);
 }
@@ -2431,7 +2487,7 @@ Keys:
 "noise5" sound played when flag touches the ground... */
 spawnfunc(item_flag_team1)
 {
-       if(!g_ctf) { remove(this); return; }
+       if(!g_ctf) { delete(this); return; }
 
        ctf_FlagSetup(NUM_TEAM_1, this);
 }
@@ -2449,7 +2505,7 @@ Keys:
 "noise5" sound played when flag touches the ground... */
 spawnfunc(item_flag_team2)
 {
-       if(!g_ctf) { remove(this); return; }
+       if(!g_ctf) { delete(this); return; }
 
        ctf_FlagSetup(NUM_TEAM_2, this);
 }
@@ -2467,7 +2523,7 @@ Keys:
 "noise5" sound played when flag touches the ground... */
 spawnfunc(item_flag_team3)
 {
-       if(!g_ctf) { remove(this); return; }
+       if(!g_ctf) { delete(this); return; }
 
        ctf_FlagSetup(NUM_TEAM_3, this);
 }
@@ -2485,7 +2541,7 @@ Keys:
 "noise5" sound played when flag touches the ground... */
 spawnfunc(item_flag_team4)
 {
-       if(!g_ctf) { remove(this); return; }
+       if(!g_ctf) { delete(this); return; }
 
        ctf_FlagSetup(NUM_TEAM_4, this);
 }
@@ -2503,8 +2559,8 @@ Keys:
 "noise5" sound played when flag touches the ground... */
 spawnfunc(item_flag_neutral)
 {
-       if(!g_ctf) { remove(this); return; }
-       if(!cvar("g_ctf_oneflag")) { remove(this); return; }
+       if(!g_ctf) { delete(this); return; }
+       if(!cvar("g_ctf_oneflag")) { delete(this); return; }
 
        ctf_FlagSetup(0, this);
 }
@@ -2517,7 +2573,7 @@ Keys:
 "cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
 spawnfunc(ctf_team)
 {
-       if(!g_ctf) { remove(this); return; }
+       if(!g_ctf) { delete(this); return; }
 
        this.classname = "ctf_team";
        this.team = this.cnt + 1;
@@ -2536,6 +2592,14 @@ spawnfunc(team_CTF_bluespawn)  { spawnfunc_info_player_team2(this);  }
 spawnfunc(team_CTF_neutralflag)        { spawnfunc_item_flag_neutral(this);  }
 spawnfunc(team_neutralobelisk) { spawnfunc_item_flag_neutral(this);  }
 
+// compatibility for wop maps
+spawnfunc(team_redplayer)      { spawnfunc_info_player_team1(this);  }
+spawnfunc(team_blueplayer)     { spawnfunc_info_player_team2(this);  }
+spawnfunc(team_ctl_redlolly)   { spawnfunc_item_flag_team1(this);    }
+spawnfunc(team_CTL_redlolly)   { spawnfunc_item_flag_team1(this);    }
+spawnfunc(team_ctl_bluelolly)  { spawnfunc_item_flag_team2(this);    }
+spawnfunc(team_CTL_bluelolly)  { spawnfunc_item_flag_team2(this);    }
+
 
 // ==============
 // Initialization
@@ -2547,12 +2611,12 @@ void ctf_ScoreRules(int teams)
        CheckAllowedTeams(NULL);
        ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
        ScoreInfo_SetLabel_TeamScore  (ST_CTF_CAPS,     "caps",      SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME,  "captime",   SFL_LOWER_IS_BETTER | SFL_TIME);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS,  "pickups",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS,  "fckills",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS,  "returns",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS,    "drops",     SFL_LOWER_IS_BETTER);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,    "caps",      SFL_SORT_PRIO_SECONDARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME, "captime",   SFL_LOWER_IS_BETTER | SFL_TIME);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS, "pickups",   0);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS, "fckills",   0);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS, "returns",   0);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS,   "drops",     SFL_LOWER_IS_BETTER);
        ScoreRules_basics_end();
 }
 
@@ -2568,27 +2632,44 @@ void ctf_SpawnTeam (string teamname, int teamcolor)
 
 void ctf_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
 {
-       ctf_teams = 2;
+       ctf_teams = 0;
 
        entity tmp_entity;
        for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
        {
-               if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); }
-               if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); }
+               //if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); }
+               //if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); }
+
+               switch(tmp_entity.team)
+               {
+                       case NUM_TEAM_1: BITSET_ASSIGN(ctf_teams, BIT(0)); break;
+                       case NUM_TEAM_2: BITSET_ASSIGN(ctf_teams, BIT(1)); break;
+                       case NUM_TEAM_3: BITSET_ASSIGN(ctf_teams, BIT(2)); break;
+                       case NUM_TEAM_4: BITSET_ASSIGN(ctf_teams, BIT(3)); break;
+               }
                if(tmp_entity.team == 0) { ctf_oneflag = true; }
        }
 
-       ctf_teams = bound(2, ctf_teams, 4);
+       if(NumTeams(ctf_teams) < 2) // somehow, there's not enough flags!
+       {
+               ctf_teams = 0; // so set the default red and blue teams
+               BITSET_ASSIGN(ctf_teams, BIT(0));
+               BITSET_ASSIGN(ctf_teams, BIT(1));
+       }
+
+       //ctf_teams = bound(2, ctf_teams, 4);
 
        // if no teams are found, spawn defaults
        if(find(NULL, classname, "ctf_team") == NULL)
        {
-               LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway.\n");
-               ctf_SpawnTeam("Red", NUM_TEAM_1);
-               ctf_SpawnTeam("Blue", NUM_TEAM_2);
-               if(ctf_teams >= 3)
+               LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway.");
+               if(ctf_teams & BIT(0))
+                       ctf_SpawnTeam("Red", NUM_TEAM_1);
+               if(ctf_teams & BIT(1))
+                       ctf_SpawnTeam("Blue", NUM_TEAM_2);
+               if(ctf_teams & BIT(2))
                        ctf_SpawnTeam("Yellow", NUM_TEAM_3);
-               if(ctf_teams >= 4)
+               if(ctf_teams & BIT(3))
                        ctf_SpawnTeam("Pink", NUM_TEAM_4);
        }
 
@@ -2605,5 +2686,3 @@ void ctf_Initialize()
 
        InitializeEntity(NULL, ctf_DelayedInit, INITPRIO_GAMETYPE);
 }
-
-#endif
index 95e0bcd424ded55bc032f93b0706baf02eb4f882..593b0bec34d0930c54b47f9a31cf350e5427495e 100644 (file)
@@ -8,19 +8,13 @@ void ctf_RespawnFlag(entity flag);
 
 // score rule declarations
 const int ST_CTF_CAPS = 1;
-const int SP_CTF_CAPS = 4;
-const int SP_CTF_CAPTIME = 5;
-const int SP_CTF_PICKUPS = 6;
-const int SP_CTF_DROPS = 7;
-const int SP_CTF_FCKILLS = 8;
-const int SP_CTF_RETURNS = 9;
 
 CLASS(Flag, Pickup)
-    ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13')
-    ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13')
+    ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13');
+    ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13');
 ENDCLASS(Flag)
 Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); }
-void ctf_FlagTouch(entity this) { ITEM_HANDLE(Pickup, CTF_FLAG, this, other); }
+void ctf_FlagTouch(entity this, entity toucher) { ITEM_HANDLE(Pickup, CTF_FLAG, this, toucher); }
 
 // flag constants // for most of these, there is just one question to be asked: WHYYYYY?
 
@@ -70,6 +64,7 @@ entity ctf_worldflaglist;
 .entity wps_flagbase;
 .entity wps_flagcarrier;
 .entity wps_flagdropped;
+.entity wps_flagreturn;
 .entity wps_enemyflagcarrier;
 .float wps_helpme_time;
 bool wpforenemy_announced;
@@ -98,6 +93,8 @@ const int RETURN_DAMAGE = 3;
 const int RETURN_SPEEDRUN = 4;
 const int RETURN_NEEDKILL = 5;
 
+bool ctf_Stalemate_Customize(entity this, entity client);
+
 void ctf_Handle_Throw(entity player, entity receiver, float droptype);
 
 // flag properties
@@ -110,7 +107,7 @@ float ctf_captimerecord; // record time for capturing the flag
 .entity ctf_dropper; // don't allow spam of dropping the flag
 .int max_flag_health;
 .float next_take_time;
-.bool ctf_flagdamaged;
+.bool ctf_flagdamaged_byworld;
 int ctf_teams;
 
 // passing/throwing properties
@@ -171,3 +168,4 @@ const int CTF_NEUTRAL_FLAG_LOST                     = 512;
 const int CTF_NEUTRAL_FLAG_CARRYING            = 768;
 const int CTF_FLAG_NEUTRAL                             = 2048;
 const int CTF_SHIELDED                                 = 4096;
+const int CTF_STALEMATE                                        = 8192;
index bfddc30b01bbbc7439a2fb76ab793e0662db4e41..f83eb6b649a577568d1e796f6e1bafdca54088d9 100644 (file)
@@ -1,50 +1,6 @@
 #include "gamemode_cts.qh"
 #include <server/race.qh>
 
-#ifndef GAMEMODE_CTS_H
-#define GAMEMODE_CTS_H
-
-void cts_Initialize();
-
-REGISTER_MUTATOR(cts, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-
-               g_race_qualifying = true;
-               independent_players = 1;
-               SetLimits(0, 0, autocvar_timelimit_override, -1);
-
-               cts_Initialize();
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back cts_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-// scores
-const float ST_CTS_LAPS = 1;
-const float SP_CTS_LAPS = 4;
-const float SP_CTS_TIME = 5;
-const float SP_CTS_FASTEST = 6;
-#endif
-
-#ifdef IMPLEMENTATION
-
 #include <server/race.qh>
 
 float autocvar_g_cts_finish_kill_delay;
@@ -62,7 +18,7 @@ void havocbot_role_cts(entity this)
                this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
 
-               FOREACH_ENTITY_CLASS("trigger_race_checkpoint", true,
+               IL_EACH(g_racecheckpoints, true,
                {
                        if(it.cnt == this.race_checkpoint)
                                navigation_routerating(this, it, 1000000, 5000);
@@ -79,13 +35,13 @@ void cts_ScoreRules()
        ScoreRules_basics(0, 0, 0, false);
        if(g_race_qualifying)
        {
-               ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest",   SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME);
+               ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest",   SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME);
        }
        else
        {
-               ScoreInfo_SetLabel_PlayerScore(SP_CTS_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
-               ScoreInfo_SetLabel_PlayerScore(SP_CTS_TIME,    "time",      SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
-               ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest",   SFL_LOWER_IS_BETTER | SFL_TIME);
+               ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
+               ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME,    "time",      SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
+               ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest",   SFL_LOWER_IS_BETTER | SFL_TIME);
        }
        ScoreRules_basics_end();
 }
@@ -111,8 +67,9 @@ void CTS_ClientKill(entity e) // silent version of ClientKill, used when player
 MUTATOR_HOOKFUNCTION(cts, PlayerPhysics)
 {
        entity player = M_ARGV(0, entity);
+       float dt = M_ARGV(1, float);
 
-       player.race_movetime_frac += PHYS_INPUT_TIMELENGTH;
+       player.race_movetime_frac += dt;
        float f = floor(player.race_movetime_frac);
        player.race_movetime_frac -= f;
        player.race_movetime_count += f;
@@ -127,7 +84,7 @@ MUTATOR_HOOKFUNCTION(cts, PlayerPhysics)
                if(player.race_penalty)
                {
                        player.velocity = '0 0 0';
-                       player.movetype = MOVETYPE_NONE;
+                       set_movetype(player, MOVETYPE_NONE);
                        player.disableclientprediction = 2;
                }
        }
@@ -285,7 +242,7 @@ MUTATOR_HOOKFUNCTION(cts, PutClientInServer)
 MUTATOR_HOOKFUNCTION(cts, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        frag_target.respawn_flags |= RESPAWN_FORCE;
        race_AbandonRaceCheck(frag_target);
 }
@@ -406,7 +363,7 @@ MUTATOR_HOOKFUNCTION(cts, ClientKill)
 
        if(player.killindicator && player.killindicator.health == 1) // player.killindicator.health == 1 means that the kill indicator was spawned by CTS_ClientKill
        {
-               remove(player.killindicator);
+               delete(player.killindicator);
                player.killindicator = NULL;
 
                ClientKill_Now(player); // allow instant kill in this case
@@ -440,5 +397,3 @@ void cts_Initialize()
 {
        cts_ScoreRules();
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..2a18fe915d4ad85f8999022d834452fb9ef0ff60 100644 (file)
@@ -1,3 +1,39 @@
 #pragma once
 
 #include "../gamemode.qh"
+#include <server/race.qh>
+
+void cts_Initialize();
+
+REGISTER_MUTATOR(cts, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+
+               g_race_qualifying = true;
+               independent_players = 1;
+               SetLimits(0, 0, autocvar_timelimit_override, -1);
+
+               cts_Initialize();
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back cts_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+// scores
+const float ST_CTS_LAPS = 1;
index 866370351377f1dda2c9ac12fa447b8a3ed8f4db..9590027d3f6b6454e8ba2837985ff30663957a07 100644 (file)
@@ -1,37 +1,7 @@
 #include "gamemode_deathmatch.qh"
-#ifndef GAMEMODE_DEATHMATCH_H
-#define GAMEMODE_DEATHMATCH_H
 
-REGISTER_MUTATOR(dm, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back dm_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               error("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-#endif
-
-#ifdef IMPLEMENTATION
 MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining)
 {
        // announce remaining frags
        return true;
 }
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..d3cc197eafd40efd5ed884c50713e23f746223a8 100644 (file)
@@ -1,3 +1,27 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+REGISTER_MUTATOR(dm, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back dm_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               error("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
index 6a217133a787f70ed859f872eed6af1bec91c25c..7f98ed289cd990a2041834270a58181ce0190b16 100644 (file)
@@ -1,70 +1,4 @@
 #include "gamemode_domination.qh"
-#ifndef GAMEMODE_DOMINATION_H
-#define GAMEMODE_DOMINATION_H
-
-#define autocvar_g_domination_point_limit cvar("g_domination_point_limit")
-bool autocvar_g_domination_roundbased;
-int autocvar_g_domination_roundbased_point_limit;
-int autocvar_g_domination_point_leadlimit;
-
-void dom_Initialize();
-
-REGISTER_MUTATOR(dom, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               dom_Initialize();
-
-               int fraglimit_override = autocvar_g_domination_point_limit;
-               if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
-                       fraglimit_override = autocvar_g_domination_roundbased_point_limit;
-
-               ActivateTeamplay();
-               SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, autocvar_timelimit_override, -1);
-               have_team_spawns = -1; // request team spawns
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-// score rule declarations
-const float ST_DOM_TICKS = 1;
-const float SP_DOM_TICKS = 4;
-const float SP_DOM_TAKES = 5;
-const float ST_DOM_CAPS = 1;
-const float SP_DOM_CAPS = 4;
-
-// pps: points per second
-.float dom_total_pps = _STAT(DOM_TOTAL_PPS);
-.float dom_pps_red = _STAT(DOM_PPS_RED);
-.float dom_pps_blue = _STAT(DOM_PPS_BLUE);
-.float dom_pps_yellow = _STAT(DOM_PPS_YELLOW);
-.float dom_pps_pink = _STAT(DOM_PPS_PINK);
-float total_pps;
-float pps_red;
-float pps_blue;
-float pps_yellow;
-float pps_pink;
-
-// capture declarations
-.float enemy_playerid;
-.entity sprite;
-.float captime;
-
-// misc globals
-float domination_roundbased;
-float domination_teams;
-#endif
-
-#ifdef IMPLEMENTATION
 
 #include <server/teamplay.qh>
 
@@ -165,8 +99,8 @@ void dompoint_captured(entity this)
        WaypointSprite_UpdateSprites(this.sprite, msg, WP_Null, WP_Null);
 
        total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
-       for(head = NULL; (head = find(head, classname, "dom_controlpoint")) != NULL; )
-       FOREACH_ENTITY_CLASS("dom_controlpoint", true, LAMBDA(
+       IL_EACH(g_dompoints, true,
+       {
                if (autocvar_g_domination_point_amt)
                        points = autocvar_g_domination_point_amt;
                else
@@ -183,7 +117,7 @@ void dompoint_captured(entity this)
                        case NUM_TEAM_4: pps_pink += points/wait_time; break;
                }
                total_pps += points/wait_time;
-       ));
+       });
 
        WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DOMPOINT, colormapPaletteColor(this.goalentity.team - 1, 0));
        WaypointSprite_Ping(this.sprite);
@@ -250,11 +184,11 @@ void dompointthink(entity this)
        }
 }
 
-void dompointtouch(entity this)
+void dompointtouch(entity this, entity toucher)
 {
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
-       if (other.health < 1)
+       if (toucher.health < 1)
                return;
 
        if(round_handler_IsActive() && !round_handler_IsRoundStarted())
@@ -265,7 +199,7 @@ void dompointtouch(entity this)
 
        // only valid teams can claim it
        entity head = find(NULL, classname, "dom_team");
-       while (head && head.team != other.team)
+       while (head && head.team != toucher.team)
                head = find(head, classname, "dom_team");
        if (!head || head.netname == "" || head == this.goalentity)
                return;
@@ -274,9 +208,9 @@ void dompointtouch(entity this)
 
        this.team = this.goalentity.team; // this stores the PREVIOUS team!
 
-       this.cnt = other.team;
+       this.cnt = toucher.team;
        this.owner = head; // team to switch to after the delay
-       this.dmg_inflictor = other;
+       this.dmg_inflictor = toucher;
 
        // this.state = 1;
        // this.delay = time + cvar("g_domination_point_capturetime");
@@ -299,8 +233,8 @@ void dompointtouch(entity this)
        this.modelindex = head.dmg;
        this.skin = head.skin;
 
-       this.enemy = other; // individual player scoring
-       this.enemy_playerid = other.playerid;
+       this.enemy = toucher; // individual player scoring
+       this.enemy_playerid = toucher.playerid;
        dompoint_captured(this);
 }
 
@@ -350,6 +284,8 @@ void dom_controlpoint_setup(entity this)
        this.nextthink = time;
        settouch(this, dompointtouch);
        this.solid = SOLID_TRIGGER;
+       if(!this.flags & FL_ITEM)
+               IL_PUSH(g_items, this);
        this.flags = FL_ITEM;
        setsize(this, '-32 -32 -32', '32 32 32');
        setorigin(this, this.origin + '0 0 20');
@@ -362,16 +298,15 @@ void dom_controlpoint_setup(entity this)
 float total_controlpoints;
 void Domination_count_controlpoints()
 {
-       entity e;
        total_controlpoints = redowned = blueowned = yellowowned = pinkowned = 0;
-       for(e = NULL; (e = find(e, classname, "dom_controlpoint")) != NULL; )
+       IL_EACH(g_dompoints, true,
        {
                ++total_controlpoints;
-               redowned += (e.goalentity.team == NUM_TEAM_1);
-               blueowned += (e.goalentity.team == NUM_TEAM_2);
-               yellowowned += (e.goalentity.team == NUM_TEAM_3);
-               pinkowned += (e.goalentity.team == NUM_TEAM_4);
-       }
+               redowned += (it.goalentity.team == NUM_TEAM_1);
+               blueowned += (it.goalentity.team == NUM_TEAM_2);
+               yellowowned += (it.goalentity.team == NUM_TEAM_3);
+               pinkowned += (it.goalentity.team == NUM_TEAM_4);
+       });
 }
 
 float Domination_GetWinnerTeam()
@@ -446,6 +381,19 @@ void Domination_RoundStart()
 }
 
 //go to best items, or control points you don't own
+void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius)
+{
+       IL_EACH(g_dompoints, vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius),
+       {
+               if(it.cnt > -1) // this is just being fought
+                       navigation_routerating(this, it, ratingscale, 5000);
+               else if(it.goalentity.cnt == 0) // unclaimed
+                       navigation_routerating(this, it, ratingscale * 0.5, 5000);
+               else if(it.goalentity.team != this.team) // other team's point
+                       navigation_routerating(this, it, ratingscale * 0.2, 5000);
+       });
+}
+
 void havocbot_role_dom(entity this)
 {
        if(IS_DEAD(this))
@@ -463,7 +411,7 @@ void havocbot_role_dom(entity this)
        }
 }
 
-MUTATOR_HOOKFUNCTION(dom, GetTeamCount)
+MUTATOR_HOOKFUNCTION(dom, CheckAllowedTeams)
 {
        // fallback?
        M_ARGV(0, float) = domination_teams;
@@ -495,7 +443,7 @@ MUTATOR_HOOKFUNCTION(dom, reset_map_players)
 {
        total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
-               WITHSELF(it, PutClientInServer());
+               PutClientInServer(it);
                if(domination_roundbased)
                        it.player_blocked = 1;
                if(IS_REAL_CLIENT(it))
@@ -537,7 +485,7 @@ spawnfunc(dom_controlpoint)
 {
        if(!g_domination)
        {
-               remove(this);
+               delete(this);
                return;
        }
        setthink(this, dom_controlpoint_setup);
@@ -550,6 +498,8 @@ spawnfunc(dom_controlpoint)
        this.effects = this.effects | EF_LOWPRECISION;
        if (autocvar_g_domination_point_fullbright)
                this.effects |= EF_FULLBRIGHT;
+
+       IL_PUSH(g_dompoints, this);
 }
 
 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
@@ -582,7 +532,7 @@ spawnfunc(dom_team)
 {
        if(!g_domination || autocvar_g_domination_teams_override >= 2)
        {
-               remove(this);
+               delete(this);
                return;
        }
        precache_model(this.model);
@@ -602,7 +552,7 @@ spawnfunc(dom_team)
 }
 
 // scoreboard setup
-void ScoreRules_dom(float teams)
+void ScoreRules_dom(int teams)
 {
        if(domination_roundbased)
        {
@@ -680,13 +630,23 @@ void dom_DelayedInit(entity this) // Do this check with a delay so we can wait f
        // if no teams are found, spawn defaults
        if(find(NULL, classname, "dom_team") == NULL || autocvar_g_domination_teams_override >= 2)
        {
-               LOG_TRACE("No \"dom_team\" entities found on this map, creating them anyway.\n");
-               domination_teams = bound(2, ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override), 4);
+               LOG_TRACE("No \"dom_team\" entities found on this map, creating them anyway.");
+               domination_teams = autocvar_g_domination_teams_override;
+               if (domination_teams < 2)
+                       domination_teams = autocvar_g_domination_default_teams;
+               domination_teams = bound(2, domination_teams, 4);
                dom_spawnteams(domination_teams);
        }
 
        CheckAllowedTeams(NULL);
-       domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
+       //domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
+
+       int teams = 0;
+       if(c1 >= 0) teams |= BIT(0);
+       if(c2 >= 0) teams |= BIT(1);
+       if(c3 >= 0) teams |= BIT(2);
+       if(c4 >= 0) teams |= BIT(3);
+       domination_teams = teams;
 
        domination_roundbased = autocvar_g_domination_roundbased;
 
@@ -704,5 +664,3 @@ void dom_Initialize()
        g_domination = true;
        InitializeEntity(NULL, dom_DelayedInit, INITPRIO_GAMETYPE);
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..609fcfd0d9f01c91afbe25566948f75f96626219 100644 (file)
@@ -1,3 +1,66 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+#define autocvar_g_domination_point_limit cvar("g_domination_point_limit")
+bool autocvar_g_domination_roundbased;
+int autocvar_g_domination_roundbased_point_limit;
+int autocvar_g_domination_point_leadlimit;
+
+void dom_Initialize();
+
+REGISTER_MUTATOR(dom, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               dom_Initialize();
+
+               int fraglimit_override = autocvar_g_domination_point_limit;
+               if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
+                       fraglimit_override = autocvar_g_domination_roundbased_point_limit;
+
+               ActivateTeamplay();
+               SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, autocvar_timelimit_override, -1);
+               have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+// score rule declarations
+const float ST_DOM_TICKS = 1;
+const float ST_DOM_CAPS = 1;
+
+// pps: points per second
+.float dom_total_pps = _STAT(DOM_TOTAL_PPS);
+.float dom_pps_red = _STAT(DOM_PPS_RED);
+.float dom_pps_blue = _STAT(DOM_PPS_BLUE);
+.float dom_pps_yellow = _STAT(DOM_PPS_YELLOW);
+.float dom_pps_pink = _STAT(DOM_PPS_PINK);
+float total_pps;
+float pps_red;
+float pps_blue;
+float pps_yellow;
+float pps_pink;
+
+// capture declarations
+.float enemy_playerid;
+.entity sprite;
+.float captime;
+
+// misc globals
+float domination_roundbased;
+float domination_teams;
+
+void AnimateDomPoint(entity this);
+
+IntrusiveList g_dompoints;
+STATIC_INIT(g_dompoints) { g_dompoints = IL_NEW(); }
index 6db1db73d4f6f89276876696a3ea5541057e4c5f..cec7d950e9076cdecfc573fdaeafe9be16f409e2 100644 (file)
@@ -1,68 +1,13 @@
 #include "gamemode_freezetag.qh"
-#ifndef GAMEMODE_FREEZETAG_H
-#define GAMEMODE_FREEZETAG_H
-
-int autocvar_g_freezetag_point_limit;
-int autocvar_g_freezetag_point_leadlimit;
-bool autocvar_g_freezetag_team_spawns;
-void freezetag_Initialize();
-
-REGISTER_MUTATOR(ft, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               freezetag_Initialize();
-
-               ActivateTeamplay();
-               SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, autocvar_timelimit_override, -1);
-
-               if (autocvar_g_freezetag_team_spawns)
-                       have_team_spawns = -1; // request team spawns
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back freezetag_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-.float freezetag_frozen_time;
-.float freezetag_frozen_timeout;
-const float ICE_MAX_ALPHA = 1;
-const float ICE_MIN_ALPHA = 0.1;
-float freezetag_teams;
-
-.float reviving; // temp var
-
-float autocvar_g_freezetag_revive_extra_size;
-float autocvar_g_freezetag_revive_speed;
-bool autocvar_g_freezetag_revive_nade;
-float autocvar_g_freezetag_revive_nade_health;
-
-#endif
-#ifdef IMPLEMENTATION
 
 float autocvar_g_freezetag_frozen_maxtime;
 float autocvar_g_freezetag_revive_clearspeed;
 float autocvar_g_freezetag_round_timelimit;
-int autocvar_g_freezetag_teams;
+//int autocvar_g_freezetag_teams;
 int autocvar_g_freezetag_teams_override;
 float autocvar_g_freezetag_warmup;
 
-const float SP_FREEZETAG_REVIVALS = 4;
-void freezetag_ScoreRules(float teams)
+void freezetag_ScoreRules(int teams)
 {
        ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); // SFL_SORT_PRIO_PRIMARY
        ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0);
@@ -91,7 +36,7 @@ void freezetag_count_alive_players()
        eliminatedPlayers.SendFlags |= 1;
 }
 #define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
-#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
+#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == NumTeams(freezetag_teams))
 
 float freezetag_CheckTeams()
 {
@@ -110,9 +55,15 @@ float freezetag_CheckTeams()
                prev_missing_teams_mask = -1;
                return 0;
        }
-       float missing_teams_mask = (!redalive) + (!bluealive) * 2;
-       if(freezetag_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
-       if(freezetag_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+       int missing_teams_mask = 0;
+       if(freezetag_teams & BIT(0))
+               missing_teams_mask += (!redalive) * 1;
+       if(freezetag_teams & BIT(1))
+               missing_teams_mask += (!bluealive) * 2;
+       if(freezetag_teams & BIT(2))
+               missing_teams_mask += (!yellowalive) * 4;
+       if(freezetag_teams & BIT(3))
+               missing_teams_mask += (!pinkalive) * 8;
        if(prev_missing_teams_mask != missing_teams_mask)
        {
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
@@ -146,6 +97,9 @@ float freezetag_getWinnerTeam()
        return -1; // no player left
 }
 
+void nades_Clear(entity);
+void nades_GiveBonus(entity player, float score);
+
 float freezetag_CheckWinner()
 {
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
@@ -299,7 +253,7 @@ void havocbot_role_ft_offense(entity this)
        // If only one left on team or if role has timed out then start trying to free players.
        if (((unfrozen == 0) && (!STAT(FROZEN, this))) || (time > this.havocbot_role_timeout))
        {
-               LOG_TRACE("changing role to freeing\n");
+               LOG_TRACE("changing role to freeing");
                this.havocbot_role = havocbot_role_ft_freeing;
                this.havocbot_role_timeout = 0;
                return;
@@ -328,7 +282,7 @@ void havocbot_role_ft_freeing(entity this)
 
        if (time > this.havocbot_role_timeout)
        {
-               LOG_TRACE("changing role to offense\n");
+               LOG_TRACE("changing role to offense");
                this.havocbot_role = havocbot_role_ft_offense;
                this.havocbot_role_timeout = 0;
                return;
@@ -380,7 +334,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
-       float frag_deathtype = M_ARGV(2, float);
+       float frag_deathtype = M_ARGV(3, float);
 
        if(round_handler_IsActive())
        if(round_handler_CountdownRunning())
@@ -424,10 +378,6 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies)
        }
        else
        {
-               if(IS_PLAYER(frag_target))
-                       Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_FROZEN, frag_attacker.netname);
-               if(IS_PLAYER(frag_attacker))
-                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_FREEZETAG_FREEZE, frag_target.netname);
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
        }
 
@@ -464,7 +414,7 @@ MUTATOR_HOOKFUNCTION(ft, reset_map_players)
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                it.killcount = 0;
                it.freezetag_frozen_timeout = -1;
-               WITHSELF(it, PutClientInServer());
+               PutClientInServer(it);
                it.freezetag_frozen_timeout = 0;
        ));
        freezetag_count_alive_players();
@@ -482,22 +432,13 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
        if(gameover)
                return true;
 
-       entity player = M_ARGV(0, entity);
-
-       if(STAT(FROZEN, player) == 1)
-       {
-               // keep health = 1
-               player.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
-       }
-
        if(round_handler_IsActive())
        if(!round_handler_IsRoundStarted())
                return true;
 
        int n;
-
-       entity o;
-       o = NULL;
+       entity o = NULL;
+       entity player = M_ARGV(0, entity);
        //if(STAT(FROZEN, player))
        //if(player.freezetag_frozen_timeout > 0 && time < player.freezetag_frozen_timeout)
                //player.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (player.freezetag_frozen_timeout - time) / (player.freezetag_frozen_timeout - player.freezetag_frozen_time);
@@ -599,7 +540,7 @@ MUTATOR_HOOKFUNCTION(ft, HavocBot_ChooseRole)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ft, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(ft, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(0, float) = freezetag_teams;
 }
@@ -611,12 +552,37 @@ MUTATOR_HOOKFUNCTION(ft, SetWeaponArena)
                M_ARGV(0, string) = "most";
 }
 
+MUTATOR_HOOKFUNCTION(ft, FragCenterMessage)
+{
+       entity frag_attacker = M_ARGV(0, entity);
+       entity frag_target = M_ARGV(1, entity);
+       //float frag_deathtype = M_ARGV(2, float);
+       int kill_count_to_attacker = M_ARGV(3, int);
+       int kill_count_to_target = M_ARGV(4, int);
+
+       if(STAT(FROZEN, frag_target))
+               return; // target was already frozen, so this is just pushing them off the cliff
+
+       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CHOICE, CHOICE_FRAG_FREEZE, frag_target.netname, kill_count_to_attacker, (IS_BOT_CLIENT(frag_target) ? -1 : frag_target.ping));
+       Send_Notification(NOTIF_ONE, frag_target, MSG_CHOICE, CHOICE_FRAGGED_FREEZE, frag_attacker.netname, kill_count_to_target, frag_attacker.health, frag_attacker.armorvalue, (IS_BOT_CLIENT(frag_attacker) ? -1 : frag_attacker.ping));
+
+       return true;
+}
+
 void freezetag_Initialize()
 {
        freezetag_teams = autocvar_g_freezetag_teams_override;
        if(freezetag_teams < 2)
-               freezetag_teams = autocvar_g_freezetag_teams;
+               freezetag_teams = cvar("g_freezetag_teams"); // read the cvar directly as it gets written earlier in the same frame
        freezetag_teams = bound(2, freezetag_teams, 4);
+
+       int teams = 0;
+       if(freezetag_teams >= 1) teams |= BIT(0);
+       if(freezetag_teams >= 2) teams |= BIT(1);
+       if(freezetag_teams >= 3) teams |= BIT(2);
+       if(freezetag_teams >= 4) teams |= BIT(3);
+
+       freezetag_teams = teams; // now set it?
        freezetag_ScoreRules(freezetag_teams);
 
        round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null);
@@ -624,5 +590,3 @@ void freezetag_Initialize()
 
        EliminatedPlayers_Init(freezetag_isEliminated);
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..fda0737dd92e1ced1bb5b32923197628fa62dadb 100644 (file)
@@ -1,3 +1,52 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+int autocvar_g_freezetag_point_limit;
+int autocvar_g_freezetag_point_leadlimit;
+bool autocvar_g_freezetag_team_spawns;
+void freezetag_Initialize();
+
+REGISTER_MUTATOR(ft, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               freezetag_Initialize();
+
+               ActivateTeamplay();
+               SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, autocvar_timelimit_override, -1);
+
+               if (autocvar_g_freezetag_team_spawns)
+                       have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back freezetag_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+.float freezetag_frozen_time;
+.float freezetag_frozen_timeout;
+const float ICE_MAX_ALPHA = 1;
+const float ICE_MIN_ALPHA = 0.1;
+float freezetag_teams;
+
+.float reviving; // temp var
+
+float autocvar_g_freezetag_revive_extra_size;
+float autocvar_g_freezetag_revive_speed;
+bool autocvar_g_freezetag_revive_nade;
+float autocvar_g_freezetag_revive_nade_health;
index 7c949d8663625e207db2ece50ac03d993d999d2c..1496feec595cdd2ec0fb83b0026ba267ae7380a7 100644 (file)
@@ -1,72 +1,12 @@
 #include "gamemode_invasion.qh"
-#ifndef GAMEMODE_INVASION_H
-#define GAMEMODE_INVASION_H
 
-#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
-int autocvar_g_invasion_teams;
-bool autocvar_g_invasion_team_spawns;
-bool g_invasion;
-void invasion_Initialize();
-
-REGISTER_MUTATOR(inv, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               g_invasion = true;
-               invasion_Initialize();
-
-               cvar_settemp("g_monsters", "1");
-
-               SetLimits(autocvar_g_invasion_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1);
-               if (autocvar_g_invasion_teams >= 2)
-               {
-                       ActivateTeamplay();
-                       if (autocvar_g_invasion_team_spawns)
-                               have_team_spawns = -1; // request team spawns
-               }
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back invasion_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-float inv_numspawned;
-float inv_maxspawned;
-float inv_roundcnt;
-float inv_maxrounds;
-float inv_numkilled;
-float inv_lastcheck;
-float inv_maxcurrent;
-
-float invasion_teams;
-float inv_monsters_perteam[17];
-
-float inv_monsterskill;
-
-const float ST_INV_KILLS = 1;
-#endif
-
-#ifdef IMPLEMENTATION
-
-#include <common/monsters/spawn.qh>
+#include <common/monsters/sv_spawn.qh>
 #include <common/monsters/sv_monsters.qh>
 
 #include <server/teamplay.qh>
 
+IntrusiveList g_invasion_spawns;
+STATIC_INIT(g_invasion_spawns) { g_invasion_spawns = IL_NEW(); }
 
 float autocvar_g_invasion_round_timelimit;
 float autocvar_g_invasion_spawnpoint_spawn_delay;
@@ -75,57 +15,46 @@ int autocvar_g_invasion_monster_count;
 bool autocvar_g_invasion_zombies_only;
 float autocvar_g_invasion_spawn_delay;
 
+.string spawnmob;
+
 spawnfunc(invasion_spawnpoint)
 {
-       if(!g_invasion) { remove(this); return; }
+       if(!g_invasion) { delete(this); return; }
 
        this.classname = "invasion_spawnpoint";
-
-       if(autocvar_g_invasion_zombies_only) // precache only if it hasn't been already
-       if(this.monsterid) {
-               Monster mon = get_monsterinfo(this.monsterid);
-               mon.mr_precache(mon);
-       }
+       IL_PUSH(g_invasion_spawns, this);
 }
 
-float invasion_PickMonster(float supermonster_count)
+Monster invasion_PickMonster(int supermonster_count)
 {
-       if(autocvar_g_invasion_zombies_only)
-               return MON_ZOMBIE.monsterid;
-
-       float i;
-       entity mon;
-
        RandomSelection_Init();
 
-       for(i = MON_FIRST; i <= MON_LAST; ++i)
+       FOREACH(Monsters, it != MON_Null,
        {
-               mon = get_monsterinfo(i);
-               if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM) || ((mon.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1))
-                       continue; // flying/swimming monsters not yet supported
-
-               RandomSelection_Add(NULL, i, string_null, 1, 1);
-       }
+               if((it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM) || (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1))
+                       continue;
+               if(autocvar_g_invasion_zombies_only && !(it.spawnflags & MONSTER_TYPE_UNDEAD))
+                       continue;
+        RandomSelection_AddEnt(it, 1, 1);
+       });
 
-       return RandomSelection_chosen_float;
+       return RandomSelection_chosen_ent;
 }
 
 entity invasion_PickSpawn()
 {
-       entity e;
-
        RandomSelection_Init();
 
-       for(e = NULL;(e = find(e, classname, "invasion_spawnpoint")); )
+       IL_EACH(g_invasion_spawns, true,
        {
-               RandomSelection_Add(e, 0, string_null, 1, ((time >= e.spawnshieldtime) ? 0.2 : 1)); // give recently used spawnpoints a very low rating
-               e.spawnshieldtime = time + autocvar_g_invasion_spawnpoint_spawn_delay;
-       }
+               RandomSelection_AddEnt(it, 1, ((time >= it.spawnshieldtime) ? 0.2 : 1)); // give recently used spawnpoints a very low rating
+               it.spawnshieldtime = time + autocvar_g_invasion_spawnpoint_spawn_delay;
+       });
 
        return RandomSelection_chosen_ent;
 }
 
-void invasion_SpawnChosenMonster(float mon)
+void invasion_SpawnChosenMonster(Monster mon)
 {
        entity spawn_point, monster;
 
@@ -133,19 +62,23 @@ void invasion_SpawnChosenMonster(float mon)
 
        if(spawn_point == NULL)
        {
-               LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations\n");
+               LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations");
                entity e = spawn();
-               setsize(e, (get_monsterinfo(mon)).mins, (get_monsterinfo(mon)).maxs);
+               setsize(e, mon.mins, mon.maxs);
 
                if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
-                       monster = spawnmonster("", mon, NULL, NULL, e.origin, false, false, 2);
-               else return;
-
-               setthink(e, SUB_Remove);
-               e.nextthink = time + 0.1;
+                       monster = spawnmonster(e, "", mon.monsterid, NULL, NULL, e.origin, false, false, 2);
+               else
+               {
+                       delete(e);
+                       return;
+               }
        }
-       else
-               monster = spawnmonster("", ((spawn_point.monsterid) ? spawn_point.monsterid : mon), spawn_point, spawn_point, spawn_point.origin, false, false, 2);
+       else // if spawnmob field falls through (unset), fallback to mon (relying on spawnmonster for that behaviour)
+               monster = spawnmonster(spawn(), spawn_point.spawnmob, mon.monsterid, spawn_point, spawn_point, spawn_point.origin, false, false, 2);
+
+       if(!monster)
+               return;
 
        if(spawn_point) monster.target2 = spawn_point.target2;
        monster.spawnshieldtime = time;
@@ -157,10 +90,10 @@ void invasion_SpawnChosenMonster(float mon)
        else
        {
                RandomSelection_Init();
-               if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_Add(NULL, NUM_TEAM_1, string_null, 1, 1);
-               if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_Add(NULL, NUM_TEAM_2, string_null, 1, 1);
-               if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_Add(NULL, NUM_TEAM_3, string_null, 1, 1); }
-               if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_Add(NULL, NUM_TEAM_4, string_null, 1, 1); }
+               if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_AddFloat(NUM_TEAM_1, 1, 1);
+               if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_AddFloat(NUM_TEAM_2, 1, 1);
+               if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_AddFloat(NUM_TEAM_3, 1, 1); }
+               if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_AddFloat(NUM_TEAM_4, 1, 1); }
 
                monster.team = RandomSelection_chosen_float;
        }
@@ -184,18 +117,22 @@ void invasion_SpawnChosenMonster(float mon)
                monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses
 }
 
-void invasion_SpawnMonsters(float supermonster_count)
+void invasion_SpawnMonsters(int supermonster_count)
 {
-       float chosen_monster = invasion_PickMonster(supermonster_count);
+       Monster chosen_monster = invasion_PickMonster(supermonster_count);
 
        invasion_SpawnChosenMonster(chosen_monster);
 }
 
-float Invasion_CheckWinner()
+bool Invasion_CheckWinner()
 {
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
        {
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it)));
+               IL_EACH(g_monsters, true,
+               {
+                       Monster_Remove(it);
+               });
+               IL_CLEAR(g_monsters);
 
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER);
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
@@ -205,23 +142,21 @@ float Invasion_CheckWinner()
 
        float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0;
 
-       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
-               if(it.health > 0)
-               {
-                       if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
-                               ++supermonster_count;
-                       ++total_alive_monsters;
+       IL_EACH(g_monsters, it.health > 0,
+       {
+               if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
+                       ++supermonster_count;
+               ++total_alive_monsters;
 
-                       if(teamplay)
-                       switch(it.team)
-                       {
-                               case NUM_TEAM_1: ++red_alive; break;
-                               case NUM_TEAM_2: ++blue_alive; break;
-                               case NUM_TEAM_3: ++yellow_alive; break;
-                               case NUM_TEAM_4: ++pink_alive; break;
-                       }
+               if(teamplay)
+               switch(it.team)
+               {
+                       case NUM_TEAM_1: ++red_alive; break;
+                       case NUM_TEAM_2: ++blue_alive; break;
+                       case NUM_TEAM_3: ++yellow_alive; break;
+                       case NUM_TEAM_4: ++pink_alive; break;
                }
-       ));
+       });
 
        if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned)
        {
@@ -274,7 +209,11 @@ float Invasion_CheckWinner()
                ));
        }
 
-       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it)));
+       IL_EACH(g_monsters, true,
+       {
+               Monster_Remove(it);
+       });
+       IL_CLEAR(g_monsters);
 
        if(teamplay)
        {
@@ -398,6 +337,8 @@ MUTATOR_HOOKFUNCTION(inv, PlayerSpawn)
 {
        entity player = M_ARGV(0, entity);
 
+       if(player.bot_attack)
+               IL_REMOVE(g_bot_targets, player);
        player.bot_attack = false;
 }
 
@@ -470,7 +411,7 @@ MUTATOR_HOOKFUNCTION(inv, AllowMobSpawning)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(inv, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(inv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(0, float) = invasion_teams;
 }
@@ -481,7 +422,7 @@ MUTATOR_HOOKFUNCTION(inv, AllowMobButcher)
        return true;
 }
 
-void invasion_ScoreRules(float inv_teams)
+void invasion_ScoreRules(int inv_teams)
 {
        if(inv_teams) { CheckAllowedTeams(NULL); }
        ScoreRules_basics(inv_teams, 0, 0, false);
@@ -493,7 +434,16 @@ void invasion_ScoreRules(float inv_teams)
 void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
 {
        if(autocvar_g_invasion_teams)
+       {
                invasion_teams = bound(2, autocvar_g_invasion_teams, 4);
+               int teams = 0;
+               if(invasion_teams >= 1) teams |= BIT(0);
+               if(invasion_teams >= 2) teams |= BIT(1);
+               if(invasion_teams >= 3) teams |= BIT(2);
+               if(invasion_teams >= 4) teams |= BIT(3);
+
+               invasion_teams = teams; // now set it?
+       }
        else
                invasion_teams = 0;
 
@@ -512,24 +462,5 @@ void invasion_DelayedInit(entity this) // Do this check with a delay so we can w
 
 void invasion_Initialize()
 {
-       if(autocvar_g_invasion_zombies_only) {
-               Monster mon = MON_ZOMBIE;
-               mon.mr_precache(mon);
-       } else
-       {
-               float i;
-               entity mon;
-               for(i = MON_FIRST; i <= MON_LAST; ++i)
-               {
-                       mon = get_monsterinfo(i);
-                       if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM))
-                               continue; // flying/swimming monsters not yet supported
-
-                       mon.mr_precache(mon);
-               }
-       }
-
        InitializeEntity(NULL, invasion_DelayedInit, INITPRIO_GAMETYPE);
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..e934f8745a4e29bfbcee542ee2c9b1793ea815df 100644 (file)
@@ -1,3 +1,60 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
+int autocvar_g_invasion_teams;
+bool autocvar_g_invasion_team_spawns;
+bool g_invasion;
+void invasion_Initialize();
+
+REGISTER_MUTATOR(inv, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               g_invasion = true;
+               invasion_Initialize();
+
+               cvar_settemp("g_monsters", "1");
+
+               SetLimits(autocvar_g_invasion_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1);
+               if (autocvar_g_invasion_teams >= 2)
+               {
+                       ActivateTeamplay();
+                       if (autocvar_g_invasion_team_spawns)
+                               have_team_spawns = -1; // request team spawns
+               }
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back invasion_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+float inv_numspawned;
+float inv_maxspawned;
+float inv_roundcnt;
+float inv_maxrounds;
+float inv_numkilled;
+float inv_lastcheck;
+float inv_maxcurrent;
+
+float invasion_teams;
+float inv_monsters_perteam[17];
+
+float inv_monsterskill;
+
+const float ST_INV_KILLS = 1;
index 454e2f168f984369d6e3a6b40c3e8675ff7d4f00..6fa76d6c5c1e140fb15382fffbd15118979f8b23 100644 (file)
@@ -1,48 +1,4 @@
 #include "gamemode_keepaway.qh"
-#ifndef GAMEMODE_KEEPAWAY_H
-#define GAMEMODE_KEEPAWAY_H
-
-void ka_Initialize();
-
-REGISTER_MUTATOR(ka, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               ka_Initialize();
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back ka_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return false;
-}
-
-
-entity ka_ball;
-
-const float SP_KEEPAWAY_PICKUPS = 4;
-const float SP_KEEPAWAY_CARRIERKILLS = 5;
-const float SP_KEEPAWAY_BCTIME = 6;
-
-void(entity this) havocbot_role_ka_carrier;
-void(entity this) havocbot_role_ka_collector;
-
-void ka_DropEvent(entity plyr);
-#endif
-
-#ifdef IMPLEMENTATION
 
 int autocvar_g_keepaway_ballcarrier_effects;
 float autocvar_g_keepaway_ballcarrier_damage;
@@ -81,7 +37,7 @@ void ka_EventLog(string mode, entity actor) // use an alias for easy changing an
                GameLogEcho(strcat(":ka:", mode, ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : "")));
 }
 
-void ka_TouchEvent(entity this);
+void ka_TouchEvent(entity this, entity toucher);
 void ka_RespawnBall(entity this);
 void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated
 {
@@ -96,7 +52,7 @@ void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated
        }
 
        makevectors(this.angles);
-       this.movetype = MOVETYPE_BOUNCE;
+       set_movetype(this, MOVETYPE_BOUNCE);
        this.velocity = '0 0 200';
        this.angles = '0 0 0';
        this.effects = autocvar_g_keepawayball_effects;
@@ -125,7 +81,7 @@ void ka_TimeScoring(entity this)
        }
 }
 
-void ka_TouchEvent(entity this) // runs any time that the ball comes in contact with something
+void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something
 {
        if(gameover) { return; }
        if(!this) { return; }
@@ -134,9 +90,9 @@ void ka_TouchEvent(entity this) // runs any time that the ball comes in contact
                ka_RespawnBall(this);
                return;
        }
-       if(IS_DEAD(other)) { return; }
-       if(STAT(FROZEN, other)) { return; }
-       if (!IS_PLAYER(other))
+       if(IS_DEAD(toucher)) { return; }
+       if(STAT(FROZEN, toucher)) { return; }
+       if (!IS_PLAYER(toucher))
        {  // The ball just touched an object, most likely the world
                Send_Effect(EFFECT_BALL_SPARKS, this.origin, '0 0 0', 1);
                sound(this, CH_TRIGGER, SND_KA_TOUCH, VOL_BASE, ATTEN_NORM);
@@ -145,14 +101,14 @@ void ka_TouchEvent(entity this) // runs any time that the ball comes in contact
        else if(this.wait > time) { return; }
 
        // attach the ball to the player
-       this.owner = other;
-       other.ballcarried = this;
-       setattachment(this, other, "");
+       this.owner = toucher;
+       toucher.ballcarried = this;
+       setattachment(this, toucher, "");
        setorigin(this, '0 0 0');
 
        // make the ball invisible/unable to do anything/set up time scoring
        this.velocity = '0 0 0';
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        this.effects |= EF_NODRAW;
        settouch(this, func_null);
        setthink(this, ka_TimeScoring);
@@ -160,25 +116,25 @@ void ka_TouchEvent(entity this) // runs any time that the ball comes in contact
        this.takedamage = DAMAGE_NO;
 
        // apply effects to player
-       other.glow_color = autocvar_g_keepawayball_trail_color;
-       other.glow_trail = true;
-       other.effects |= autocvar_g_keepaway_ballcarrier_effects;
+       toucher.glow_color = autocvar_g_keepawayball_trail_color;
+       toucher.glow_trail = true;
+       toucher.effects |= autocvar_g_keepaway_ballcarrier_effects;
 
        // messages and sounds
-       ka_EventLog("pickup", other);
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, other.netname);
-       Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, other.netname);
-       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF);
+       ka_EventLog("pickup", toucher);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, toucher.netname);
+       Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, toucher.netname);
+       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF);
        sound(this.owner, CH_TRIGGER, SND_KA_PICKEDUP, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
 
        // scoring
-       PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1);
+       PlayerScore_Add(toucher, SP_KEEPAWAY_PICKUPS, 1);
 
        // waypoints
-       WaypointSprite_AttachCarrier(WP_KaBallCarrier, other, RADARICON_FLAGCARRIER);
-       other.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player;
-       WaypointSprite_UpdateRule(other.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
-       WaypointSprite_Ping(other.waypointsprite_attachedforcarrier);
+       WaypointSprite_AttachCarrier(WP_KaBallCarrier, toucher, RADARICON_FLAGCARRIER);
+       toucher.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player;
+       WaypointSprite_UpdateRule(toucher.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+       WaypointSprite_Ping(toucher.waypointsprite_attachedforcarrier);
        WaypointSprite_Kill(this.waypointsprite_attachedforcarrier);
 }
 
@@ -191,7 +147,7 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los
 
        // reset the ball
        setattachment(ball, NULL, "");
-       ball.movetype = MOVETYPE_BOUNCE;
+       set_movetype(ball, MOVETYPE_BOUNCE);
        ball.wait = time + 1;
        settouch(ball, ka_TouchEvent);
        setthink(ball, ka_RespawnBall);
@@ -211,7 +167,7 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los
        ka_EventLog("dropped", plyr);
        Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_DROPPED, plyr.netname);
        Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEEPAWAY_DROPPED, plyr.netname);
-       sound(other, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+       sound(NULL, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
 
        // scoring
        // PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1); Not anymore, this is 100% the same as pickups and is useless.
@@ -470,11 +426,12 @@ MUTATOR_HOOKFUNCTION(ka, HavocBot_ChooseRole)
 MUTATOR_HOOKFUNCTION(ka, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        if(frag_target.ballcarried)
                ka_DropEvent(frag_target);
 }
 
+.bool pushable;
 
 // ==============
 // Initialization
@@ -490,10 +447,11 @@ void ka_SpawnBall() // loads various values for the ball, runs only once at star
        e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
        e.takedamage = DAMAGE_YES;
        e.solid = SOLID_TRIGGER;
-       e.movetype = MOVETYPE_BOUNCE;
+       set_movetype(e, MOVETYPE_BOUNCE);
        e.glow_color = autocvar_g_keepawayball_trail_color;
        e.glow_trail = true;
        e.flags = FL_ITEM;
+       IL_PUSH(g_items, e);
        e.pushable = true;
        e.reset = ka_Reset;
        settouch(e, ka_TouchEvent);
@@ -506,9 +464,9 @@ void ka_SpawnBall() // loads various values for the ball, runs only once at star
 void ka_ScoreRules()
 {
        ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, true); // SFL_SORT_PRIO_PRIMARY
-       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS,                     "pickups",              0);
-       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS,        "bckills",              0);
-       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME,                      "bctime",               SFL_SORT_PRIO_SECONDARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS,             "pickups",              0);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS,   "bckills",           0);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME,                  "bctime",           SFL_SORT_PRIO_SECONDARY);
        ScoreRules_basics_end();
 }
 
@@ -517,5 +475,3 @@ void ka_Initialize() // run at the start of a match, initiates game mode
        ka_ScoreRules();
        ka_SpawnBall();
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..a13ab83a555089681bd43974e221421980665c14 100644 (file)
@@ -1,3 +1,38 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+void ka_Initialize();
+
+REGISTER_MUTATOR(ka, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               ka_Initialize();
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back ka_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return false;
+}
+
+
+entity ka_ball;
+
+void(entity this) havocbot_role_ka_carrier;
+void(entity this) havocbot_role_ka_collector;
+
+void ka_DropEvent(entity plyr);
index 448a2bbc78c47d1d905de3a9d13910e71a987e74..5bfbe676e6d7566127075915c37f0527e6540d60 100644 (file)
@@ -1,60 +1,4 @@
 #include "gamemode_keyhunt.qh"
-#ifndef GAMEMODE_KEYHUNT_H
-#define GAMEMODE_KEYHUNT_H
-
-#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
-int autocvar_g_keyhunt_point_leadlimit;
-bool autocvar_g_keyhunt_team_spawns;
-void kh_Initialize();
-
-REGISTER_MUTATOR(kh, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               kh_Initialize();
-
-               ActivateTeamplay();
-               SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, autocvar_timelimit_override, -1);
-               if (autocvar_g_keyhunt_team_spawns)
-                       have_team_spawns = -1; // request team spawns
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back kh_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext )
-
-// ALL OF THESE should be removed in the future, as other code should not have to care
-
-// used by bots:
-float kh_tracking_enabled;
-.entity kh_next;
-float kh_Key_AllOwnedByWhichTeam();
-
-USING(kh_Think_t, void());
-void kh_StartRound();
-void kh_Controller_SetThink(float t, kh_Think_t func);
-
-entity kh_worldkeylist;
-.entity kh_worldkeynext;
-#endif
-
-#ifdef IMPLEMENTATION
 
 float autocvar_g_balance_keyhunt_damageforcescale;
 float autocvar_g_balance_keyhunt_delay_collect;
@@ -73,7 +17,7 @@ int autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
 int autocvar_g_balance_keyhunt_score_push;
 float autocvar_g_balance_keyhunt_throwvelocity;
 
-int autocvar_g_keyhunt_teams;
+//int autocvar_g_keyhunt_teams;
 int autocvar_g_keyhunt_teams_override;
 
 // #define KH_PLAYER_USE_ATTACHMENT
@@ -96,24 +40,24 @@ const vector KH_KEY_MIN = '-10 -10 -46';
 const vector KH_KEY_MAX = '10 10 3';
 const float KH_KEY_BRIGHTNESS = 2;
 
-float kh_no_radar_circles;
+bool kh_no_radar_circles;
 
 // kh_state
 //     bits  0- 4: team of key 1, or 0 for no such key, or 30 for dropped, or 31 for self
 //     bits  5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self
 //     bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self
 //     bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self
-.float kh_state = _STAT(KH_KEYS);
+.int kh_state = _STAT(KH_KEYS);
 .float siren_time;  //  time delay the siren
 //.float stuff_time;  //  time delay to stuffcmd a cvar
 
-float kh_keystatus[17];
+int kh_keystatus[17];
 //kh_keystatus[0] = status of dropped keys, kh_keystatus[1 - 16] = player #
 //replace 17 with cvar("maxplayers") or similar !!!!!!!!!
 //for(i = 0; i < maxplayers; ++i)
 //     kh_keystatus[i] = "0";
 
-float kh_Team_ByID(float t)
+int kh_Team_ByID(int t)
 {
        if(t == 0) return NUM_TEAM_1;
        if(t == 1) return NUM_TEAM_2;
@@ -125,26 +69,22 @@ float kh_Team_ByID(float t)
 //entity kh_worldkeylist;
 .entity kh_worldkeynext;
 entity kh_controller;
-//float kh_tracking_enabled;
-float kh_teams;
-float kh_interferemsg_time, kh_interferemsg_team;
+//bool kh_tracking_enabled;
+int kh_teams;
+int kh_interferemsg_team;
+float kh_interferemsg_time;
 .entity kh_next, kh_prev; // linked list
 .float kh_droptime;
-.float kh_dropperteam;
+.int kh_dropperteam;
 .entity kh_previous_owner;
-.float kh_previous_owner_playerid;
-.float kh_cp_duration;
+.int kh_previous_owner_playerid;
+
+int kh_key_dropped, kh_key_carried;
 
-float kh_key_dropped, kh_key_carried;
+int kh_Key_AllOwnedByWhichTeam();
 
 const float ST_KH_CAPS = 1;
-const float SP_KH_CAPS = 4;
-const float SP_KH_PUSHES = 5;
-const float SP_KH_DESTROYS = 6;
-const float SP_KH_PICKUPS = 7;
-const float SP_KH_KCKILLS = 8;
-const float SP_KH_LOSSES = 9;
-void kh_ScoreRules(float teams)
+void kh_ScoreRules(int teams)
 {
        ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
        ScoreInfo_SetLabel_TeamScore(  ST_KH_CAPS,      "caps",      SFL_SORT_PRIO_SECONDARY);
@@ -180,10 +120,8 @@ bool kh_Key_waypointsprite_visible_for_player(entity this, entity player, entity
 void kh_update_state()
 {
        entity key;
-       float s;
-       float f;
-
-       s = 0;
+       int f;
+       int s = 0;
        FOR_EACH_KH_KEY(key)
        {
                if(key.owner)
@@ -217,10 +155,13 @@ void kh_Controller_SetThink(float t, kh_Think_t func)  // runs occasionaly
 void kh_WaitForPlayers();
 void kh_Controller_Think(entity this)  // called a lot
 {
-       if(intermission_running)
+       if(gameover)
                return;
        if(this.cnt > 0)
-       { if(getthink(this) != kh_WaitForPlayers) { this.cnt -= 1; } }
+       {
+               if(getthink(this) != kh_WaitForPlayers)
+                       this.cnt -= 1;
+       }
        else if(this.cnt == 0)
        {
                this.cnt -= 1;
@@ -234,7 +175,7 @@ void kh_Controller_Think(entity this)  // called a lot
 void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner)  // update the score when a key is captured
 {
        string s;
-       if(intermission_running)
+       if(gameover)
                return;
 
        if(frags_player)
@@ -275,8 +216,7 @@ vector kh_AttachedOrigin(entity e)  // runs when a team captures the flag, it ca
 void kh_Key_Attach(entity key)  // runs when a player picks up a key and several times when a key is assigned to a player at the start of a round
 {
 #ifdef KH_PLAYER_USE_ATTACHMENT
-       entity first;
-       first = key.owner.kh_next;
+       entity first = key.owner.kh_next;
        if(key == first)
        {
                setattachment(key, key.owner, KH_PLAYER_ATTACHMENT_BONE);
@@ -306,8 +246,9 @@ void kh_Key_Attach(entity key)  // runs when a player picks up a key and several
        key.angles_y -= key.owner.angles.y;
 #endif
        key.flags = 0;
+       IL_REMOVE(g_items, key);
        key.solid = SOLID_NOT;
-       key.movetype = MOVETYPE_NONE;
+       set_movetype(key, MOVETYPE_NONE);
        key.team = key.owner.team;
        key.nextthink = time;
        key.damageforcescale = 0;
@@ -318,8 +259,7 @@ void kh_Key_Attach(entity key)  // runs when a player picks up a key and several
 void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs several times times when all the keys are captured
 {
 #ifdef KH_PLAYER_USE_ATTACHMENT
-       entity first;
-       first = key.owner.kh_next;
+       entity first = key.owner.kh_next;
        if(key == first)
        {
                if(key.kh_next)
@@ -337,7 +277,7 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs
        }
        // in any case:
        setattachment(key, NULL, "");
-       setorigin(key, key.owner.origin + '0 0 1' * (STAT(PL_MIN, NULL).z - KH_KEY_MIN_z));
+       setorigin(key, key.owner.origin + '0 0 1' * (STAT(PL_MIN, key.owner).z - KH_KEY_MIN_z));
        key.angles = key.owner.angles;
 #else
        setorigin(key, key.owner.origin + key.origin.z * '0 0 1');
@@ -345,8 +285,9 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs
        key.angles_y += key.owner.angles.y;
 #endif
        key.flags = FL_ITEM;
+       IL_PUSH(g_items, key);
        key.solid = SOLID_TRIGGER;
-       key.movetype = MOVETYPE_TOSS;
+       set_movetype(key, MOVETYPE_TOSS);
        key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return;
        key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale;
        key.takedamage = DAMAGE_YES;
@@ -358,12 +299,10 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs
 
 void kh_Key_AssignTo(entity key, entity player)  // runs every time a key is picked up or assigned. Runs prior to kh_key_attach
 {
-       entity k;
-       float ownerteam0, ownerteam;
        if(key.owner == player)
                return;
 
-       ownerteam0 = kh_Key_AllOwnedByWhichTeam();
+       int ownerteam0 = kh_Key_AllOwnedByWhichTeam();
 
        if(key.owner)
        {
@@ -435,15 +374,16 @@ void kh_Key_AssignTo(entity key, entity player)  // runs every time a key is pic
 
        key.pusher = NULL;
 
-       ownerteam = kh_Key_AllOwnedByWhichTeam();
+       int ownerteam = kh_Key_AllOwnedByWhichTeam();
        if(ownerteam != ownerteam0)
        {
+               entity k;
                if(ownerteam != -1)
                {
                        kh_interferemsg_time = time + 0.2;
                        kh_interferemsg_team = player.team;
 
-                       // audit all key carrier sprites, update them to RUN HERE
+                       // audit all key carrier sprites, update them to "Run here"
                        FOR_EACH_KH_KEY(k)
                        {
                                if (!k.owner) continue;
@@ -458,7 +398,7 @@ void kh_Key_AssignTo(entity key, entity player)  // runs every time a key is pic
                {
                        kh_interferemsg_time = 0;
 
-                       // audit all key carrier sprites, update them to RUN HERE
+                       // audit all key carrier sprites, update them to "Key Carrier"
                        FOR_EACH_KH_KEY(k)
                        {
                                if (!k.owner) continue;
@@ -483,7 +423,7 @@ void kh_Key_Damage(entity this, entity inflictor, entity attacker, float damage,
                // immediately return is bad
                // maybe start a shorter countdown?
        }
-       if(vlen(force) <= 0)
+       if(force == '0 0 0')
                return;
        if(time > this.pushltime)
                if(IS_PLAYER(attacker))
@@ -500,14 +440,15 @@ void kh_Key_Collect(entity key, entity player)  //a player picks up a dropped ke
                PlayerScore_Add(player, SP_KH_PICKUPS, 1);
        }
        key.kh_dropperteam = 0;
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(key, INFO_KEYHUNT_PICKUP), player.netname);
+       int realteam = kh_Team_ByID(key.count);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_PICKUP), player.netname);
 
        kh_Key_AssignTo(key, player); // this also updates .kh_state
 }
 
-void kh_Key_Touch(entity this)  // runs many, many times when a key has been dropped and can be picked up
+void kh_Key_Touch(entity this, entity toucher)  // runs many, many times when a key has been dropped and can be picked up
 {
-       if(intermission_running)
+       if(gameover)
                return;
 
        if(this.owner) // already carried
@@ -521,20 +462,19 @@ void kh_Key_Touch(entity this)  // runs many, many times when a key has been dro
                // maybe start a shorter countdown?
        }
 
-       if (!IS_PLAYER(other))
+       if (!IS_PLAYER(toucher))
                return;
-       if(IS_DEAD(other))
+       if(IS_DEAD(toucher))
                return;
-       if(other == this.enemy)
+       if(toucher == this.enemy)
                if(time < this.kh_droptime + autocvar_g_balance_keyhunt_delay_collect)
                        return;  // you just dropped it!
-       kh_Key_Collect(this, other);
+       kh_Key_Collect(this, toucher);
 }
 
 void kh_Key_Remove(entity key)  // runs after when all the keys have been collected or when a key has been dropped for more than X seconds
 {
-       entity o;
-       o = key.owner;
+       entity o = key.owner;
        kh_Key_AssignTo(key, NULL);
        if(o) // it was attached
                WaypointSprite_Kill(key.waypointsprite_attachedforcarrier);
@@ -558,7 +498,7 @@ void kh_Key_Remove(entity key)  // runs after when all the keys have been collec
                }
        }
 
-       remove(key);
+       delete(key);
 
        kh_update_state();
 }
@@ -578,27 +518,25 @@ void kh_FinishRound()  // runs when a team captures the keys
        kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound);
 }
 
-void kh_WinnerTeam(float teem)  // runs when a team wins // Samual: Teem?.... TEEM?!?! what the fuck is wrong with you people
+void nades_GiveBonus(entity player, float score);
+
+void kh_WinnerTeam(int winner_team)  // runs when a team wins
 {
        // all key carriers get some points
-       vector firstorigin, lastorigin, midpoint;
-       float first;
        entity key;
-       float score;
-       score = (kh_teams - 1) * autocvar_g_balance_keyhunt_score_capture;
-       DistributeEvenly_Init(score, kh_teams);
+       float score = (NumTeams(kh_teams) - 1) * autocvar_g_balance_keyhunt_score_capture;
+       DistributeEvenly_Init(score, NumTeams(kh_teams));
        // twice the score for 3 team games, three times the score for 4 team games!
        // note: for a win by destroying the key, this should NOT be applied
        FOR_EACH_KH_KEY(key)
        {
-               float f;
-               f = DistributeEvenly_Get(1);
+               float f = DistributeEvenly_Get(1);
                kh_Scores_Event(key.owner, key, "capture", f, 0);
                PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1);
                nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high);
        }
 
-       first = true;
+       bool first = true;
        string keyowner = "";
        FOR_EACH_KH_KEY(key)
                if(key.owner.kh_next == key)
@@ -609,17 +547,13 @@ void kh_WinnerTeam(float teem)  // runs when a team wins // Samual: Teem?.... TE
                        first = false;
                }
 
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(teem, INFO_KEYHUNT_CAPTURE), keyowner);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_KEYHUNT_CAPTURE), keyowner);
 
        first = true;
-       midpoint = '0 0 0';
-       firstorigin = '0 0 0';
-       lastorigin = '0 0 0';
+       vector firstorigin = '0 0 0', lastorigin = '0 0 0', midpoint = '0 0 0';
        FOR_EACH_KH_KEY(key)
        {
-               vector thisorigin;
-
-               thisorigin = kh_AttachedOrigin(key);
+               vector thisorigin = kh_AttachedOrigin(key);
                //dprint("Key origin: ", vtos(thisorigin), "\n");
                midpoint += thisorigin;
 
@@ -630,32 +564,26 @@ void kh_WinnerTeam(float teem)  // runs when a team wins // Samual: Teem?.... TE
                        firstorigin = thisorigin;
                first = false;
        }
-       if(kh_teams > 2)
+       if(NumTeams(kh_teams) > 2)
        {
                te_lightning2(NULL, lastorigin, firstorigin);
        }
-       midpoint = midpoint * (1 / kh_teams);
-       te_customflash(midpoint, 1000, 1, Team_ColorRGB(teem) * 0.5 + '0.5 0.5 0.5');  // make the color >=0.5 in each component
+       midpoint = midpoint * (1 / NumTeams(kh_teams));
+       te_customflash(midpoint, 1000, 1, Team_ColorRGB(winner_team) * 0.5 + '0.5 0.5 0.5');  // make the color >=0.5 in each component
 
        play2all(SND(KH_CAPTURE));
        kh_FinishRound();
 }
 
-void kh_LoserTeam(float teem, entity lostkey)  // runs when a player pushes a flag carrier off the map
+void kh_LoserTeam(int loser_team, entity lostkey)  // runs when a player pushes a flag carrier off the map
 {
-       entity key, attacker;
-       float players;
-       float keys;
        float f;
-
-       attacker = NULL;
+       entity attacker = NULL;
        if(lostkey.pusher)
-               if(lostkey.pusher.team != teem)
+               if(lostkey.pusher.team != loser_team)
                        if(IS_PLAYER(lostkey.pusher))
                                attacker = lostkey.pusher;
 
-       players = keys = 0;
-
        if(attacker)
        {
                if(lostkey.kh_previous_owner)
@@ -667,13 +595,15 @@ void kh_LoserTeam(float teem, entity lostkey)  // runs when a player pushes a fl
        }
        else
        {
-               float of, fragsleft, i, j, thisteam;
-               of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
+               int players = 0;
+               float of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
 
-               FOREACH_CLIENT(IS_PLAYER(it) && it.team != teem, LAMBDA(++players));
+               FOREACH_CLIENT(IS_PLAYER(it) && it.team != loser_team, LAMBDA(++players));
 
+               entity key;
+               int keys = 0;
                FOR_EACH_KH_KEY(key)
-                       if(key.owner && key.team != teem)
+                       if(key.owner && key.team != loser_team)
                                ++keys;
 
                if(lostkey.kh_previous_owner)
@@ -686,20 +616,20 @@ void kh_LoserTeam(float teem, entity lostkey)  // runs when a player pushes a fl
                DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players);
 
                FOR_EACH_KH_KEY(key)
-                       if(key.owner && key.team != teem)
+                       if(key.owner && key.team != loser_team)
                        {
                                f = DistributeEvenly_Get(of);
                                kh_Scores_Event(key.owner, NULL, "destroyed_holdingkey", f, 0);
                        }
 
-               fragsleft = DistributeEvenly_Get(players);
+               int fragsleft = DistributeEvenly_Get(players);
 
                // Now distribute these among all other teams...
-               j = kh_teams - 1;
-               for(i = 0; i < kh_teams; ++i)
+               int j = NumTeams(kh_teams) - 1;
+               for(int i = 0; i < NumTeams(kh_teams); ++i)
                {
-                       thisteam = kh_Team_ByID(i);
-                       if(thisteam == teem) // bad boy, no cookie - this WILL happen
+                       int thisteam = kh_Team_ByID(i);
+                       if(thisteam == loser_team) // bad boy, no cookie - this WILL happen
                                continue;
 
                        players = 0;
@@ -718,7 +648,8 @@ void kh_LoserTeam(float teem, entity lostkey)  // runs when a player pushes a fl
                }
        }
 
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(lostkey, INFO_KEYHUNT_LOST), lostkey.kh_previous_owner.netname);
+       int realteam = kh_Team_ByID(lostkey.count);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_LOST), lostkey.kh_previous_owner.netname);
 
        play2all(SND(KH_DESTROY));
        te_tarexplosion(lostkey.origin);
@@ -728,7 +659,7 @@ void kh_LoserTeam(float teem, entity lostkey)  // runs when a player pushes a fl
 
 void kh_Key_Think(entity this)  // runs all the time
 {
-       if(intermission_running)
+       if(gameover)
                return;
 
        if(this.owner)
@@ -754,8 +685,7 @@ void kh_Key_Think(entity this)  // runs all the time
                }
 
                entity key;
-               vector p;
-               p = this.owner.origin;
+               vector p = this.owner.origin;
                FOR_EACH_KH_KEY(key)
                        if(vdist(key.owner.origin - p, >, autocvar_g_balance_keyhunt_maxdist))
                                goto not_winning;
@@ -840,14 +770,11 @@ void kh_Key_Spawn(entity initial_owner, float _angle, float i)  // runs every ti
 }
 
 // -1 when no team completely owns all keys yet
-float kh_Key_AllOwnedByWhichTeam()  // constantly called. check to see if all the keys are owned by the same team
+int kh_Key_AllOwnedByWhichTeam()  // constantly called. check to see if all the keys are owned by the same team
 {
        entity key;
-       float teem;
-       float keys;
-
-       teem = -1;
-       keys = kh_teams;
+       int teem = -1;
+       int keys = NumTeams(kh_teams);
        FOR_EACH_KH_KEY(key)
        {
                if(!key.owner)
@@ -866,15 +793,15 @@ float kh_Key_AllOwnedByWhichTeam()  // constantly called. check to see if all th
 void kh_Key_DropOne(entity key)
 {
        // prevent collecting this one for some time
-       entity player;
-       player = key.owner;
+       entity player = key.owner;
 
        key.kh_droptime = time;
        key.enemy = player;
 
        kh_Scores_Event(player, key, "dropkey", 0, 0);
        PlayerScore_Add(player, SP_KH_LOSSES, 1);
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(key, INFO_KEYHUNT_DROP), player.netname);
+       int realteam = kh_Team_ByID(key.count);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_DROP), player.netname);
 
        kh_Key_AssignTo(key, NULL);
        makevectors(player.v_angle);
@@ -888,19 +815,20 @@ void kh_Key_DropOne(entity key)
 
 void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies
 {
-       entity key;
-       entity mypusher;
        if(player.kh_next)
        {
-               mypusher = NULL;
+               entity mypusher = NULL;
                if(player.pusher)
                        if(time < player.pushltime)
                                mypusher = player.pusher;
+
+               entity key;
                while((key = player.kh_next))
                {
                        kh_Scores_Event(player, key, "losekey", 0, 0);
                        PlayerScore_Add(player, SP_KH_LOSSES, 1);
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(key, INFO_KEYHUNT_LOST), player.netname);
+                       int realteam = kh_Team_ByID(key.count);
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_LOST), player.netname);
                        kh_Key_AssignTo(key, NULL);
                        makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random());
                        key.velocity = W_CalculateProjectileVelocity(player, player.velocity, autocvar_g_balance_keyhunt_dropvelocity * v_forward, false);
@@ -913,24 +841,23 @@ void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies
        }
 }
 
-float kh_CheckPlayers(float num)
+int kh_GetMissingTeams()
 {
-       if(num < kh_teams)
+       int missing_teams = 0;
+       for(int i = 0; i < NumTeams(kh_teams); ++i)
        {
-               float t_team = kh_Team_ByID(num);
-               float players = 0;
+               int teem = kh_Team_ByID(i);
+               int players = 0;
                FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
-                       if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == t_team)
+                       if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem)
                                ++players;
                ));
-
-               if (!players) { return t_team; }
+               if (!players)
+                       missing_teams |= pow(2, i);
        }
-       return 0;
+       return missing_teams;
 }
 
-#define KH_READY_TEAMS() (!p1 + !p2 + ((kh_teams >= 3) ? !p3 : p3) + ((kh_teams >= 4) ? !p4 : p4))
-#define KH_READY_TEAMS_OK() (KH_READY_TEAMS() == kh_teams)
 void kh_WaitForPlayers()  // delay start of the round until enough players are present
 {
        if(time < game_starttime)
@@ -939,9 +866,9 @@ void kh_WaitForPlayers()  // delay start of the round until enough players are p
                return;
        }
 
-       static float prev_missing_teams_mask;
-       float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
-       if(KH_READY_TEAMS_OK())
+       static int prev_missing_teams_mask;
+       int missing_teams_mask = kh_GetMissingTeams();
+       if(!missing_teams_mask)
        {
                if(prev_missing_teams_mask > 0)
                        Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS);
@@ -959,9 +886,6 @@ void kh_WaitForPlayers()  // delay start of the round until enough players are p
                }
                else
                {
-                       float missing_teams_mask = boolean(p1) + boolean(p2) * 2;
-                       if(kh_teams >= 3) missing_teams_mask += boolean(p3) * 4;
-                       if(kh_teams >= 4) missing_teams_mask += boolean(p4) * 8;
                        if(prev_missing_teams_mask != missing_teams_mask)
                        {
                                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
@@ -982,16 +906,13 @@ void kh_EnableTrackingDevice()  // runs after each round
 
 void kh_StartRound()  // runs at the start of each round
 {
-       float i, players, teem;
-
        if(time < game_starttime)
        {
                kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers);
                return;
        }
 
-       float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
-       if(!KH_READY_TEAMS_OK())
+       if(kh_GetMissingTeams())
        {
                kh_Controller_SetThink(1, kh_WaitForPlayers);
                return;
@@ -1000,10 +921,10 @@ void kh_StartRound()  // runs at the start of each round
        Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT);
        Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER);
 
-       for(i = 0; i < kh_teams; ++i)
+       for(int i = 0; i < NumTeams(kh_teams); ++i)
        {
-               teem = kh_Team_ByID(i);
-               players = 0;
+               int teem = kh_Team_ByID(i);
+               int players = 0;
                entity my_player = NULL;
                FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                        if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem)
@@ -1013,7 +934,7 @@ void kh_StartRound()  // runs at the start of each round
                                        my_player = it;
                        }
                ));
-               kh_Key_Spawn(my_player, 360 * i / kh_teams, i);
+               kh_Key_Spawn(my_player, 360 * i / NumTeams(kh_teams), i);
        }
 
        kh_tracking_enabled = false;
@@ -1030,10 +951,8 @@ float kh_HandleFrags(entity attacker, entity targ, float f)  // adds to the play
        {
                if(attacker.team == targ.team)
                {
-                       entity k;
-                       float nk;
-                       nk = 0;
-                       for(k = targ.kh_next; k != NULL; k = k.kh_next)
+                       int nk = 0;
+                       for(entity k = targ.kh_next; k != NULL; k = k.kh_next)
                                ++nk;
                        kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0);
                }
@@ -1053,9 +972,17 @@ void kh_Initialize()  // sets up th KH environment
        // setup variables
        kh_teams = autocvar_g_keyhunt_teams_override;
        if(kh_teams < 2)
-               kh_teams = autocvar_g_keyhunt_teams;
+               kh_teams = cvar("g_keyhunt_teams"); // read the cvar directly as it gets written earlier in the same frame
        kh_teams = bound(2, kh_teams, 4);
 
+       int teams = 0;
+       if(kh_teams >= 1) teams |= BIT(0);
+       if(kh_teams >= 2) teams |= BIT(1);
+       if(kh_teams >= 3) teams |= BIT(2);
+       if(kh_teams >= 4) teams |= BIT(3);
+
+       kh_teams = teams; // now set it?
+
        // make a KH entity for controlling the game
        kh_controller = spawn();
        setthink(kh_controller, kh_Controller_Think);
@@ -1085,7 +1012,7 @@ void kh_finalize()
 {
        // to be called before intermission
        kh_FinishRound();
-       remove(kh_controller);
+       delete(kh_controller);
        kh_controller = NULL;
 }
 
@@ -1133,7 +1060,7 @@ void havocbot_role_kh_carrier(entity this)
 
        if (!(this.kh_next))
        {
-               LOG_TRACE("changing role to freelancer\n");
+               LOG_TRACE("changing role to freelancer");
                this.havocbot_role = havocbot_role_kh_freelancer;
                this.havocbot_role_timeout = 0;
                return;
@@ -1160,7 +1087,7 @@ void havocbot_role_kh_defense(entity this)
 
        if (this.kh_next)
        {
-               LOG_TRACE("changing role to carrier\n");
+               LOG_TRACE("changing role to carrier");
                this.havocbot_role = havocbot_role_kh_carrier;
                this.havocbot_role_timeout = 0;
                return;
@@ -1170,7 +1097,7 @@ void havocbot_role_kh_defense(entity this)
                this.havocbot_role_timeout = time + random() * 10 + 20;
        if (time > this.havocbot_role_timeout)
        {
-               LOG_TRACE("changing role to freelancer\n");
+               LOG_TRACE("changing role to freelancer");
                this.havocbot_role = havocbot_role_kh_freelancer;
                this.havocbot_role_timeout = 0;
                return;
@@ -1201,7 +1128,7 @@ void havocbot_role_kh_offense(entity this)
 
        if (this.kh_next)
        {
-               LOG_TRACE("changing role to carrier\n");
+               LOG_TRACE("changing role to carrier");
                this.havocbot_role = havocbot_role_kh_carrier;
                this.havocbot_role_timeout = 0;
                return;
@@ -1211,7 +1138,7 @@ void havocbot_role_kh_offense(entity this)
                this.havocbot_role_timeout = time + random() * 10 + 20;
        if (time > this.havocbot_role_timeout)
        {
-               LOG_TRACE("changing role to freelancer\n");
+               LOG_TRACE("changing role to freelancer");
                this.havocbot_role = havocbot_role_kh_freelancer;
                this.havocbot_role_timeout = 0;
                return;
@@ -1243,7 +1170,7 @@ void havocbot_role_kh_freelancer(entity this)
 
        if (this.kh_next)
        {
-               LOG_TRACE("changing role to carrier\n");
+               LOG_TRACE("changing role to carrier");
                this.havocbot_role = havocbot_role_kh_carrier;
                this.havocbot_role_timeout = 0;
                return;
@@ -1255,12 +1182,12 @@ void havocbot_role_kh_freelancer(entity this)
        {
                if (random() < 0.5)
                {
-                       LOG_TRACE("changing role to offense\n");
+                       LOG_TRACE("changing role to offense");
                        this.havocbot_role = havocbot_role_kh_offense;
                }
                else
                {
-                       LOG_TRACE("changing role to defense\n");
+                       LOG_TRACE("changing role to defense");
                        this.havocbot_role = havocbot_role_kh_defense;
                }
                this.havocbot_role_timeout = 0;
@@ -1269,12 +1196,10 @@ void havocbot_role_kh_freelancer(entity this)
 
        if (this.bot_strategytime < time)
        {
-               float key_owner_team;
-
                this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
 
-               key_owner_team = kh_Key_AllOwnedByWhichTeam();
+               int key_owner_team = kh_Key_AllOwnedByWhichTeam();
                if(key_owner_team == this.team)
                        havocbot_goalrating_kh(this, 10, 0.1, 0.1); // defend anyway
                else if(key_owner_team == -1)
@@ -1329,7 +1254,7 @@ MUTATOR_HOOKFUNCTION(kh, MatchEnd)
        kh_finalize();
 }
 
-MUTATOR_HOOKFUNCTION(kh, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(kh, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(0, float) = kh_teams;
 }
@@ -1378,7 +1303,7 @@ MUTATOR_HOOKFUNCTION(kh, HavocBot_ChooseRole)
 MUTATOR_HOOKFUNCTION(kh, DropSpecialItems)
 {
        entity frag_target = M_ARGV(0, entity);
-       
+
        kh_Key_DropAll(frag_target, false);
 }
 
@@ -1386,5 +1311,3 @@ MUTATOR_HOOKFUNCTION(kh, reset_map_global)
 {
        kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round + (game_starttime - time), kh_StartRound);
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..d0fb5f95bc2ff0c455a96da9900932b586e75e8f 100644 (file)
@@ -1,3 +1,50 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
+int autocvar_g_keyhunt_point_leadlimit;
+bool autocvar_g_keyhunt_team_spawns;
+void kh_Initialize();
+
+REGISTER_MUTATOR(kh, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               kh_Initialize();
+
+               ActivateTeamplay();
+               SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, autocvar_timelimit_override, -1);
+               if (autocvar_g_keyhunt_team_spawns)
+                       have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back kh_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext )
+
+// ALL OF THESE should be removed in the future, as other code should not have to care
+
+// used by bots:
+bool kh_tracking_enabled;
+.entity kh_next;
+
+USING(kh_Think_t, void());
+void kh_StartRound();
+void kh_Controller_SetThink(float t, kh_Think_t func);
index adfd6eb6a76648b90ddb9f0b33413ec3dae01038..25f6d3e0ab03ae441a08313a3606bf7869222b25 100644 (file)
@@ -1,53 +1,8 @@
 #include "gamemode_lms.qh"
-#ifndef GAMEMODE_LMS_H
-#define GAMEMODE_LMS_H
-
-#define autocvar_g_lms_lives_override cvar("g_lms_lives_override")
-void lms_Initialize();
-
-REGISTER_MUTATOR(lms, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               lms_Initialize();
-
-               SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1);
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back lms_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-// scoreboard stuff
-const float SP_LMS_LIVES = 4;
-const float SP_LMS_RANK = 5;
-
-// lives related defs
-float lms_lowest_lives;
-float lms_next_place;
-float LMS_NewPlayerLives();
-
-#endif
-
-#ifdef IMPLEMENTATION
 
 #include <common/mutators/mutator/instagib/items.qc>
 #include <server/campaign.qh>
-#include <server/command/cmd.qh>
+#include <server/command/_mod.qh>
 
 int autocvar_g_lms_extra_lives;
 bool autocvar_g_lms_join_anytime;
@@ -117,7 +72,10 @@ int WinningCondition_LMS()
                                // a winner!
                                // and assign him his first place
                                PlayerScore_Add(head, SP_LMS_RANK, 1);
-                               return WINNING_YES;
+                               if(warmup_stage)
+                                       return WINNING_NO;
+                               else
+                                       return WINNING_YES;
                        }
                }
        }
@@ -132,7 +90,7 @@ int WinningCondition_LMS()
                {
                        // SNAFU (maybe a draw game?)
                        ClearWinners();
-                       LOG_TRACE("No players, ending game.\n");
+                       LOG_TRACE("No players, ending game.");
                        return WINNING_YES;
                }
        }
@@ -155,42 +113,104 @@ int WinningCondition_LMS()
 MUTATOR_HOOKFUNCTION(lms, reset_map_global)
 {
        lms_lowest_lives = 999;
-       lms_next_place = player_count;
 }
 
 MUTATOR_HOOKFUNCTION(lms, reset_map_players)
 {
-       if(restart_mapalreadyrestarted || (time < game_starttime))
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(PlayerScore_Add(it, SP_LMS_LIVES, LMS_NewPlayerLives())));
+       FOREACH_CLIENT(true, {
+               TRANSMUTE(Player, it);
+               it.frags = FRAGS_PLAYER;
+               PlayerScore_Add(it, SP_LMS_LIVES, LMS_NewPlayerLives());
+               PutClientInServer(it);
+       });
 }
 
 MUTATOR_HOOKFUNCTION(lms, PutClientInServer)
 {
        entity player = M_ARGV(0, entity);
 
-       // player is dead and becomes observer
-       // FIXME fix LMS scoring for new system
-       if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0)
-       {
+       if(player.frags == FRAGS_SPECTATOR)
                TRANSMUTE(Observer, player);
+       else
+       {
+               float tl = PlayerScore_Add(player, SP_LMS_LIVES, 0);
+               if(tl < lms_lowest_lives)
+                       lms_lowest_lives = tl;
+               if(tl <= 0)
+                       TRANSMUTE(Observer, player);
+               if(warmup_stage)
+                       PlayerScore_Add(player, SP_LMS_RANK, -PlayerScore_Add(player, SP_LMS_RANK, 0));
+       }
+}
+
+MUTATOR_HOOKFUNCTION(lms, ForbidSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(warmup_stage)
+               return false;
+       if(player.frags == FRAGS_SPECTATOR)
+               return true;
+       if(PlayerScore_Add(player, SP_LMS_LIVES, 0) <= 0)
+       {
                Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_LMS_NOLIVES);
+               return true;
        }
+       return false;
 }
 
 MUTATOR_HOOKFUNCTION(lms, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        frag_target.respawn_flags |= RESPAWN_FORCE;
 }
 
 void lms_RemovePlayer(entity player)
 {
-       // Only if the player cannot play at all
-       if(PlayerScore_Add(player, SP_LMS_RANK, 0) == 666)
-               player.frags = FRAGS_SPECTATOR;
-       else
-               player.frags = FRAGS_LMS_LOSER;
+       static int quitters = 0;
+       float player_rank = PlayerScore_Add(player, SP_LMS_RANK, 0);
+       if (!player_rank)
+       {
+               int pl_cnt = 0;
+               FOREACH_CLIENT(IS_PLAYER(it), { pl_cnt++; });
+               if (player.lms_spectate_warning != 2)
+               {
+                       if(IS_BOT_CLIENT(player))
+                               bot_clear(player);
+                       player.frags = FRAGS_LMS_LOSER;
+                       PlayerScore_Add(player, SP_LMS_RANK, pl_cnt + 1);
+               }
+               else
+               {
+                       lms_lowest_lives = 999;
+                       FOREACH_CLIENT(true, {
+                               if (it.frags == FRAGS_LMS_LOSER)
+                               {
+                                       float it_rank = PlayerScore_Add(it, SP_LMS_RANK, 0);
+                                       if (it_rank > player_rank && it_rank <= 256)
+                                               PlayerScore_Add(it, SP_LMS_RANK, -1);
+                                       lms_lowest_lives = 0;
+                               }
+                               else if (it.frags != FRAGS_SPECTATOR)
+                               {
+                                       float tl = PlayerScore_Add(it, SP_LMS_LIVES, 0);
+                                       if(tl < lms_lowest_lives)
+                                               lms_lowest_lives = tl;
+                               }
+                       });
+                       PlayerScore_Add(player, SP_LMS_RANK, 665 - quitters); // different from 666
+                       if(!warmup_stage)
+                       {
+                               PlayerScore_Add(player, SP_LMS_LIVES, -PlayerScore_Add(player, SP_LMS_LIVES, 0));
+                               ++quitters;
+                       }
+                       player.frags = FRAGS_LMS_LOSER;
+                       TRANSMUTE(Observer, player);
+               }
+               if (pl_cnt == 2 && !warmup_stage) // a player is forfeiting leaving only one player
+                       lms_lowest_lives = 0; // end the game now!
+       }
 
        if(player.killcount != FRAGS_SPECTATOR)
                if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0 && player.lms_spectate_warning != 2)
@@ -223,7 +243,7 @@ MUTATOR_HOOKFUNCTION(lms, ClientConnect)
 
        if(PlayerScore_Add(player, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
        {
-               PlayerScore_Add(player, SP_LMS_RANK, 666);
+               PlayerScore_Add(player, SP_LMS_RANK, 666); // mark as forced spectator for the hud code
                player.frags = FRAGS_SPECTATOR;
        }
 }
@@ -253,21 +273,23 @@ MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill)
 {
        entity frag_target = M_ARGV(1, entity);
 
-       // remove a life
-       float tl;
-       tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
-       if(tl < lms_lowest_lives)
-               lms_lowest_lives = tl;
-       if(tl <= 0)
+       if (!warmup_stage)
        {
-               if(!lms_next_place)
-                       lms_next_place = player_count;
-               else
-                       lms_next_place = min(lms_next_place, player_count);
-               PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again
-               --lms_next_place;
+               // remove a life
+               int tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
+               if(tl < lms_lowest_lives)
+                       lms_lowest_lives = tl;
+               if(tl <= 0)
+               {
+                       int pl_cnt = 0;
+                       FOREACH_CLIENT(IS_PLAYER(it), { pl_cnt++; });
+                       if(IS_BOT_CLIENT(frag_target))
+                               bot_clear(frag_target);
+                       frag_target.frags = FRAGS_LMS_LOSER;
+                       PlayerScore_Add(frag_target, SP_LMS_RANK, pl_cnt);
+               }
        }
-       M_ARGV(2, float) = 0;
+       M_ARGV(2, float) = 0; // frag score
 
        return true;
 }
@@ -346,8 +368,8 @@ MUTATOR_HOOKFUNCTION(lms, ItemTouch)
 MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
 {
        FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
-               ++M_ARGV(0, int);
-               ++M_ARGV(1, int);
+               ++M_ARGV(0, int); // activerealplayers
+               ++M_ARGV(1, int); // realplayers
        ));
 
        return true;
@@ -357,17 +379,18 @@ MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate)
 {
     entity player = M_ARGV(0, entity);
 
-       if(player.lms_spectate_warning)
+       if(warmup_stage || player.lms_spectate_warning)
        {
                // for the forfeit message...
                player.lms_spectate_warning = 2;
-               // mark player as spectator
-               PlayerScore_Add(player, SP_LMS_RANK, 666 - PlayerScore_Add(player, SP_LMS_RANK, 0));
        }
        else
        {
-               player.lms_spectate_warning = 1;
-               sprint(player, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n");
+               if(player.frags != FRAGS_SPECTATOR && player.frags != FRAGS_LMS_LOSER)
+               {
+                       player.lms_spectate_warning = 1;
+                       sprint(player, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n");
+               }
                return MUT_SPECCMD_RETURN;
        }
        return MUT_SPECCMD_CONTINUE;
@@ -392,7 +415,7 @@ MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus)
 MUTATOR_HOOKFUNCTION(lms, AddPlayerScore)
 {
        if(gameover)
-       if(M_ARGV(0, int) == SP_LMS_RANK) // score field
+       if(M_ARGV(0, entity) == SP_LMS_RANK) // score field
                return true; // allow writing to this field in intermission as it is needed for newly joining players
 }
 
@@ -408,10 +431,6 @@ void lms_ScoreRules()
 void lms_Initialize()
 {
        lms_lowest_lives = 9999;
-       lms_next_place = 0;
 
        lms_ScoreRules();
 }
-
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..1f1d2d4c3419654509a7d402f42fe41cbbdaa3ea 100644 (file)
@@ -1,3 +1,38 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+.float lms_spectate_warning;
+#define autocvar_g_lms_lives_override cvar("g_lms_lives_override")
+void lms_Initialize();
+
+REGISTER_MUTATOR(lms, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               lms_Initialize();
+
+               SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1);
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back lms_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+
+// lives related defs
+float lms_lowest_lives;
+float LMS_NewPlayerLives();
index 132eba1620515b6e8c7d9478029deeac3d38cca5..abe6b846e73fb714e4f132bef49ff7df0d89cd4d 100644 (file)
@@ -1,42 +1,5 @@
 #include "gamemode_race.qh"
 
-#ifndef GAMEMODE_RACE_H
-#define GAMEMODE_RACE_H
-
-void rc_SetLimits();
-void race_Initialize();
-
-REGISTER_MUTATOR(rc, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-
-               rc_SetLimits();
-               race_Initialize();
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back race_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-#endif
-
-#ifdef IMPLEMENTATION
-
 #include <server/race.qh>
 
 #define autocvar_g_race_laps_limit cvar("g_race_laps_limit")
@@ -51,23 +14,22 @@ void havocbot_role_race(entity this)
        if(IS_DEAD(this))
                return;
 
-       entity e;
        if (this.bot_strategytime < time)
        {
                this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
 
-               for(e = NULL; (e = find(e, classname, "trigger_race_checkpoint")) != NULL; )
+               IL_EACH(g_racecheckpoints, true,
                {
-                       if(e.cnt == this.race_checkpoint)
+                       if(it.cnt == this.race_checkpoint)
                        {
-                               navigation_routerating(this, e, 1000000, 5000);
+                               navigation_routerating(this, it, 1000000, 5000);
                        }
                        else if(this.race_checkpoint == -1)
                        {
-                               navigation_routerating(this, e, 1000000, 5000);
+                               navigation_routerating(this, it, 1000000, 5000);
                        }
-               }
+               });
 
                navigation_goalrating_end(this);
        }
@@ -78,7 +40,7 @@ void race_ScoreRules()
        ScoreRules_basics(race_teams, 0, 0, false);
        if(race_teams)
        {
-               ScoreInfo_SetLabel_TeamScore(  ST_RACE_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
+               ScoreInfo_SetLabel_TeamScore(  ST_RACE_LAPS,    "laps",       SFL_SORT_PRIO_PRIMARY);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME,    "time",      SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest",   SFL_LOWER_IS_BETTER | SFL_TIME);
@@ -143,8 +105,9 @@ float WinningCondition_QualifyingThenRace(float limit)
 MUTATOR_HOOKFUNCTION(rc, PlayerPhysics)
 {
        entity player = M_ARGV(0, entity);
+       float dt = M_ARGV(1, float);
 
-       player.race_movetime_frac += PHYS_INPUT_TIMELENGTH;
+       player.race_movetime_frac += dt;
        float f = floor(player.race_movetime_frac);
        player.race_movetime_frac -= f;
        player.race_movetime_count += f;
@@ -159,7 +122,7 @@ MUTATOR_HOOKFUNCTION(rc, PlayerPhysics)
                if(player.race_penalty)
                {
                        player.velocity = '0 0 0';
-                       player.movetype = MOVETYPE_NONE;
+                       set_movetype(player, MOVETYPE_NONE);
                        player.disableclientprediction = 2;
                }
        }
@@ -318,7 +281,7 @@ MUTATOR_HOOKFUNCTION(rc, PutClientInServer)
 MUTATOR_HOOKFUNCTION(rc, PlayerDies)
 {
        entity frag_target = M_ARGV(2, entity);
-       
+
        frag_target.respawn_flags |= RESPAWN_FORCE;
        race_AbandonRaceCheck(frag_target);
 }
@@ -380,7 +343,7 @@ MUTATOR_HOOKFUNCTION(rc, ForbidPlayerScore_Clear)
                return true; // in qualifying, you don't lose score by observing
 }
 
-MUTATOR_HOOKFUNCTION(rc, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(rc, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(0, float) = race_teams;
 }
@@ -463,6 +426,14 @@ void rc_SetLimits()
        {
                ActivateTeamplay();
                race_teams = bound(2, autocvar_g_race_teams, 4);
+               int teams = 0;
+               if(race_teams >= 1) teams |= BIT(0);
+               if(race_teams >= 2) teams |= BIT(1);
+               if(race_teams >= 3) teams |= BIT(2);
+               if(race_teams >= 4) teams |= BIT(3);
+
+               race_teams = teams; // now set it?
+
                have_team_spawns = -1; // request team spawns
        }
        else
@@ -496,5 +467,3 @@ void rc_SetLimits()
                g_race_qualifying = 0;
        SetLimits(fraglimit_override, leadlimit_override, timelimit_override, qualifying_override);
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..ec71a62d178483d2277228801751f147d721504d 100644 (file)
@@ -1,3 +1,33 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+void rc_SetLimits();
+void race_Initialize();
+
+REGISTER_MUTATOR(rc, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+
+               rc_SetLimits();
+               race_Initialize();
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back race_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
index 787412908513449a6f5c28a81d4a47557ca95023..101f57fcc4d6efb96ecd5d2ed5157b0065c29060 100644 (file)
@@ -1,45 +1,5 @@
 #include "gamemode_tdm.qh"
-#ifndef GAMEMODE_TDM_H
-#define GAMEMODE_TDM_H
 
-int autocvar_g_tdm_point_limit;
-int autocvar_g_tdm_point_leadlimit;
-bool autocvar_g_tdm_team_spawns;
-void tdm_DelayedInit(entity this);
-
-REGISTER_MUTATOR(tdm, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE);
-
-               ActivateTeamplay();
-               SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, autocvar_timelimit_override, -1);
-               if (autocvar_g_tdm_team_spawns)
-                       have_team_spawns = -1; // request team spawns
-       }
-
-       MUTATOR_ONROLLBACK_OR_REMOVE
-       {
-               // we actually cannot roll back tdm_Initialize here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
-       }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-#endif
-
-#ifdef IMPLEMENTATION
 int autocvar_g_tdm_teams;
 int autocvar_g_tdm_teams_override;
 
@@ -51,20 +11,21 @@ Keys:
 "cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
 spawnfunc(tdm_team)
 {
-       if(!g_tdm || !this.cnt) { remove(this); return; }
+       if(!g_tdm || !this.cnt) { delete(this); return; }
 
        this.classname = "tdm_team";
        this.team = this.cnt + 1;
 }
 
 // code from here on is just to support maps that don't have team entities
-void tdm_SpawnTeam (string teamname, float teamcolor)
+void tdm_SpawnTeam (string teamname, int teamcolor)
 {
-       entity this = new(tdm_team);
+       entity this = new_pure(tdm_team);
        this.netname = teamname;
-       this.cnt = teamcolor;
+       this.cnt = teamcolor - 1;
+       this.team = teamcolor;
        this.spawnfunc_checked = true;
-       spawnfunc_tdm_team(this);
+       //spawnfunc_tdm_team(this);
 }
 
 void tdm_DelayedInit(entity this)
@@ -72,20 +33,31 @@ void tdm_DelayedInit(entity this)
        // if no teams are found, spawn defaults
        if(find(NULL, classname, "tdm_team") == NULL)
        {
-               LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway.\n");
+               LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway.");
 
-               int numteams = min(4, autocvar_g_tdm_teams_override);
+               int numteams = autocvar_g_tdm_teams_override;
 
                if(numteams < 2) { numteams = autocvar_g_tdm_teams; }
                numteams = bound(2, numteams, 4);
 
-               float i;
-               for(i = 1; i <= numteams; ++i)
-                       tdm_SpawnTeam(Team_ColorName(Team_NumberToTeam(i)), Team_NumberToTeam(i) - 1);
+               int teams = 0;
+               if(numteams >= 1) teams |= BIT(0);
+               if(numteams >= 2) teams |= BIT(1);
+               if(numteams >= 3) teams |= BIT(2);
+               if(numteams >= 4) teams |= BIT(3);
+
+               if(teams & BIT(0))
+                       tdm_SpawnTeam("Red", NUM_TEAM_1);
+               if(teams & BIT(1))
+                       tdm_SpawnTeam("Blue", NUM_TEAM_2);
+               if(teams & BIT(2))
+                       tdm_SpawnTeam("Yellow", NUM_TEAM_3);
+               if(teams & BIT(3))
+                       tdm_SpawnTeam("Pink", NUM_TEAM_4);
        }
 }
 
-MUTATOR_HOOKFUNCTION(tdm, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(tdm, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(1, string) = "tdm_team";
        return true;
@@ -96,5 +68,3 @@ MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining)
        // announce remaining frags
        return true;
 }
-
-#endif
index 399830dad294adb0eb6ca0ca71fe6af6e0976604..e7efbae7f5b13a9930a3a0523f61dc9d5fe0b6b6 100644 (file)
@@ -1,3 +1,38 @@
 #pragma once
 
 #include "../gamemode.qh"
+
+int autocvar_g_tdm_point_limit;
+int autocvar_g_tdm_point_leadlimit;
+bool autocvar_g_tdm_team_spawns;
+void tdm_DelayedInit(entity this);
+
+REGISTER_MUTATOR(tdm, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE);
+
+               ActivateTeamplay();
+               SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, autocvar_timelimit_override, -1);
+               if (autocvar_g_tdm_team_spawns)
+                       have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back tdm_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/pathlib/_all.inc b/qcsrc/server/pathlib/_all.inc
deleted file mode 100644 (file)
index 7a06615..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef DEBUGPATHING
-       #define DEBUGPATHING 0
-#endif
-
-#include "costs.qc"
-#include "expandnode.qc"
-#include "main.qc"
-#include "movenode.qc"
-#include "path_waypoint.qc"
-#include "utility.qc"
-#if DEBUGPATHING
-       #include "debug.qc"
-#endif
index 6b89bb640223c1776d48cf5f152f285789258868..fdb95d2f608b967be48675f96e549975693e766e 100644 (file)
@@ -1,5 +1,4 @@
 #include "costs.qh"
-#include "pathlib.qh"
 
 float pathlib_g_static(entity parent,vector to, float static_cost)
 {
index 6f70f09beec2219624baeca92e2cd7deaa104fb4..811c031aff3d968a056e6dffa974179a409ce321 100644 (file)
@@ -1 +1,2 @@
 #pragma once
+#include "pathlib.qh"
index 0a350df2c2f467f4ed03958639fd31607b7dd133..b84ae6414e20ba637d8f81c86bf946040bf37cb5 100644 (file)
@@ -1,5 +1,6 @@
 #include "debug.qh"
-#include "pathlib.qh"
+
+#if DEBUGPATHING
 
 MODEL(SQUARE,       "models/pathlib/square.md3");
 MODEL(SQUARE_GOOD,  "models/pathlib/goodsquare.md3");
@@ -119,3 +120,5 @@ void pathlib_showedge(vector where,float _lifetime,float rot)
     //e.angles_x += 90;
 
 }
+
+#endif
index 6f70f09beec2219624baeca92e2cd7deaa104fb4..811c031aff3d968a056e6dffa974179a409ce321 100644 (file)
@@ -1 +1,2 @@
 #pragma once
+#include "pathlib.qh"
index b6e1350789c3904bcd1e7f97a0bee5db9d82c70b..36c7c1d3147968284bf8a478c68f145cb7e965e6 100644 (file)
@@ -6,15 +6,11 @@
 
 void pathlib_deletepath(entity start)
 {
-    entity e;
-
-    e = findchainentity(owner, start);
-    while(e)
+    FOREACH_ENTITY_ENT(owner, start,
     {
-        setthink(e, SUB_Remove);
-        e.nextthink = time;
-        e = e.chain;
-    }
+        setthink(it, SUB_Remove);
+        it.nextthink = time;
+    });
 }
 
 //#define PATHLIB_NODEEXPIRE 0.05
@@ -59,7 +55,6 @@ entity pathlib_mknode(vector where,entity parent)
     setsize(node, '0 0 0', '0 0 0');
 
     setorigin(node, where);
-    node.medium = pointcontents(where);
 #if DEBUGPATHING
     pathlib_showsquare(where, 1 ,15);
 #endif
@@ -79,7 +74,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
 
     if(inwater(parent.origin))
     {
-        LOG_TRACE("FromWater\n");
+        LOG_TRACE("FromWater");
         pathlib_expandnode = pathlib_expandnode_box;
         pathlib_movenode   = pathlib_swimnode;
     }
@@ -87,13 +82,13 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
     {
         if(inwater(to))
         {
-            LOG_TRACE("ToWater\n");
+            LOG_TRACE("ToWater");
             pathlib_expandnode = pathlib_expandnode_box;
             pathlib_movenode   = pathlib_walknode;
         }
         else
         {
-            LOG_TRACE("LandToLoand\n");
+            LOG_TRACE("LandToLoand");
             //if(edge_check(parent.origin))
             //    return 0;
 
@@ -106,7 +101,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
     node = pathlib_nodeatpoint(to);
     if(node)
     {
-        LOG_TRACE("NodeAtPoint\n");
+        LOG_TRACE("NodeAtPoint");
         ++pathlib_merge_cnt;
 
         if(node.owner == openlist)
@@ -139,7 +134,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
     {
         //pathlib_showsquare(where, 0 ,30);
         //pathlib_showsquare(parent.origin, 1 ,30);
-        LOG_TRACE("pathlib_movenode_goodnode = 0\n");
+        LOG_TRACE("pathlib_movenode_goodnode = 0");
         return 0;
     }
 
@@ -147,9 +142,9 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
 
     if(pathlib_nodeatpoint(where))
     {
-        LOG_TRACE("NAP WHERE :",vtos(where),"\n");
-        LOG_TRACE("not NAP TO:",vtos(to),"\n");
-        LOG_TRACE("NAP-NNAP:",ftos(vlen(to-where)),"\n\n");
+        LOG_TRACE("NAP WHERE :",vtos(where));
+        LOG_TRACE("not NAP TO:",vtos(to));
+        LOG_TRACE("NAP-NNAP:",ftos(vlen(to-where)));
         return 0;
     }
 
@@ -157,7 +152,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
     if(doedge)
         if (!tile_check(parent, where))
         {
-            LOG_TRACE("tile_check fail\n");
+            LOG_TRACE("tile_check fail");
 #if DEBUGPATHING
             pathlib_showsquare(where, 0 ,30);
 #endif
@@ -218,9 +213,6 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go
 
 entity pathlib_getbestopen()
 {
-    entity node;
-    entity bestnode;
-
     if(best_open_node)
     {
         ++pathlib_bestcash_hits;
@@ -229,19 +221,14 @@ entity pathlib_getbestopen()
         return best_open_node;
     }
 
-    node = findchainentity(owner,openlist);
-    if(!node)
-        return NULL;
-
-    bestnode = node;
-    while(node)
+    entity bestnode = NULL;
+    FOREACH_ENTITY_ENT(owner, openlist,
     {
-        ++pathlib_bestopen_seached;
-        if(node.pathlib_node_f < bestnode.pathlib_node_f)
-            bestnode = node;
+        ++pathlib_bestopen_searched;
 
-        node = node.chain;
-    }
+        if(!bestnode || it.pathlib_node_f < bestnode.pathlib_node_f)
+            bestnode = it;
+    });
 
     return bestnode;
 }
@@ -251,7 +238,7 @@ void pathlib_close_node(entity node,vector goal)
 
     if(node.owner == closedlist)
     {
-        LOG_TRACE("Pathlib: Tried to close a closed node!\n");
+        LOG_TRACE("Pathlib: Tried to close a closed node!");
         return;
     }
 
@@ -300,21 +287,21 @@ void pathlib_cleanup()
     }
 
     if(openlist)
-        remove(openlist);
+        delete(openlist);
 
     if(closedlist)
-        remove(closedlist);
+        delete(closedlist);
 
     openlist       = NULL;
     closedlist     = NULL;
 
 }
 
-float Cosine_Interpolate(float a, float b, float x)
+float Cosine_Interpolate(float a, float b, float c)
 {
     float ft,f;
 
-       ft = x * 3.1415927;
+       ft = c * 3.1415927;
        f = (1 - cos(ft)) * 0.5;
 
        return  a*(1-f) + b*f;
@@ -432,7 +419,7 @@ entity pathlib_astar(entity this, vector from,vector to)
     pathlib_made_cnt         = 0;
     pathlib_merge_cnt        = 0;
     pathlib_searched_cnt     = 0;
-    pathlib_bestopen_seached = 0;
+    pathlib_bestopen_searched = 0;
     pathlib_bestcash_hits    = 0;
     pathlib_bestcash_saved   = 0;
 
@@ -467,12 +454,12 @@ entity pathlib_astar(entity this, vector from,vector to)
     to.y = fsnap(to.y, pathlib_gridsize);
     //to_z += 32;
 
-    LOG_TRACE("AStar init\n");
+    LOG_TRACE("AStar init");
     path = pathlib_mknode(from, NULL);
     pathlib_close_node(path, to);
     if(pathlib_foundgoal)
     {
-        LOG_TRACE("AStar: Goal found on first node!\n");
+        LOG_TRACE("AStar: Goal found on first node!");
 
         open           = new(path_end);
         open.owner     = open;
@@ -485,7 +472,7 @@ entity pathlib_astar(entity this, vector from,vector to)
 
     if(pathlib_expandnode(path, from, to) <= 0)
     {
-        LOG_TRACE("AStar path fail.\n");
+        LOG_TRACE("AStar path fail.");
         pathlib_cleanup();
 
         return NULL;
@@ -503,11 +490,11 @@ entity pathlib_astar(entity this, vector from,vector to)
     {
         if((gettime(GETTIME_REALTIME) - pathlib_starttime) > pathlib_maxtime)
         {
-            LOG_TRACE("Path took to long to compute!\n");
-            LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt),"\n");
-            LOG_TRACE("Nodes -    open: ", ftos(pathlib_open_cnt),"\n");
-            LOG_TRACE("Nodes -  merged: ", ftos(pathlib_merge_cnt),"\n");
-            LOG_TRACE("Nodes -  closed: ", ftos(pathlib_closed_cnt),"\n");
+            LOG_TRACE("Path took to long to compute!");
+            LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt));
+            LOG_TRACE("Nodes -    open: ", ftos(pathlib_open_cnt));
+            LOG_TRACE("Nodes -  merged: ", ftos(pathlib_merge_cnt));
+            LOG_TRACE("Nodes -  closed: ", ftos(pathlib_closed_cnt));
 
             pathlib_cleanup();
             return NULL;
@@ -524,7 +511,7 @@ entity pathlib_astar(entity this, vector from,vector to)
 
         if(pathlib_foundgoal)
         {
-            LOG_TRACE("Target found. Rebuilding and filtering path...\n");
+            LOG_TRACE("Target found. Rebuilding and filtering path...");
             ftime = gettime(GETTIME_REALTIME);
             ptime = ftime - ptime;
 
@@ -551,26 +538,26 @@ entity pathlib_astar(entity this, vector from,vector to)
 #if DEBUGPATHING
             pathlib_showpath2(start);
 
-            LOG_TRACE("Time used -      pathfinding: ", ftos(ptime),"\n");
-            LOG_TRACE("Time used - rebuild & filter: ", ftos(ftime),"\n");
-            LOG_TRACE("Time used -          cleanup: ", ftos(ctime),"\n");
-            LOG_TRACE("Time used -            total: ", ftos(ptime + ftime + ctime),"\n");
-            LOG_TRACE("Time used -         # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_frametime)),"\n\n");
-            LOG_TRACE("Nodes -         created: ", ftos(pathlib_made_cnt),"\n");
-            LOG_TRACE("Nodes -            open: ", ftos(pathlib_open_cnt),"\n");
-            LOG_TRACE("Nodes -          merged: ", ftos(pathlib_merge_cnt),"\n");
-            LOG_TRACE("Nodes -          closed: ", ftos(pathlib_closed_cnt),"\n");
-            LOG_TRACE("Nodes -        searched: ", ftos(pathlib_searched_cnt),"\n");
-            LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n");
-            LOG_TRACE("Nodes bestcash -   hits: ", ftos(pathlib_bestcash_hits),"\n");
-            LOG_TRACE("Nodes bestcash -   save: ", ftos(pathlib_bestcash_saved),"\n");
-            LOG_TRACE("AStar done.\n");
+            LOG_TRACE("Time used -      pathfinding: ", ftos(ptime));
+            LOG_TRACE("Time used - rebuild & filter: ", ftos(ftime));
+            LOG_TRACE("Time used -          cleanup: ", ftos(ctime));
+            LOG_TRACE("Time used -            total: ", ftos(ptime + ftime + ctime));
+            LOG_TRACE("Time used -         # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_frametime)));
+            LOG_TRACE("Nodes -         created: ", ftos(pathlib_made_cnt));
+            LOG_TRACE("Nodes -            open: ", ftos(pathlib_open_cnt));
+            LOG_TRACE("Nodes -          merged: ", ftos(pathlib_merge_cnt));
+            LOG_TRACE("Nodes -          closed: ", ftos(pathlib_closed_cnt));
+            LOG_TRACE("Nodes -        searched: ", ftos(pathlib_searched_cnt));
+            LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_searched));
+            LOG_TRACE("Nodes bestcash -   hits: ", ftos(pathlib_bestcash_hits));
+            LOG_TRACE("Nodes bestcash -   save: ", ftos(pathlib_bestcash_saved));
+            LOG_TRACE("AStar done.");
 #endif
             return start;
         }
     }
 
-    LOG_TRACE("A* Faild to find a path! Try a smaller gridsize.\n");
+    LOG_TRACE("A* Faild to find a path! Try a smaller gridsize.");
 
     pathlib_cleanup();
 
index 3059021d1238536b9d6b86b627bfbbd623bd6e5e..4a3bfe35c5bee6ba18262a33eff34e3f3a95bc11 100644 (file)
@@ -77,7 +77,7 @@ void a_think(entity this)
 {
     te_lightning1(this,this.origin, this.pos1);
     if(this.cnt < time)
-        remove(this);
+        delete(this);
     else
         this.nextthink = time + 0.2;
 }
@@ -87,7 +87,7 @@ vector pathlib_walknode(entity this, vector start, vector end, float doedge)
     vector direction,point,last_point,s,e;
     float steps, distance, i;
 
-    LOG_TRACE("Walking node from ", vtos(start), " to ", vtos(end), "\n");
+    LOG_TRACE("Walking node from ", vtos(start), " to ", vtos(end));
 
     pathlib_movenode_goodnode = 0;
 
@@ -109,7 +109,7 @@ vector pathlib_walknode(entity this, vector start, vector end, float doedge)
         //start - movenode_maxdrop
         a.cnt = time + 10;
 
-        LOG_TRACE("I cant walk on air!\n");
+        LOG_TRACE("I cant walk on air!");
         return trace_endpos;
     }
 
index 35dcce5b25030cad21e169218900308c5c1f6f55..906ebc7a54b1134d4291648da47e58c878b7831c 100644 (file)
@@ -1,5 +1,5 @@
 #include "path_waypoint.qh"
-#include "../bot/waypoints.qh"
+#include "../bot/api.qh"
 
 #include "pathlib.qh"
 #include "main.qh"
@@ -114,20 +114,16 @@ float pathlib_wpp_expand(entity wp)
 
 entity pathlib_wpp_bestopen()
 {
-    entity n, best;
-
     if(best_open_node)
         return best_open_node;
 
-    n = findchainentity(pathlib_list, openlist);
-    best = n;
-    while(n)
-    {
-        if(n.pathlib_node_f < best.pathlib_node_f)
-            best = n;
+    entity best = NULL;
 
-        n = n.chain;
-    }
+    FOREACH_ENTITY_ENT(pathlib_list, openlist,
+    {
+        if(!best || it.pathlib_node_f < best.pathlib_node_f)
+            best = it;
+    });
 
     return best;
 
@@ -135,7 +131,6 @@ entity pathlib_wpp_bestopen()
 
 entity pathlib_waypointpath(entity wp_from, entity wp_to, float callback)
 {
-    entity n;
     float ptime;
 
     ptime                                      = gettime(GETTIME_REALTIME);
@@ -164,32 +159,30 @@ entity pathlib_waypointpath(entity wp_from, entity wp_to, float callback)
     pathlib_searched_cnt     = 0;
     pathlib_foundgoal      = false;
 
-    LOG_TRACE("pathlib_waypointpath init\n");
+    LOG_TRACE("pathlib_waypointpath init");
 
     // Initialize waypoint grid
-    // FIXME! presisted chain for better preformance
-    for(n = findchain(classname, "waypoint"); n; n = n.chain)
+    IL_EACH(g_waypoints, true,
     {
-        n.pathlib_list = NULL;
-        n.pathlib_node_g = 0;
-        n.pathlib_node_f = 0;
-        n.pathlib_node_h = 0;
-
-        //setmodel(n, "models/runematch/rune.mdl");
-        //n.effects = EF_LOWPRECISION;
-        //n.colormod = '0 0 0';
-        //n.scale = 1;
+        it.pathlib_list = NULL;
+        it.pathlib_node_g = 0;
+        it.pathlib_node_f = 0;
+        it.pathlib_node_h = 0;
 
-    }
+        //setmodel(it, "models/runematch/rune.mdl");
+        //it.effects = EF_LOWPRECISION;
+        //it.colormod = '0 0 0';
+        //it.scale = 1;
+    });
 
     goal_node  = wp_to;
     start_node = wp_from;
 
     start_node.pathlib_list = closedlist;
-    LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(start_node))," links\n");
+    LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(start_node))," links");
     if(pathlib_open_cnt <= 0)
     {
-        LOG_TRACE("pathlib_waypointpath: Start waypoint not linked! aborting.\n");
+        LOG_TRACE("pathlib_waypointpath: Start waypoint not linked! aborting.");
         return NULL;
     }
 
@@ -203,17 +196,17 @@ entity pathlib_waypointpath_step()
     n = pathlib_wpp_bestopen();
     if(!n)
     {
-        LOG_TRACE("Cannot find best open node, abort.\n");
+        LOG_TRACE("Cannot find best open node, abort.");
         return NULL;
     }
     pathlib_wpp_close(n);
-       LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(n))," links\n");
+       LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(n))," links");
 
     if(pathlib_foundgoal)
     {
         entity start, end, open, ln;
 
-        LOG_TRACE("Target found. Rebuilding and filtering path...\n");
+        LOG_TRACE("Target found. Rebuilding and filtering path...");
 
                buildpath_nodefilter = buildpath_nodefilter_none;
                start = path_build(NULL, start_node.origin, NULL, NULL);
index a5f36da4a349d0059bfdc7846e98409847951b52..028a2c7e6e5b26eb9b01650327ab8b153bc5617e 100644 (file)
@@ -1,11 +1,14 @@
 #pragma once
 
+#ifndef DEBUGPATHING
+       #define DEBUGPATHING 0
+#endif
+
 .entity pathlib_list;
 .entity path_next;
 .entity path_prev;
 
 #define inwater(point) (pointcontents(point) == CONTENT_WATER)
-.int medium;
 
 const vector PLIB_FORWARD = '0 1 0';
 //#define PLIB_BACK    '0 -1 0'
@@ -46,7 +49,7 @@ float pathlib_closed_cnt;
 float pathlib_made_cnt;
 float pathlib_merge_cnt;
 float pathlib_searched_cnt;
-float pathlib_bestopen_seached;
+float pathlib_bestopen_searched;
 float pathlib_bestcash_hits;
 float pathlib_bestcash_saved;
 float pathlib_gridsize;
diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc
new file mode 100644 (file)
index 0000000..9cccbe8
--- /dev/null
@@ -0,0 +1,970 @@
+#include "player.qh"
+
+#include "bot/api.qh"
+#include "cheats.qh"
+#include "g_damage.qh"
+#include "g_subs.qh"
+#include "miscfunctions.qh"
+#include "portals.qh"
+#include "teamplay.qh"
+#include "weapons/throwing.qh"
+#include "command/common.qh"
+#include "../common/state.qh"
+#include "../common/anim.qh"
+#include "../common/animdecide.qh"
+#include "../common/csqcmodel_settings.qh"
+#include "../common/deathtypes/all.qh"
+#include "../common/triggers/subs.qh"
+#include "../common/playerstats.qh"
+#include "../lib/csqcmodel/sv_model.qh"
+
+#include "../common/minigames/sv_minigames.qh"
+
+#include "../common/physics/player.qh"
+#include "../common/effects/qc/all.qh"
+#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
+#include "../common/triggers/include.qh"
+
+#include "weapons/weaponstats.qh"
+
+#include "../common/animdecide.qh"
+
+void Drop_Special_Items(entity player)
+{
+       // called when the player has become stuck or frozen
+       // so objective items aren't stuck with the player
+
+       MUTATOR_CALLHOOK(DropSpecialItems, player);
+}
+
+void CopyBody_Think(entity this)
+{
+       if(this.CopyBody_nextthink && time > this.CopyBody_nextthink)
+       {
+               this.CopyBody_think(this);
+               if(wasfreed(this))
+                       return;
+               this.CopyBody_nextthink = this.nextthink;
+               this.CopyBody_think = getthink(this);
+               setthink(this, CopyBody_Think);
+       }
+       CSQCMODEL_AUTOUPDATE(this);
+       this.nextthink = time;
+}
+void CopyBody(entity this, float keepvelocity)
+{
+       if (this.effects & EF_NODRAW)
+               return;
+       entity clone = new(body);
+       clone.enemy = this;
+       clone.lip = this.lip;
+       clone.colormap = this.colormap;
+       clone.iscreature = this.iscreature;
+       clone.teleportable = this.teleportable;
+       clone.damagedbycontents = this.damagedbycontents;
+       if(clone.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, clone);
+       clone.angles = this.angles;
+       clone.v_angle = this.v_angle;
+       clone.avelocity = this.avelocity;
+       clone.damageforcescale = this.damageforcescale;
+       clone.effects = this.effects;
+       clone.glowmod = this.glowmod;
+       clone.event_damage = this.event_damage;
+       clone.anim_state = this.anim_state;
+       clone.anim_time = this.anim_time;
+       clone.anim_lower_action = this.anim_lower_action;
+       clone.anim_lower_time = this.anim_lower_time;
+       clone.anim_upper_action = this.anim_upper_action;
+       clone.anim_upper_time = this.anim_upper_time;
+       clone.anim_implicit_state = this.anim_implicit_state;
+       clone.anim_implicit_time = this.anim_implicit_time;
+       clone.anim_lower_implicit_action = this.anim_lower_implicit_action;
+       clone.anim_lower_implicit_time = this.anim_lower_implicit_time;
+       clone.anim_upper_implicit_action = this.anim_upper_implicit_action;
+       clone.anim_upper_implicit_time = this.anim_upper_implicit_time;
+       clone.dphitcontentsmask = this.dphitcontentsmask;
+       clone.death_time = this.death_time;
+       clone.pain_finished = this.pain_finished;
+       clone.health = this.health;
+       clone.armorvalue = this.armorvalue;
+       clone.armortype = this.armortype;
+       clone.model = this.model;
+       clone.modelindex = this.modelindex;
+       clone.skin = this.skin;
+       clone.species = this.species;
+       clone.move_qcphysics = false; // don't run gamecode logic on clones, too many
+       set_movetype(clone, this.move_movetype);
+       clone.solid = this.solid;
+       clone.ballistics_density = this.ballistics_density;
+       clone.takedamage = this.takedamage;
+       setcefc(clone, getcefc(this));
+       clone.uncustomizeentityforclient = this.uncustomizeentityforclient;
+       clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set;
+       if (keepvelocity == 1)
+               clone.velocity = this.velocity;
+       clone.oldvelocity = clone.velocity;
+       clone.alpha = this.alpha;
+       clone.fade_time = this.fade_time;
+       clone.fade_rate = this.fade_rate;
+       //clone.weapon = this.weapon;
+       setorigin(clone, this.origin);
+       setsize(clone, this.mins, this.maxs);
+       clone.prevorigin = this.origin;
+       clone.reset = SUB_Remove;
+       clone._ps = this._ps;
+
+       Drag_MoveDrag(this, clone);
+
+       if(clone.colormap <= maxclients && clone.colormap > 0)
+               clone.colormap = 1024 + this.clientcolors;
+
+       CSQCMODEL_AUTOINIT(clone);
+       clone.CopyBody_nextthink = this.nextthink;
+       clone.CopyBody_think = getthink(this);
+       clone.nextthink = time;
+       setthink(clone, CopyBody_Think);
+       // "bake" the current animation frame for clones (they don't get clientside animation)
+       animdecide_load_if_needed(clone);
+       animdecide_setframes(clone, false, frame, frame1time, frame2, frame2time);
+
+       IL_PUSH(g_clones, clone);
+
+       MUTATOR_CALLHOOK(CopyBody, this, clone, keepvelocity);
+}
+
+void player_setupanimsformodel(entity this)
+{
+       // load animation info
+       animdecide_load_if_needed(this);
+       animdecide_setstate(this, 0, false);
+}
+
+void player_anim(entity this)
+{
+       int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
+       if(IS_DEAD(this)) {
+               if (!deadbits) {
+                       // Decide on which death animation to use.
+                       if(random() < 0.5)
+                               deadbits = ANIMSTATE_DEAD1;
+                       else
+                               deadbits = ANIMSTATE_DEAD2;
+               }
+       } else {
+               // Clear a previous death animation.
+               deadbits = 0;
+       }
+       int animbits = deadbits;
+       if(STAT(FROZEN, this))
+               animbits |= ANIMSTATE_FROZEN;
+       if(this.move_movetype == MOVETYPE_FOLLOW)
+               animbits |= ANIMSTATE_FOLLOW;
+       if(this.crouch)
+               animbits |= ANIMSTATE_DUCK;
+       animdecide_setstate(this, animbits, false);
+       animdecide_setimplicitstate(this, IS_ONGROUND(this));
+}
+
+void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       float take, save;
+       vector v;
+       Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
+
+       // damage resistance (ignore most of the damage from a bullet or similar)
+       damage = max(damage - 5, 1);
+
+       v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage);
+       take = v.x;
+       save = v.y;
+
+       if(sound_allowed(MSG_BROADCAST, attacker))
+       {
+               if (save > 10)
+                       sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
+               else if (take > 30)
+                       sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
+               else if (take > 10)
+                       sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM);
+       }
+
+       if (take > 50)
+               Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker);
+       if (take > 100)
+               Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
+
+       this.armorvalue = this.armorvalue - save;
+       this.health = this.health - take;
+       // pause regeneration for 5 seconds
+       this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
+
+       this.dmg_save = this.dmg_save + save;//max(save - 10, 0);
+       this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
+       this.dmg_inflictor = inflictor;
+
+       if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0)
+       {
+               // don't use any animations as a gib
+               this.frame = 0;
+               // view just above the floor
+               this.view_ofs = '0 0 4';
+
+               Violence_GibSplash(this, 1, 1, attacker);
+               this.alpha = -1;
+               this.solid = SOLID_NOT; // restore later
+               this.takedamage = DAMAGE_NO; // restore later
+               if(this.damagedbycontents)
+                       IL_REMOVE(g_damagedbycontents, this);
+               this.damagedbycontents = false;
+       }
+}
+
+void calculate_player_respawn_time(entity this)
+{
+       if(g_ca)
+               return;
+
+       float gametype_setting_tmp;
+       float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
+       float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
+       float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large);
+       float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count);
+       float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count);
+       float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves);
+
+       float pcount = 1;  // Include myself whether or not team is already set right and I'm a "player".
+       if (teamplay)
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA(
+                       if(it.team == this.team)
+                               ++pcount;
+               ));
+               if (sdelay_small_count == 0)
+                       sdelay_small_count = 1;
+               if (sdelay_large_count == 0)
+                       sdelay_large_count = 1;
+       }
+       else
+       {
+               FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA(
+                       ++pcount;
+               ));
+               if (sdelay_small_count == 0)
+               {
+                       if (g_cts)
+                       {
+                               // Players play independently. No point in requiring enemies.
+                               sdelay_small_count = 1;
+                       }
+                       else
+                       {
+                               // Players play AGAINST each other. Enemies required.
+                               sdelay_small_count = 2;
+                       }
+               }
+               if (sdelay_large_count == 0)
+               {
+                       if (g_cts)
+                       {
+                               // Players play independently. No point in requiring enemies.
+                               sdelay_large_count = 1;
+                       }
+                       else
+                       {
+                               // Players play AGAINST each other. Enemies required.
+                               sdelay_large_count = 2;
+                       }
+               }
+       }
+
+       float sdelay;
+
+       if (pcount <= sdelay_small_count)
+               sdelay = sdelay_small;
+       else if (pcount >= sdelay_large_count)
+               sdelay = sdelay_large;
+       else  // NOTE: this case implies sdelay_large_count > sdelay_small_count.
+               sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count);
+
+       if(waves)
+               this.respawn_time = ceil((time + sdelay) / waves) * waves;
+       else
+               this.respawn_time = time + sdelay;
+
+       if(sdelay < sdelay_max)
+               this.respawn_time_max = time + sdelay_max;
+       else
+               this.respawn_time_max = this.respawn_time;
+
+       if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75))
+               this.respawn_countdown = 10; // first number to count down from is 10
+       else
+               this.respawn_countdown = -1; // do not count down
+
+       if(autocvar_g_forced_respawn)
+               this.respawn_flags = this.respawn_flags | RESPAWN_FORCE;
+}
+
+void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       float take, save, dh, da;
+       vector v;
+       float valid_damage_for_weaponstats;
+       float excess;
+
+       dh = max(this.health, 0);
+       da = max(this.armorvalue, 0);
+
+       if(!DEATH_ISSPECIAL(deathtype))
+       {
+               damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0));
+               if(this != attacker)
+                       damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0));
+       }
+
+       if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
+               damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage);
+
+       if(DEATH_ISWEAPON(deathtype, WEP_TUBA))
+       {
+               // tuba causes blood to come out of the ears
+               vector ear1, ear2;
+               vector d;
+               float f;
+               ear1 = this.origin;
+               ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8
+               ear2 = ear1;
+               makevectors(this.angles);
+               ear1 += v_right * -10;
+               ear2 += v_right * +10;
+               d = inflictor.origin - this.origin;
+               if (d)
+                       f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right!
+               else
+                       f = 0;  // Assum ecenter.
+               force = v_right * vlen(force);
+               Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker);
+               Violence_GibSplash_At(ear2, force,      2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker);
+               if(f > 0)
+               {
+                       hitloc = ear1;
+                       force = force * -1;
+               }
+               else
+               {
+                       hitloc = ear2;
+                       // force is already good
+               }
+       }
+       else
+               Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
+
+       v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage);
+       take = v.x;
+       save = v.y;
+
+       if(attacker == this)
+       {
+               // don't reset pushltime for this damage as it may be an attempt to
+               // escape a lava pit or similar
+               //this.pushltime = 0;
+               this.istypefrag = 0;
+       }
+       else if(IS_PLAYER(attacker))
+       {
+               this.pusher = attacker;
+               this.pushltime = time + autocvar_g_maxpushtime;
+               this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this);
+       }
+       else if(time < this.pushltime)
+       {
+               attacker = this.pusher;
+               this.pushltime = max(this.pushltime, time + 0.6);
+       }
+       else
+       {
+               this.pushltime = 0;
+               this.istypefrag = 0;
+       }
+
+       MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage);
+       take = bound(0, M_ARGV(4, float), this.health);
+       save = bound(0, M_ARGV(5, float), this.armorvalue);
+       excess = max(0, damage - take - save);
+
+       if(sound_allowed(MSG_BROADCAST, attacker))
+       {
+               if (save > 10)
+                       sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
+               else if (take > 30)
+                       sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
+               else if (take > 10)
+                       sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them?
+       }
+
+       if (take > 50)
+               Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker);
+       if (take > 100)
+               Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
+
+       if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1)
+       {
+               if (!(this.flags & FL_GODMODE))
+               {
+                       this.armorvalue = this.armorvalue - save;
+                       this.health = this.health - take;
+                       // pause regeneration for 5 seconds
+                       if(take)
+                               this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
+
+                       if (time > this.pain_finished)          //Don't switch pain sequences like crazy
+                       {
+                               this.pain_finished = time + 0.5;        //Supajoe
+
+                               if(autocvar_sv_gentle < 1) {
+                                       if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
+                                       {
+                                               if (!this.animstate_override)
+                                               {
+                                                       if (random() > 0.5)
+                                                               animdecide_setaction(this, ANIMACTION_PAIN1, true);
+                                                       else
+                                                               animdecide_setaction(this, ANIMACTION_PAIN2, true);
+                                               }
+                                       }
+
+                                       if(sound_allowed(MSG_BROADCAST, attacker))
+                                       if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser
+                                       if(this.health > 1)
+                                       // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
+                                       {
+                                               if(deathtype == DEATH_FALL.m_id)
+                                                       PlayerSound(this, playersound_fall, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                                               else if(this.health > 75)
+                                                       PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                                               else if(this.health > 50)
+                                                       PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                                               else if(this.health > 25)
+                                                       PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                                               else
+                                                       PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                                       }
+                               }
+                       }
+
+                       // throw off bot aim temporarily
+                       float shake;
+                       if(IS_BOT_CLIENT(this) && this.health >= 1)
+                       {
+                               shake = damage * 5 / (bound(0,skill,100) + 1);
+                               this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake;
+                               this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake;
+                               this.v_angle_x = bound(-90, this.v_angle.x, 90);
+                       }
+               }
+               else
+                       this.max_armorvalue += (save + take);
+       }
+       this.dmg_save = this.dmg_save + save;//max(save - 10, 0);
+       this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
+       this.dmg_inflictor = inflictor;
+
+       if (this != attacker) {
+               float realdmg = damage - excess;
+               if (IS_PLAYER(attacker)) {
+                       PlayerScore_Add(attacker, SP_DMG, realdmg);
+               }
+               if (IS_PLAYER(this)) {
+                       PlayerScore_Add(this, SP_DMGTAKEN, realdmg);
+               }
+       }
+
+       bool abot = (IS_BOT_CLIENT(attacker));
+       bool vbot = (IS_BOT_CLIENT(this));
+
+       valid_damage_for_weaponstats = 0;
+       Weapon awep = WEP_Null;
+
+       if(vbot || IS_REAL_CLIENT(this))
+       if(abot || IS_REAL_CLIENT(attacker))
+       if(attacker && this != attacker)
+       if(DIFF_TEAM(this, attacker))
+       {
+               if(DEATH_ISSPECIAL(deathtype))
+                       awep = PS(attacker).m_weapon;
+               else
+                       awep = DEATH_WEAPONOF(deathtype);
+               valid_damage_for_weaponstats = 1;
+       }
+
+       dh = dh - max(this.health, 0);
+       da = da - max(this.armorvalue, 0);
+       if(valid_damage_for_weaponstats)
+       {
+               WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da);
+       }
+       if (damage)
+       {
+               MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage);
+       }
+
+       if (this.health < 1)
+       {
+               float defer_ClientKill_Now_TeamChange;
+               defer_ClientKill_Now_TeamChange = false;
+
+               if(this.alivetime)
+               {
+                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+                       this.alivetime = 0;
+               }
+
+               if(valid_damage_for_weaponstats)
+                       WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot);
+
+               if(autocvar_sv_gentle < 1)
+               if(sound_allowed(MSG_BROADCAST, attacker))
+               {
+                       if(deathtype == DEATH_DROWN.m_id)
+                               PlayerSound(this, playersound_drown, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+                       else
+                               PlayerSound(this, playersound_death, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
+               }
+
+               // get rid of kill indicator
+               if(this.killindicator)
+               {
+                       delete(this.killindicator);
+                       this.killindicator = NULL;
+                       if(this.killindicator_teamchange)
+                               defer_ClientKill_Now_TeamChange = true;
+
+                       if(this.classname == "body")
+                       if(deathtype == DEATH_KILL.m_id)
+                       {
+                               // for the lemmings fans, a small harmless explosion
+                               Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
+                       }
+               }
+
+               // print an obituary message
+               if(this.classname != "body")
+                       Obituary (attacker, inflictor, this, deathtype);
+
+        // increment frag counter for used weapon type
+        Weapon w = DEATH_WEAPONOF(deathtype);
+               if(w != WEP_Null && accuracy_isgooddamage(attacker, this))
+                       attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1;
+
+               MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
+               excess = M_ARGV(4, float);
+
+               Weapon wep = PS(this).m_weapon;
+               /*for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       wep.wr_playerdeath(wep, this, weaponentity);
+               }*/
+               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+               wep.wr_playerdeath(wep, this, weaponentity);
+
+               RemoveGrapplingHook(this);
+
+               Portal_ClearAllLater(this);
+
+               this.fixangle = true;
+
+               if(defer_ClientKill_Now_TeamChange)
+                       ClientKill_Now_TeamChange(this); // can turn player into spectator
+
+               // player could have been miraculously resuscitated ;)
+               // e.g. players in freezetag get frozen, they don't really die
+               if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body"))
+                       return;
+
+               // when we get here, player actually dies
+
+               Unfreeze(this); // remove any icy remains
+               this.health = 0; // Unfreeze resets health, so we need to set it back
+
+               // clear waypoints
+               WaypointSprite_PlayerDead(this);
+               // throw a weapon
+               SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id);
+
+               // become fully visible
+               this.alpha = default_player_alpha;
+               // make the corpse upright (not tilted)
+               this.angles_x = 0;
+               this.angles_z = 0;
+               // don't spin
+               this.avelocity = '0 0 0';
+               // view from the floor
+               this.view_ofs = '0 0 -8';
+               // toss the corpse
+               set_movetype(this, MOVETYPE_TOSS);
+               // shootable corpse
+               this.solid = SOLID_CORPSE;
+               this.ballistics_density = autocvar_g_ballistics_density_corpse;
+               // don't stick to the floor
+               UNSET_ONGROUND(this);
+               // dying animation
+               this.deadflag = DEAD_DYING;
+
+               // when to allow respawn
+               calculate_player_respawn_time(this);
+
+               this.death_time = time;
+               if (random() < 0.5)
+                       animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true);
+               else
+                       animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true);
+               if (this.maxs.z > 5)
+               {
+                       this.maxs_z = 5;
+                       setsize(this, this.mins, this.maxs);
+               }
+               // set damage function to corpse damage
+               this.event_damage = PlayerCorpseDamage;
+               // call the corpse damage function just in case it wants to gib
+               this.event_damage(this, inflictor, attacker, excess, deathtype, hitloc, force);
+
+               // set up to fade out later
+               SUB_SetFade (this, time + 6 + random (), 1);
+               // reset body think wrapper broken by SUB_SetFade
+               if(this.classname == "body" && getthink(this) != CopyBody_Think) {
+                       this.CopyBody_think = getthink(this);
+                       this.CopyBody_nextthink = this.nextthink;
+                       setthink(this, CopyBody_Think);
+                       this.nextthink = time;
+               }
+
+               if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") {
+                       // remove corpse
+                       // clones don't run any animation code any more, so we must gib them when they die :(
+                       PlayerCorpseDamage(this, inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force);
+               }
+
+               // reset fields the weapons may use just in case
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       it.wr_resetplayer(it, this);
+                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                       {
+                               ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0;
+                       }
+               ));
+       }
+}
+
+void MoveToTeam(entity client, int team_colour, int type)
+{
+       int lockteams_backup = lockteams;  // backup any team lock
+       lockteams = 0;  // disable locked teams
+       TeamchangeFrags(client);  // move the players frags
+       SetPlayerColors(client, team_colour - 1);  // set the players colour
+       Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0');  // kill the player
+       lockteams = lockteams_backup;  // restore the team lock
+       LogTeamchange(client.playerid, client.team, type);
+}
+
+/** print(), but only print if the server is not local */
+void dedicated_print(string input)
+{
+       if (server_is_dedicated) print(input);
+}
+
+/**
+ * message "": do not say, just test flood control
+ * return value:
+ *   1 = accept
+ *   0 = reject
+ *  -1 = fake accept
+ */
+int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol)
+{
+       if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ")
+        msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!)
+
+       msgin = formatmessage(source, msgin);
+
+    string colorstr;
+       if (!IS_PLAYER(source))
+               colorstr = "^0"; // black for spectators
+       else if(teamplay)
+               colorstr = Team_ColorCode(source.team);
+       else
+       {
+               colorstr = "";
+               teamsay = false;
+       }
+
+       if(gameover)
+               teamsay = false;
+
+    if (!source) {
+               colorstr = "";
+               teamsay = false;
+    }
+
+       if(msgin != "")
+               msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin);
+
+       /*
+        * using bprint solves this... me stupid
+       // how can we prevent the message from appearing in a listen server?
+       // for now, just give "say" back and only handle say_team
+       if(!teamsay)
+       {
+               clientcommand(source, strcat("say ", msgin));
+               return;
+       }
+       */
+
+    string namestr = "";
+    if (source)
+        namestr = autocvar_g_chat_teamcolors ? playername(source) : source.netname;
+
+    string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7";
+
+    string msgstr, cmsgstr;
+    string privatemsgprefix = string_null;
+    int privatemsgprefixlen = 0;
+       if (msgin == "") {
+        msgstr = cmsgstr = "";
+       } else {
+               if(privatesay)
+               {
+                       msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7");
+                       privatemsgprefixlen = strlen(msgstr);
+                       msgstr = strcat(msgstr, msgin);
+                       cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin);
+                       if(autocvar_g_chat_teamcolors)
+                               privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7");
+                       else
+                               privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^7");
+               }
+               else if(teamsay)
+               {
+                       if(strstrofs(msgin, "/me", 0) >= 0)
+                       {
+                               //msgin = strreplace("/me", "", msgin);
+                               //msgin = substring(msgin, 3, strlen(msgin));
+                               msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin);
+                               msgstr = strcat("\{1}\{13}^4* ", "^7", msgin);
+                       }
+                       else
+                               msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin);
+                       cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin);
+               }
+               else
+               {
+                       if(strstrofs(msgin, "/me", 0) >= 0)
+                       {
+                               //msgin = strreplace("/me", "", msgin);
+                               //msgin = substring(msgin, 3, strlen(msgin));
+                               msgin = strreplace("/me", strcat(colorprefix, namestr), msgin);
+                               msgstr = strcat("\{1}^4* ", "^7", msgin);
+                       }
+                       else {
+                msgstr = "\{1}";
+                msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7");
+                msgstr = strcat(msgstr, msgin);
+            }
+                       cmsgstr = "";
+               }
+               msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
+       }
+
+       string fullmsgstr = msgstr;
+       string fullcmsgstr = cmsgstr;
+
+       // FLOOD CONTROL
+       int flood = 0;
+       var .float flood_field = floodcontrol_chat;
+       if(floodcontrol)
+       {
+               float flood_spl;
+               float flood_burst;
+               float flood_lmax;
+               float lines;
+               if(privatesay)
+               {
+                       flood_spl = autocvar_g_chat_flood_spl_tell;
+                       flood_burst = autocvar_g_chat_flood_burst_tell;
+                       flood_lmax = autocvar_g_chat_flood_lmax_tell;
+                       flood_field = floodcontrol_chattell;
+               }
+               else if(teamsay)
+               {
+                       flood_spl = autocvar_g_chat_flood_spl_team;
+                       flood_burst = autocvar_g_chat_flood_burst_team;
+                       flood_lmax = autocvar_g_chat_flood_lmax_team;
+                       flood_field = floodcontrol_chatteam;
+               }
+               else
+               {
+                       flood_spl = autocvar_g_chat_flood_spl;
+                       flood_burst = autocvar_g_chat_flood_burst;
+                       flood_lmax = autocvar_g_chat_flood_lmax;
+                       flood_field = floodcontrol_chat;
+               }
+               flood_burst = max(0, flood_burst - 1);
+               // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four!
+
+               // do flood control for the default line size
+               if(msgstr != "")
+               {
+                       getWrappedLine_remaining = msgstr;
+                       msgstr = "";
+                       lines = 0;
+                       while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax))
+                       {
+                               msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width
+                               ++lines;
+                       }
+                       msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
+
+                       if(getWrappedLine_remaining != "")
+                       {
+                               msgstr = strcat(msgstr, "\n");
+                               flood = 2;
+                       }
+
+                       if (time >= source.(flood_field))
+                       {
+                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl;
+                       }
+                       else
+                       {
+                               flood = 1;
+                               msgstr = fullmsgstr;
+                       }
+               }
+               else
+               {
+                       if (time >= source.(flood_field))
+                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl;
+                       else
+                               flood = 1;
+               }
+
+               if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection
+                       source.(flood_field) = flood = 0;
+       }
+
+    string sourcemsgstr, sourcecmsgstr;
+       if(flood == 2) // cannot happen for empty msgstr
+       {
+               if(autocvar_g_chat_flood_notify_flooder)
+               {
+                       sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n");
+                       sourcecmsgstr = "";
+               }
+               else
+               {
+                       sourcemsgstr = fullmsgstr;
+                       sourcecmsgstr = fullcmsgstr;
+               }
+               cmsgstr = "";
+       }
+       else
+       {
+               sourcemsgstr = msgstr;
+               sourcecmsgstr = cmsgstr;
+       }
+
+       if (!privatesay && source && !IS_PLAYER(source))
+       {
+               if (!gameover)
+               if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))
+                       teamsay = -1; // spectators
+       }
+
+       if(flood)
+               LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n");
+
+       // build sourcemsgstr by cutting off a prefix and replacing it by the other one
+       if(privatesay)
+               sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1));
+
+    int ret;
+       if(source.muted)
+       {
+               // always fake the message
+               ret = -1;
+       }
+       else if(flood == 1)
+       {
+               if (autocvar_g_chat_flood_notify_flooder)
+               {
+                       sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n"));
+                       ret = 0;
+               }
+               else
+                       ret = -1;
+       }
+       else
+       {
+               ret = 1;
+       }
+
+       MUTATOR_CALLHOOK(ChatMessage, source, ret);
+       ret = M_ARGV(1, int);
+
+       if(sourcemsgstr != "" && ret != 0)
+       {
+               if(ret < 0) // faked message, because the player is muted
+               {
+                       sprint(source, sourcemsgstr);
+                       if(sourcecmsgstr != "" && !privatesay)
+                               centerprint(source, sourcecmsgstr);
+               }
+               else if(privatesay) // private message, between 2 people only
+               {
+                       sprint(source, sourcemsgstr);
+                       if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
+                       if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source))
+                       {
+                               sprint(privatesay, msgstr);
+                               if(cmsgstr != "")
+                                       centerprint(privatesay, cmsgstr);
+                       }
+               }
+               else if ( teamsay && source.active_minigame )
+               {
+                       sprint(source, sourcemsgstr);
+                       dedicated_print(msgstr); // send to server console too
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
+               }
+               else if(teamsay > 0) // team message, only sent to team mates
+               {
+                       sprint(source, sourcemsgstr);
+                       dedicated_print(msgstr); // send to server console too
+                       if(sourcecmsgstr != "")
+                               centerprint(source, sourcecmsgstr);
+                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               sprint(it, msgstr);
+                               if(cmsgstr != "")
+                                       centerprint(it, cmsgstr);
+                       });
+               }
+               else if(teamsay < 0) // spectator message, only sent to spectators
+               {
+                       sprint(source, sourcemsgstr);
+                       dedicated_print(msgstr); // send to server console too
+                       FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
+               }
+               else
+               {
+            if (source) {
+                sprint(source, sourcemsgstr);
+                dedicated_print(msgstr); // send to server console too
+                MX_Say(strcat(playername(source), "^7: ", msgin));
+            }
+            FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
+        }
+       }
+
+       return ret;
+}
diff --git a/qcsrc/server/player.qh b/qcsrc/server/player.qh
new file mode 100644 (file)
index 0000000..3e2b860
--- /dev/null
@@ -0,0 +1,41 @@
+#pragma once
+
+.entity pusher;
+.float pushltime;
+.float istypefrag;
+
+.float CopyBody_nextthink;
+.void(entity this) CopyBody_think;
+void CopyBody_Think(entity this);
+void CopyBody(entity this, float keepvelocity);
+
+void dedicated_print(string input);
+
+void player_setupanimsformodel(entity this);
+
+void player_anim(entity this);
+
+void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
+
+// g_<gametype>_str:
+// If 0, default is used.
+// If <0, 0 is used.
+// Otherwise, g_str (default value) is used.
+// For consistency, negative values there are mapped to zero too.
+#define GAMETYPE_DEFAULTED_SETTING(str) \
+       ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \
+       (gametype_setting_tmp < 0) ? 0 \
+       : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \
+       : gametype_setting_tmp)
+
+void calculate_player_respawn_time(entity this);
+
+void ClientKill_Now_TeamChange(entity this);
+
+void MoveToTeam(entity client, float team_colour, float type);
+
+void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
+
+/** to be used by `prvm_edictset server playernumber muted 1` */
+.float muted;
+int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol);
index 61104ebe2fa1a1420d01a20f035ce4ef3fa2b891..34c886fa10ebc67029ab433831117ccecb1e2fcb 100644 (file)
@@ -34,7 +34,7 @@ void playerdemo_open_read(entity this, string f)
        this.playerdemo_starttime = time - 1;
        this.playerdemo_time = stof(fgets(this.playerdemo_fh));
        this.playerdemo_time += this.playerdemo_starttime;
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        LOG_INFO("playerdemo: ", this.netname, " reading from ", f, "\n");
 }
 void playerdemo_open_write(entity this, string f)
@@ -147,9 +147,9 @@ float playerdemo_read(entity this)
                PLAYERDEMO_FIELDS(this, playerdemo_read_)
                {
                        time = this.playerdemo_time;
-                       WITHSELF(this, PlayerPreThink());
+                       PlayerPreThink(this);
                        // not running physics though... this is just so we can run weapon stuff
-                       WITHSELF(this, PlayerPostThink());
+                       PlayerPostThink(this);
                }
                this.playerdemo_time = stof(fgets(this.playerdemo_fh));
                if(this.playerdemo_time == 0)
index f6502dfd812335d794b6a43f79322a03f68a6402..1656b1400e171b5ed696ed726f8353787d5adaf8 100644 (file)
@@ -1,14 +1,14 @@
 #include "portals.qh"
 
 #include "g_hook.qh"
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 #include "../common/constants.qh"
 #include "../common/deathtypes/all.qh"
 #include "../common/notifications/all.qh"
 #include "../common/triggers/teleporters.qh"
 #include "../common/triggers/subs.qh"
 #include "../common/util.qh"
-#include "../common/weapons/all.qh"
+#include <common/weapons/_all.qh>
 #include "../lib/csqcmodel/sv_model.qh"
 #include "../lib/warpzone/anglestransform.qh"
 #include "../lib/warpzone/util_server.qh"
@@ -208,8 +208,8 @@ float Portal_FindSafeOrigin(entity portal)
 {
        vector o;
        o = portal.origin;
-       portal.mins = STAT(PL_MIN, NULL) - SAFERNUDGE;
-       portal.maxs = STAT(PL_MAX, NULL) + SAFERNUDGE;
+       portal.mins = PL_MIN_CONST - SAFERNUDGE;
+       portal.maxs = PL_MAX_CONST + SAFERNUDGE;
        fixedmakevectors(portal.mangle);
        portal.origin += 16 * v_forward;
        if(!move_out_of_solid(portal))
@@ -249,7 +249,7 @@ float Portal_WillHitPlane(vector eorg, vector emins, vector emaxs, vector evel,
 #endif
 }
 
-void Portal_Touch(entity this)
+void Portal_Touch(entity this, entity toucher)
 {
        vector g;
 
@@ -258,18 +258,18 @@ void Portal_Touch(entity this)
        if(this.solid != SOLID_TRIGGER)
                return; // possibly engine bug
 
-       if(IS_PLAYER(other))
+       if(IS_PLAYER(toucher))
                return; // handled by think
 #endif
 
-       if(other.classname == "item_flag_team")
+       if(toucher.classname == "item_flag_team")
                return; // never portal these
 
-       if(other.classname == "grapplinghook")
+       if(toucher.classname == "grapplinghook")
                return; // handled by think
 
        if(!autocvar_g_vehicles_teleportable)
-       if(other.vehicle_flags & VHF_ISVEHICLE)
+       if(IS_VEHICLE(toucher))
                return; // no teleporting vehicles?
 
        if(!this.enemy)
@@ -283,43 +283,43 @@ void Portal_Touch(entity this)
                return; // only handle impacts
 #endif
 
-       if(other.classname == "porto")
+       if(toucher.classname == "porto")
        {
-               if(other.portal_id == this.portal_id)
+               if(toucher.portal_id == this.portal_id)
                        return;
        }
        if(time < this.portal_activatetime)
-               if(other == this.aiment)
+               if(toucher == this.aiment)
                {
                        this.portal_activatetime = time + 0.1;
                        return;
                }
-       if(other != this.aiment)
-               if(IS_PLAYER(other))
-                       if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(this.aiment))
+       if(toucher != this.aiment)
+               if(IS_PLAYER(toucher))
+                       if(IS_INDEPENDENT_PLAYER(toucher) || IS_INDEPENDENT_PLAYER(this.aiment))
                                return; // cannot go through someone else's portal
-       if(other.aiment != this.aiment)
-               if(IS_PLAYER(other.aiment))
-                       if(IS_INDEPENDENT_PLAYER(other.aiment) || IS_INDEPENDENT_PLAYER(this.aiment))
+       if(toucher.aiment != this.aiment)
+               if(IS_PLAYER(toucher.aiment))
+                       if(IS_INDEPENDENT_PLAYER(toucher.aiment) || IS_INDEPENDENT_PLAYER(this.aiment))
                                return; // cannot go through someone else's portal
        fixedmakevectors(this.mangle);
        g = frametime * '0 0 -1' * autocvar_sv_gravity;
-       if(!Portal_WillHitPlane(other.origin, other.mins, other.maxs, other.velocity + g, this.origin, v_forward, this.maxs.x))
+       if(!Portal_WillHitPlane(toucher.origin, toucher.mins, toucher.maxs, toucher.velocity + g, this.origin, v_forward, this.maxs.x))
                return;
 
        /*
-       if(other.mins_x < PL_MIN.x || other.mins_y < PL_MIN.y || other.mins_z < PL_MIN.z
-       || other.maxs_x > PL_MAX.x || other.maxs_y > PL_MAX.y || other.maxs_z > PL_MAX.z)
+       if(toucher.mins_x < PL_MIN.x || toucher.mins_y < PL_MIN.y || toucher.mins_z < PL_MIN.z
+       || toucher.maxs_x > PL_MAX.x || toucher.maxs_y > PL_MAX.y || toucher.maxs_z > PL_MAX.z)
        {
                // can't teleport this
                return;
        }
        */
 
-       if(Portal_TeleportPlayer(this, other))
-               if(other.classname == "porto")
-                       if(other.effects & EF_RED)
-                               other.effects += EF_BLUE - EF_RED;
+       if(Portal_TeleportPlayer(this, toucher))
+               if(toucher.classname == "porto")
+                       if(toucher.effects & EF_RED)
+                               toucher.effects += EF_BLUE - EF_RED;
 }
 
 void Portal_Think(entity this);
@@ -418,7 +418,7 @@ void Portal_Remove(entity portal, float killed)
                fixedmakevectors(portal.mangle);
                sound(portal, CH_SHOTS, SND_PORTO_EXPLODE, VOL_BASE, ATTEN_NORM);
                Send_Effect(EFFECT_ROCKET_EXPLODE, portal.origin + v_forward * 16, v_forward * 1024, 4);
-               remove(portal);
+               delete(portal);
        }
        else
        {
@@ -494,15 +494,15 @@ void Portal_Think(entity this)
                Portal_Remove(this, 0);
 }
 
-float Portal_Customize(entity this)
+bool Portal_Customize(entity this, entity client)
 {
-       if(IS_SPEC(other))
-               other = other.enemy;
-       if(other == this.aiment)
+       if(IS_SPEC(client))
+               client = client.enemy;
+       if(client == this.aiment)
        {
                this.modelindex = this.savemodelindex;
        }
-       else if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(this.aiment))
+       else if(IS_INDEPENDENT_PLAYER(client) || IS_INDEPENDENT_PLAYER(this.aiment))
        {
                this.modelindex = 0;
        }
@@ -642,7 +642,7 @@ entity Portal_Spawn(entity own, vector org, vector ang)
 
        if(!Portal_FindSafeOrigin(portal))
        {
-               remove(portal);
+               delete(portal);
                return NULL;
        }
 
index 0396dee6cd249862d3bbfad5329cf7bff204280b..1a8ada45fe306f9156bf74e789acf51de830f1bf 100644 (file)
@@ -1,28 +1,11 @@
-#ifndef DEBUGPATHING
-       #define DEBUGPATHING 0
-#endif
-
 #include <lib/_all.inc>
-#include "_all.qh"
-
-#include "../server/_mod.inc"
-#include "bot/_mod.inc"
-#include "bot/havocbot/_mod.inc"
-#include "command/_mod.inc"
-#include "mutators/_mod.inc"
-#include "pathlib/_all.inc"
-#include "weapons/_mod.inc"
 
-#include <common/_all.inc>
-#include <common/effects/qc/all.qc>
+#if XONOTIC
+#include <server/_all.inc>
 
-#include <lib/csqcmodel/sv_model.qc>
-
-#include <lib/warpzone/anglestransform.qc>
-#include <lib/warpzone/common.qc>
-#include <lib/warpzone/server.qc>
-#include <lib/warpzone/util_server.qc>
+#include <ecs/_mod.inc>
+#endif
 
-#if BUILD_MOD
-#include "../../mod/server/progs.inc"
+#ifdef BUILD_MOD
+#include <mod/server/progs.inc>
 #endif
index c4e94cd13ddca230dd60a5bd766c522799c104d8..a3cbc685f2be9d3164ce3526d7932a9f962b7a54 100644 (file)
@@ -1,15 +1,15 @@
 #include "race.qh"
 
-#include "cl_client.qh"
+#include "client.qh"
 #include "portals.qh"
 #include "scores.qh"
 #include "spawnpoints.qh"
-#include "bot/waypoints.qh"
-#include "bot/navigation.qh"
+#include "bot/api.qh"
 #include "command/getreplies.qh"
 #include "../common/deathtypes/all.qh"
 #include "../common/notifications/all.qh"
 #include "../common/mapinfo.qh"
+#include <common/net_linked.qh>
 #include "../common/triggers/subs.qh"
 #include "../lib/warpzone/util_server.qh"
 #include "../lib/warpzone/common.qh"
@@ -698,10 +698,10 @@ void checkpoint_passed(entity this, entity player)
        }
 }
 
-void checkpoint_touch(entity this)
+void checkpoint_touch(entity this, entity toucher)
 {
-       EXACTTRIGGER_TOUCH;
-       checkpoint_passed(this, other);
+       EXACTTRIGGER_TOUCH(this, toucher);
+       checkpoint_passed(this, toucher);
 }
 
 void checkpoint_use(entity this, entity actor, entity trigger)
@@ -833,15 +833,14 @@ void trigger_race_checkpoint_verify(entity this)
                                race_timed_checkpoint = 255;
                        }
                } else {
-                       for (entity cp = NULL; (cp = find(cp, classname, "trigger_race_checkpoint")); ) {
-                               if (cp.sprite) {
-                                       if (cp.race_checkpoint == 0) {
-                                               WaypointSprite_UpdateSprites(cp.sprite, WP_RaceStart, WP_Null, WP_Null);
-                    } else if (cp.race_checkpoint == race_timed_checkpoint) {
-                                               WaypointSprite_UpdateSprites(cp.sprite, WP_RaceFinish, WP_Null, WP_Null);
-                    }
+                       IL_EACH(g_racecheckpoints, it.sprite,
+                       {
+                               if (it.race_checkpoint == 0) {
+                                       WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
+                } else if (it.race_checkpoint == race_timed_checkpoint) {
+                                       WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
                                }
-            }
+            });
                }
        }
 
@@ -905,7 +904,7 @@ vector trigger_race_checkpoint_spawn_evalfunc(entity this, entity player, entity
 spawnfunc(trigger_race_checkpoint)
 {
        vector o;
-       if(!g_race && !g_cts) { remove(this); return; }
+       if(!g_race && !g_cts) { delete(this); return; }
 
        EXACTTRIGGER_INIT;
 
@@ -914,7 +913,7 @@ spawnfunc(trigger_race_checkpoint)
                settouch(this, checkpoint_touch);
 
        o = (this.absmin + this.absmax) * 0.5;
-       tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
+       tracebox(o, PL_MIN_CONST, PL_MAX_CONST, o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
        waypoint_spawnforitem_force(this, trace_endpos);
        this.nearestwaypointtimeout = time + 1000000000;
 
@@ -947,25 +946,36 @@ spawnfunc(trigger_race_checkpoint)
        this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
        this.spawn_evalfunc = trigger_race_checkpoint_spawn_evalfunc;
 
+       IL_PUSH(g_racecheckpoints, this);
+
        InitializeEntity(this, trigger_race_checkpoint_verify, INITPRIO_FINDTARGET);
 }
 
 spawnfunc(target_checkpoint) // defrag entity
 {
-       vector o;
-       if(!g_race && !g_cts) { remove(this); return; }
+       if(!g_race && !g_cts) { delete(this); return; }
        defrag_ents = 1;
 
-       EXACTTRIGGER_INIT;
+       // if this is targeted, then it probably isn't a trigger
+       bool is_trigger = !boolean(!this.nottargeted && this.targetname != "");
+
+       if(is_trigger)
+               EXACTTRIGGER_INIT;
 
        this.use = checkpoint_use;
-       if (!(this.spawnflags & 1))
+       if (is_trigger && !(this.spawnflags & 1))
                settouch(this, checkpoint_touch);
 
-       o = (this.absmin + this.absmax) * 0.5;
-       tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this);
-       waypoint_spawnforitem_force(this, trace_endpos);
-       this.nearestwaypointtimeout = time + 1000000000;
+       vector org = this.origin;
+
+       // bots should only pathfind to this if it is a valid touchable trigger
+       if(is_trigger)
+       {
+               org = (this.absmin + this.absmax) * 0.5;
+               tracebox(org, PL_MIN_CONST, PL_MAX_CONST, org - '0 0 1' * (org.z - this.absmin.z), MOVE_NORMAL, this);
+               waypoint_spawnforitem_force(this, trace_endpos);
+               this.nearestwaypointtimeout = time + 1000000000;
+       }
 
        if(this.message == "")
                this.message = "went backwards";
@@ -982,9 +992,9 @@ spawnfunc(target_checkpoint) // defrag entity
        race_timed_checkpoint = 1;
 
        if(this.race_checkpoint == 0)
-               WaypointSprite_SpawnFixed(WP_RaceStart, o, this, sprite, RADARICON_NONE);
+               WaypointSprite_SpawnFixed(WP_RaceStart, org, this, sprite, RADARICON_NONE);
        else
-               WaypointSprite_SpawnFixed(WP_RaceCheckpoint, o, this, sprite, RADARICON_NONE);
+               WaypointSprite_SpawnFixed(WP_RaceCheckpoint, org, this, sprite, RADARICON_NONE);
 
        this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
 
@@ -1031,7 +1041,7 @@ void race_RetractPlayer(entity this)
 
 spawnfunc(info_player_race)
 {
-       if(!g_race && !g_cts) { remove(this); return; }
+       if(!g_race && !g_cts) { delete(this); return; }
        ++race_spawns;
        spawnfunc_info_player_deathmatch(this);
 
@@ -1092,13 +1102,13 @@ void race_ImposePenaltyTime(entity pl, float penalty, string reason)
        }
 }
 
-void penalty_touch(entity this)
+void penalty_touch(entity this, entity toucher)
 {
-       EXACTTRIGGER_TOUCH;
-       if(other.race_lastpenalty != this)
+       EXACTTRIGGER_TOUCH(this, toucher);
+       if(toucher.race_lastpenalty != this)
        {
-               other.race_lastpenalty = this;
-               race_ImposePenaltyTime(other, this.race_penalty, this.race_penalty_reason);
+               toucher.race_lastpenalty = this;
+               race_ImposePenaltyTime(toucher, this.race_penalty, this.race_penalty_reason);
        }
 }
 
@@ -1143,7 +1153,7 @@ float race_GetFractionalLapCount(entity e)
 
        vector o0, o1;
        float bestfraction, fraction;
-       entity lastcp, cp0, cp1;
+       entity lastcp;
        float nextcpindex, lastcpindex;
 
        nextcpindex = max(e.race_checkpoint, 0);
@@ -1154,26 +1164,26 @@ float race_GetFractionalLapCount(entity e)
                return l; // finish
 
        bestfraction = 1;
-       for(cp0 = NULL; (cp0 = find(cp0, classname, "trigger_race_checkpoint")); )
+       IL_EACH(g_racecheckpoints, true,
        {
-               if(cp0.race_checkpoint != lastcpindex)
+               if(it.race_checkpoint != lastcpindex)
                        continue;
                if(lastcp)
-                       if(cp0 != lastcp)
+                       if(it != lastcp)
                                continue;
-               o0 = (cp0.absmin + cp0.absmax) * 0.5;
-               for(cp1 = NULL; (cp1 = find(cp1, classname, "trigger_race_checkpoint")); )
+               o0 = (it.absmin + it.absmax) * 0.5;
+               IL_EACH(g_racecheckpoints, true,
                {
-                       if(cp1.race_checkpoint != nextcpindex)
+                       if(it.race_checkpoint != nextcpindex)
                                continue;
-                       o1 = (cp1.absmin + cp1.absmax) * 0.5;
+                       o1 = (it.absmin + it.absmax) * 0.5;
                        if(o0 == o1)
                                continue;
                        fraction = bound(0.0001, vlen(e.origin - o1) / vlen(o0 - o1), 1);
                        if(fraction < bestfraction)
                                bestfraction = fraction;
-               }
-       }
+               });
+       });
 
        // we are at CP "nextcpindex - bestfraction"
        // race_timed_checkpoint == 4: then nextcp==4 means 0.9999x, nextcp==0 means 0.0000x
index b69b0006a96d1a8a1b381fd4a4aaaf785657fe8a..6419afb737690604dace62c681cabe9da377f4e7 100644 (file)
@@ -4,9 +4,6 @@ float race_teams;
 
 // scores
 const float ST_RACE_LAPS = 1;
-const float SP_RACE_LAPS = 4;
-const float SP_RACE_TIME = 5;
-const float SP_RACE_FASTEST = 6;
 
 bool g_race_qualifying;
 
@@ -32,6 +29,9 @@ float race_completing;
 .float race_respawn_checkpoint;
 .entity race_respawn_spotref; // try THIS spawn in case you respawn
 
+IntrusiveList g_racecheckpoints;
+STATIC_INIT(g_racecheckpoints) { g_racecheckpoints = IL_NEW(); }
+
 // definitions for functions used outside race.qc
 float race_PreviousCheckpoint(float f);
 float race_NextCheckpoint(float f);
index d1302a84d7d0212ed1d1440d7bf5f9105e8fee53..063c12abb0aa8f67dd25c600ab70512251194589 100644 (file)
@@ -1,5 +1,6 @@
 #include "round_handler.qh"
 
+#include "campaign.qh"
 #include "command/vote.qh"
 #include "../common/util.qh"
 
@@ -12,6 +13,9 @@ void round_handler_Think(entity this)
        }
 
        if (gameover)
+               gameover = false;
+
+       if (intermission_running)
        {
                round_handler_Reset(0);
                round_handler_Remove();
@@ -28,7 +32,7 @@ void round_handler_Think(entity this)
 
        if (this.cnt > 0)  // countdown running
        {
-               if (this.canRoundStart())
+               if (this.canRoundStart() && !(autocvar_g_campaign && !campaign_bots_may_start))
                {
                        if (this.cnt == this.count + 1) round_starttime = time + this.count;
                        int f = this.cnt - 1;
@@ -55,6 +59,7 @@ void round_handler_Think(entity this)
                        // schedule a new round
                        this.wait = true;
                        this.nextthink = time + this.delay;
+                       gameover = true;
                }
                else
                {
@@ -111,6 +116,6 @@ void round_handler_Reset(float next_think)
 
 void round_handler_Remove()
 {
-       remove(round_handler);
+       delete(round_handler);
        round_handler = NULL;
 }
index aa108dd985d9622e167a3db2649d08fec0dfc0d4..e6264b8693b4b360b7e8ad7c4a3b8f6c61a67e6c 100644 (file)
@@ -1,16 +1,13 @@
 #include "scores.qh"
 
 #include "command/common.qh"
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
+#include <common/net_linked.qh>
 #include "../common/playerstats.qh"
 #include "../common/teams.qh"
 
 .entity scorekeeper;
 entity teamscorekeepers[16];
-string scores_label[MAX_SCORE];
-float scores_flags[MAX_SCORE];
-string teamscores_label[MAX_TEAMSCORE];
-float teamscores_flags[MAX_TEAMSCORE];
 float teamscores_entities_count;
 var .float scores_primary;
 var .float teamscores_primary;
@@ -65,7 +62,7 @@ bool TeamScore_SendEntity(entity this, entity to, float sendflags)
 
        longflags = 0;
        for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)
-               if(this.(teamscores[i]) > 127 || this.(teamscores[i]) <= -128)
+               if(this.(teamscores(i)) > 127 || this.(teamscores(i)) <= -128)
                        longflags |= p;
 
 #if MAX_TEAMSCORE <= 8
@@ -79,9 +76,9 @@ bool TeamScore_SendEntity(entity this, entity to, float sendflags)
                if(sendflags & p)
                {
                        if(longflags & p)
-                               WriteInt24_t(MSG_ENTITY, this.(teamscores[i]));
+                               WriteInt24_t(MSG_ENTITY, this.(teamscores(i)));
                        else
-                               WriteChar(MSG_ENTITY, this.(teamscores[i]));
+                               WriteChar(MSG_ENTITY, this.(teamscores(i)));
                }
 
        return true;
@@ -120,9 +117,9 @@ float TeamScore_AddToTeam(float t, float scorefield, float score)
                error("Adding score to unknown team!");
        }
        if(score)
-               if(teamscores_label[scorefield] != "")
+               if(teamscores_label(scorefield) != "")
                        s.SendFlags |= pow(2, scorefield);
-       return (s.(teamscores[scorefield]) += score);
+       return (s.(teamscores(scorefield)) += score);
 }
 
 float TeamScore_Add(entity player, float scorefield, float score)
@@ -139,8 +136,8 @@ float TeamScore_Compare(entity t1, entity t2, float strict)
        for(i = 0; i < MAX_TEAMSCORE; ++i)
        {
                var .float f;
-               f = teamscores[i];
-               result = ScoreField_Compare(t1, t2, f, teamscores_flags[i], result, strict);
+               f = teamscores(i);
+               result = ScoreField_Compare(t1, t2, f, teamscores_flags(i), result, strict);
        }
 
        if (result.x == 0 && strict)
@@ -153,13 +150,13 @@ float TeamScore_Compare(entity t1, entity t2, float strict)
  * the scoreinfo entity
  */
 
-void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags)
+void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scoreflags)
 {
-       scores_label[i] = label;
-       scores_flags[i] = scoreflags;
+       scores_label(i) = label;
+       scores_flags(i) = scoreflags;
        if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
        {
-               scores_primary = scores[i];
+               scores_primary = scores(i);
                scores_flags_primary = scoreflags;
        }
        if(label != "")
@@ -171,11 +168,11 @@ void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags)
 
 void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags)
 {
-       teamscores_label[i] = label;
-       teamscores_flags[i] = scoreflags;
+       teamscores_label(i) = label;
+       teamscores_flags(i) = scoreflags;
        if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
        {
-               teamscores_primary = teamscores[i];
+               teamscores_primary = teamscores(i);
                teamscores_flags_primary = scoreflags;
        }
        if(label != "")
@@ -189,21 +186,20 @@ bool ScoreInfo_SendEntity(entity this, entity to, int sf)
 {
        float i;
        WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
-       WriteInt24_t(MSG_ENTITY, MapInfo_LoadedGametype);
-       for(i = 0; i < MAX_SCORE; ++i)
-       {
-               WriteString(MSG_ENTITY, scores_label[i]);
-               WriteByte(MSG_ENTITY, scores_flags[i]);
-       }
+       WriteRegistered(Gametypes, MSG_ENTITY, MapInfo_LoadedGametype);
+       FOREACH(Scores, true, {
+               WriteString(MSG_ENTITY, scores_label(it));
+               WriteByte(MSG_ENTITY, scores_flags(it));
+       });
        for(i = 0; i < MAX_TEAMSCORE; ++i)
        {
-               WriteString(MSG_ENTITY, teamscores_label[i]);
-               WriteByte(MSG_ENTITY, teamscores_flags[i]);
+               WriteString(MSG_ENTITY, teamscores_label(i));
+               WriteByte(MSG_ENTITY, teamscores_flags(i));
        }
        return true;
 }
 
-void ScoreInfo_Init(float teams)
+void ScoreInfo_Init(int teams)
 {
        if(scores_initialized)
        {
@@ -214,13 +210,13 @@ void ScoreInfo_Init(float teams)
                scores_initialized = new_pure(ent_client_scoreinfo);
                Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity);
        }
-       if(teams >= 1)
+       if(teams & BIT(0))
                TeamScore_Spawn(NUM_TEAM_1, "Red");
-       if(teams >= 2)
+       if(teams & BIT(1))
                TeamScore_Spawn(NUM_TEAM_2, "Blue");
-       if(teams >= 3)
+       if(teams & BIT(2))
                TeamScore_Spawn(NUM_TEAM_3, "Yellow");
-       if(teams >= 4)
+       if(teams & BIT(3))
                TeamScore_Spawn(NUM_TEAM_4, "Pink");
 }
 
@@ -230,31 +226,28 @@ void ScoreInfo_Init(float teams)
 
 bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
 {
-       float i, p, longflags;
-
        WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES);
        WriteByte(MSG_ENTITY, etof(this.owner));
 
-       longflags = 0;
-       for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)
-               if(this.(scores[i]) > 127 || this.(scores[i]) <= -128)
+       int longflags = 0;
+       FOREACH(Scores, true, {
+           int p = 1 << (i % 16);
+               if (this.(scores(it)) > 127 || this.(scores(it)) <= -128)
                        longflags |= p;
+    });
 
-#if MAX_SCORE <= 8
-       WriteByte(MSG_ENTITY, sendflags);
-       WriteByte(MSG_ENTITY, longflags);
-#else
        WriteShort(MSG_ENTITY, sendflags);
        WriteShort(MSG_ENTITY, longflags);
-#endif
-       for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)
-               if(sendflags & p)
+       FOREACH(Scores, true, {
+           int p = 1 << (i % 16);
+               if (sendflags & p)
                {
                        if(longflags & p)
-                               WriteInt24_t(MSG_ENTITY, this.(scores[i]));
+                               WriteInt24_t(MSG_ENTITY, this.(scores(it)));
                        else
-                               WriteChar(MSG_ENTITY, this.(scores[i]));
+                               WriteChar(MSG_ENTITY, this.(scores(it)));
                }
+    });
 
        return true;
 }
@@ -262,7 +255,6 @@ bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
 float PlayerScore_Clear(entity player)
 {
        entity sk;
-       float i;
 
        if(teamscores_entities_count)
                return 0;
@@ -270,13 +262,12 @@ float PlayerScore_Clear(entity player)
        if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0;
 
        sk = player.scorekeeper;
-       for(i = 0; i < MAX_SCORE; ++i)
-       {
-               if(sk.(scores[i]) != 0)
-                       if(scores_label[i] != "")
-                               sk.SendFlags |= pow(2, i);
-               sk.(scores[i]) = 0;
-       }
+       FOREACH(Scores, true, {
+               if(sk.(scores(it)) != 0)
+                       if(scores_label(it) != "")
+                               sk.SendFlags |= pow(2, i % 16);
+               sk.(scores(it)) = 0;
+       });
 
        return 1;
 }
@@ -285,18 +276,15 @@ void Score_ClearAll()
 {
        entity sk;
        float t;
-       FOREACH_CLIENTSLOT(true,
-       {
+       FOREACH_CLIENTSLOT(true, {
                sk = it.scorekeeper;
-               if(!sk)
-                       continue;
-               for(int j = 0; j < MAX_SCORE; ++j)
-               {
-                       if(sk.(scores[j]) != 0)
-                               if(scores_label[j] != "")
-                                       sk.SendFlags |= pow(2, j);
-                       sk.(scores[j]) = 0;
-               }
+               if (!sk) continue;
+               FOREACH(Scores, true, {
+                       if(sk.(scores(it)) != 0)
+                               if(scores_label(it) != "")
+                                       sk.SendFlags |= pow(2, i % 16);
+                       sk.(scores(it)) = 0;
+               });
        });
        for(t = 0; t < 16; ++t)
        {
@@ -305,10 +293,10 @@ void Score_ClearAll()
                        continue;
                for(int j = 0; j < MAX_TEAMSCORE; ++j)
                {
-                       if(sk.(teamscores[j]) != 0)
-                               if(teamscores_label[j] != "")
+                       if(sk.(teamscores(j)) != 0)
+                               if(teamscores_label(j) != "")
                                        sk.SendFlags |= pow(2, j);
-                       sk.(teamscores[j]) = 0;
+                       sk.(teamscores(j)) = 0;
                }
        }
 }
@@ -327,11 +315,11 @@ void PlayerScore_Detach(entity player)
 {
        if(!player.scorekeeper)
                error("player has no scorekeeper");
-       remove(player.scorekeeper);
+       delete(player.scorekeeper);
        player.scorekeeper = NULL;
 }
 
-float PlayerScore_Add(entity player, float scorefield, float score)
+float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
 {
        bool mutator_returnvalue = MUTATOR_CALLHOOK(AddPlayerScore, scorefield, score, player);
        score = M_ARGV(1, float);
@@ -346,18 +334,18 @@ float PlayerScore_Add(entity player, float scorefield, float score)
        {
                if(gameover)
                        return 0;
-               LOG_WARNING("Adding score to unknown player!");
+               LOG_WARN("Adding score to unknown player!");
                return 0;
        }
        if(score)
-               if(scores_label[scorefield] != "")
-                       s.SendFlags |= pow(2, scorefield);
+               if(scores_label(scorefield) != "")
+                       s.SendFlags |= pow(2, scorefield.m_id % 16);
        if(!warmup_stage)
-               PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label[scorefield]), score);
-       return (s.(scores[scorefield]) += score);
+               PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
+       return (s.(scores(scorefield)) += score);
 }
 
-float PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score)
+float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
 {
        float r;
        r = PlayerScore_Add(player, pscorefield, score);
@@ -371,13 +359,10 @@ float PlayerScore_Compare(entity t1, entity t2, float strict)
        if(!t1 || !t2) return (!t2) - !t1;
 
        vector result = '0 0 0';
-       float i;
-       for(i = 0; i < MAX_SCORE; ++i)
-       {
-               var .float f;
-               f = scores[i];
-               result = ScoreField_Compare(t1, t2, f, scores_flags[i], result, strict);
-       }
+       FOREACH(Scores, true, {
+               var .float f = scores(it);
+               result = ScoreField_Compare(t1, t2, f, scores_flags(it), result, strict);
+       });
 
        if (result.x == 0 && strict)
                result.x = etof(t1.owner) - etof(t2.owner);
@@ -562,53 +547,59 @@ string GetPlayerScoreString(entity pl, float shortString)
 {
        string out;
        entity sk;
-       float i, f;
+       float f;
        string l;
 
        out = "";
        if(!pl)
        {
                // label
-               for(i = 0; i < MAX_SCORE; ++i)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
+               FOREACH(Scores, true, {
+                       if ((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
                        {
-                               f = scores_flags[i];
-                               l = scores_label[i];
+                               f = scores_flags(it);
+                               l = scores_label(it);
                                out = strcat(out, GetScoreLogLabel(l, f), ",");
                        }
+        });
                if(shortString < 2)
-               for(i = 0; i < MAX_SCORE; ++i)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
+               FOREACH(Scores, true, {
+                       if((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
                        {
-                               f = scores_flags[i];
-                               l = scores_label[i];
+                               f = scores_flags(it);
+                               l = scores_label(it);
                                out = strcat(out, GetScoreLogLabel(l, f), ",");
                        }
+        });
                if(shortString < 1)
-               for(i = 0; i < MAX_SCORE; ++i)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
+               FOREACH(Scores, true, {
+                       if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
+                       if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
                        {
-                               f = scores_flags[i];
-                               l = scores_label[i];
+                               f = scores_flags(it);
+                               l = scores_label(it);
                                out = strcat(out, GetScoreLogLabel(l, f), ",");
                        }
+        });
                out = substring(out, 0, strlen(out) - 1);
        }
        else if((sk = pl.scorekeeper))
        {
-               for(i = 0; i < MAX_SCORE; ++i)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
-                               out = strcat(out, ftos(sk.(scores[i])), ",");
+               FOREACH(Scores, true, {
+                       if ((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
+                               out = strcat(out, ftos(sk.(scores(it))), ",");
+        });
                if(shortString < 2)
-               for(i = 0; i < MAX_SCORE; ++i)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
-                               out = strcat(out, ftos(sk.(scores[i])), ",");
+               FOREACH(Scores, true, {
+                       if ((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
+                               out = strcat(out, ftos(sk.(scores(it))), ",");
+        });
                if(shortString < 1)
-               for(i = 0; i < MAX_SCORE; ++i)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
-                       if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
-                               out = strcat(out, ftos(sk.(scores[i])), ",");
+               FOREACH(Scores, true, {
+                       if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
+                       if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
+                               out = strcat(out, ftos(sk.(scores(it))), ",");
+        });
                out = substring(out, 0, strlen(out) - 1);
        }
        return out;
@@ -626,27 +617,27 @@ string GetTeamScoreString(float tm, float shortString)
        {
                // label
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
                        {
-                               f = teamscores_flags[i];
-                               l = teamscores_label[i];
+                               f = teamscores_flags(i);
+                               l = teamscores_label(i);
                                out = strcat(out, GetScoreLogLabel(l, f), ",");
                        }
                if(shortString < 2)
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
                        {
-                               f = teamscores_flags[i];
-                               l = teamscores_label[i];
+                               f = teamscores_flags(i);
+                               l = teamscores_label(i);
                                out = strcat(out, GetScoreLogLabel(l, f), ",");
                        }
                if(shortString < 1)
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
                        {
-                               f = teamscores_flags[i];
-                               l = teamscores_label[i];
+                               f = teamscores_flags(i);
+                               l = teamscores_label(i);
                                out = strcat(out, GetScoreLogLabel(l, f), ",");
                        }
                out = substring(out, 0, strlen(out) - 1);
@@ -654,17 +645,17 @@ string GetTeamScoreString(float tm, float shortString)
        else if((sk = teamscorekeepers[tm - 1]))
        {
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
-                               out = strcat(out, ftos(sk.(teamscores[i])), ",");
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
+                               out = strcat(out, ftos(sk.(teamscores(i))), ",");
                if(shortString < 2)
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
-                               out = strcat(out, ftos(sk.(teamscores[i])), ",");
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
+                               out = strcat(out, ftos(sk.(teamscores(i))), ",");
                if(shortString < 1)
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
-                       if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
-                               out = strcat(out, ftos(sk.(teamscores[i])), ",");
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
+                       if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
+                               out = strcat(out, ftos(sk.(teamscores(i))), ",");
                out = substring(out, 0, strlen(out) - 1);
        }
        return out;
@@ -799,10 +790,10 @@ void Score_NicePrint_Team(entity to, float t, float w)
        {
                s = strcat(s, Team_ColoredFullName(t));
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if(teamscores_label[i] != "")
+                       if(teamscores_label(i) != "")
                        {
-                               fl = teamscores_flags[i];
-                               sc = sk.(teamscores[i]);
+                               fl = teamscores_flags(i);
+                               sc = sk.(teamscores(i));
                                s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc));
                        }
        }
@@ -811,13 +802,14 @@ void Score_NicePrint_Team(entity to, float t, float w)
 
        s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), ""));
 
-       for(i = 0; i < MAX_SCORE; ++i)
-               if(scores_label[i] != "")
+       FOREACH(Scores, true, {
+               if(scores_label(it) != "")
                {
-                       fl = scores_flags[i];
-                       s2 = scores_label[i];
+                       fl = scores_flags(it);
+                       s2 = scores_label(it);
                        s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w)));
                }
+    });
 
        print_to(to, s);
 }
@@ -845,13 +837,14 @@ void Score_NicePrint_Player(entity to, entity p, float w)
                }
        }
 
-       for(i = 0; i < MAX_SCORE; ++i)
-               if(scores_label[i] != "")
+       FOREACH(Scores, true, {
+               if(scores_label(it) != "")
                {
-                       fl = scores_flags[i];
-                       sc = sk.(scores[i]);
+                       fl = scores_flags(it);
+                       sc = sk.(scores(it));
                        s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc)));
                }
+    });
 
        print_to(to, s);
 }
@@ -870,13 +863,13 @@ void Score_NicePrint_Spectator(entity to, entity p)
 void Score_NicePrint(entity to)
 {
        entity p;
-       float i;
        float w;
 
        int t = 0;
-       for(i = 0; i < MAX_SCORE; ++i)
-               if(scores_label[i] != "")
+       FOREACH(Scores, true, {
+               if(scores_label(it) != "")
                        ++t;
+    });
        w = bound(6, floor(SCORESWIDTH / t - 1), 9);
 
        p = PlayerScore_Sort(score_dummyfield, 1, 1, 0);
@@ -905,14 +898,12 @@ void Score_NicePrint(entity to)
 
 void PlayerScore_PlayerStats(entity p)
 {
-       entity s;
-       float i;
-       s = p.scorekeeper;
-
-       for(i = 0; i < MAX_SCORE; ++i)
-               if(s.(scores[i]) != 0)
-                       if(scores_label[i] != "")
-                               PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label[i]), s.(scores[i]));
+       entity s = p.scorekeeper;
+       FOREACH(Scores, true, {
+               if(s.(scores(it)) != 0)
+                       if(scores_label(it) != "")
+                               PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
+    });
 }
 
 void PlayerScore_TeamStats()
@@ -925,9 +916,9 @@ void PlayerScore_TeamStats()
                if(!sk)
                        continue;
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if(sk.(teamscores[i]) != 0)
-                               if(teamscores_label[i] != "")
+                       if(sk.(teamscores(i)) != 0)
+                               if(teamscores_label(i) != "")
                                        // the +1 is important here!
-                                       PS_GR_T_ADDVAL(t+1, strcat(PLAYERSTATS_SCOREBOARD, teamscores_label[i]), sk.(teamscores[i]));
+                                       PS_GR_T_ADDVAL(t+1, strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
        }
 }
index a462bde92c4bed31f706262a2314b43a5c3ff8f0..cf767659056d5f9cef2a39ae063d18a5923c8c5e 100644 (file)
@@ -3,8 +3,6 @@
 #include <common/constants.qh>
 
 entity scores_initialized; // non-NULL when scores labels/rules have been set
-.float scores[MAX_SCORE];
-.float teamscores[MAX_TEAMSCORE];
 .float scoreboard_pos;
 
 /**
@@ -24,7 +22,7 @@ void PlayerScore_Detach(entity player);
  * Means: FIXME make players unable to join the game when not called ClientConnect yet.
  * Returns the new score.
  */
-float PlayerScore_Add(entity player, float scorefield, float score);
+float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score);
 
 /**
  * Initialize the score of this player if needed.
@@ -57,12 +55,12 @@ float TeamScore_GetCompareValue(float t);
  * Adds a score to both the player and the team. Returns the team score if
  * possible, otherwise the player score.
  */
-float PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score);
+float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score);
 
 /**
  * Adds to the generic score fields for both the player and the team.
  */
-#define PlayerTeamScore_AddScore(p,s) PlayerTeamScore_Add(p, SP_SCORE, ST_SCORE, s)
+#define PlayerTeamScore_AddScore(p, s) PlayerTeamScore_Add(p, SP_SCORE, ST_SCORE, s)
 
 /**
  * Set the label of a team score item, as well as the scoring flags.
@@ -72,7 +70,7 @@ void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags);
 /**
  * Set the label of a player score item, as well as the scoring flags.
  */
-void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags);
+void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scoreflags);
 
 /**
  * Initialize the scores info for the given number of teams.
index 72eeb9a1cccaf10d4ce3142a28a69cee0139ca53..9c416472f40a207b0870c9bc5be4fc416502d48b 100644 (file)
@@ -1,19 +1,32 @@
 #include "scores_rules.qh"
 
-#include "cl_client.qh"
+#include "client.qh"
 #include "scores.qh"
 
+int ScoreRules_teams;
+
 void CheckAllowedTeams (entity for_whom);
 
-// NOTE: SP_ constants may not be >= MAX_SCORE; ST_constants may not be >= MAX_TEAMSCORE
+int NumTeams(int teams)
+{
+       return boolean(teams & BIT(0)) + boolean(teams & BIT(1)) + boolean(teams & BIT(2)) + boolean(teams & BIT(3));
+}
+
+int AvailableTeams()
+{
+       return NumTeams(ScoreRules_teams);
+       // NOTE: this method is unreliable, as forced teams set the c* globals to weird values
+       //return boolean(c1 >= 0) + boolean(c2 >= 0) + boolean(c3 >= 0) + boolean(c4 >= 0);
+}
+
+// NOTE: ST_constants may not be >= MAX_TEAMSCORE
 // scores that should be in all modes:
-float ScoreRules_teams;
-void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabled)
+void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled)
 {
-       float i;
-       for(i = 0; i < MAX_SCORE; ++i)
-               ScoreInfo_SetLabel_PlayerScore(i, "", 0);
-       for(i = 0; i < MAX_TEAMSCORE; ++i)
+       FOREACH(Scores, true, {
+               ScoreInfo_SetLabel_PlayerScore(it, "", 0);
+    });
+       for(int i = 0; i < MAX_TEAMSCORE; ++i)
                ScoreInfo_SetLabel_TeamScore(i, "", 0);
 
        ScoreRules_teams = teams;
@@ -32,8 +45,9 @@ void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabl
        if(score_enabled)
                ScoreInfo_SetLabel_PlayerScore(SP_SCORE,        "score",     sprio);
 
-       ScoreInfo_SetLabel_PlayerScore(SP_DMG, "damage", 0);
-       ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "damagetaken", SFL_LOWER_IS_BETTER);
+       ScoreInfo_SetLabel_PlayerScore(SP_DMG, "dmg", 0);
+       ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "dmgtaken", SFL_LOWER_IS_BETTER);
+       ScoreInfo_SetLabel_PlayerScore(SP_ELO, "elo", 0);
 }
 void ScoreRules_basics_end()
 {
@@ -44,7 +58,12 @@ void ScoreRules_generic()
        if(teamplay)
        {
                CheckAllowedTeams(NULL);
-               ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
+               int teams = 0;
+               if(c1 >= 0) teams |= BIT(0);
+               if(c2 >= 0) teams |= BIT(1);
+               if(c3 >= 0) teams |= BIT(2);
+               if(c4 >= 0) teams |= BIT(3);
+               ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
        }
        else
                ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true);
index 1d2646bb83e6d544d6986f3e60d2d8e119d7a9ee..9d1caf5395bff7453a6e558266bc4f1cd0ad3c92 100644 (file)
@@ -1,5 +1,7 @@
 #pragma once
 
-void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabled);
+int NumTeams(int teams);
+int AvailableTeams();
+void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled);
 void ScoreRules_basics_end();
 void ScoreRules_generic();
index f55fb1122aee7e5686b4ee83aa43324da388633c..517bc1892ca8541c77f8b35d4bb9c969c6d6cd7a 100644 (file)
@@ -1,9 +1,10 @@
 #include "spawnpoints.qh"
 
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 #include "g_world.qh"
 #include "race.qh"
 #include "../common/constants.qh"
+#include <common/net_linked.qh>
 #include "../common/teams.qh"
 #include "../common/triggers/subs.qh"
 #include "../common/util.qh"
@@ -166,6 +167,7 @@ spawnfunc(info_player_start)
 spawnfunc(info_player_deathmatch)
 {
        this.classname = "info_player_deathmatch";
+       IL_PUSH(g_spawnpoints, this);
        relocate_spawnpoint(this);
 }
 
@@ -249,17 +251,15 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck)
        vector spawn_score = prio * '1 0 0' + shortest * '0 1 0';
 
        // filter out spots for assault
-       if(spot.target != "") {
-               entity ent;
-               float found;
-
-               found = 0;
-               for(ent = NULL; (ent = find(ent, targetname, spot.target)); )
+       if(spot.target != "")
+       {
+               int found = 0;
+               for(entity targ = findchain(targetname, spot.target); targ; targ = targ.chain)
                {
                        ++found;
-                       if(ent.spawn_evalfunc)
+                       if(targ.spawn_evalfunc)
                        {
-                               spawn_score = ent.spawn_evalfunc(ent, this, spot, spawn_score);
+                               spawn_score = targ.spawn_evalfunc(targ, this, spot, spawn_score);
                                if(spawn_score.x < 0)
                                        return spawn_score;
                        }
@@ -267,7 +267,7 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck)
 
                if(!found)
                {
-                       LOG_TRACE("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n");
+                       LOG_TRACE("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target);
                        return '-1 0 0';
                }
        }
@@ -318,7 +318,7 @@ entity Spawn_WeightedPoint(entity firstspot, float lower, float upper, float exp
 
        RandomSelection_Init();
        for(spot = firstspot; spot; spot = spot.chain)
-               RandomSelection_Add(spot, 0, string_null, pow(bound(lower, spot.spawnpoint_score.y, upper), exponent) * spot.cnt, (spot.spawnpoint_score.y >= lower) * 0.5 + spot.spawnpoint_score.x);
+               RandomSelection_AddEnt(spot, pow(bound(lower, spot.spawnpoint_score.y, upper), exponent) * spot.cnt, (spot.spawnpoint_score.y >= lower) * 0.5 + spot.spawnpoint_score.x);
 
        return RandomSelection_chosen_ent;
 }
@@ -335,7 +335,7 @@ entity SelectSpawnPoint(entity this, bool anypoint)
        float teamcheck;
        entity spot, firstspot;
 
-       spot = find (NULL, classname, "testplayerstart");
+       spot = find(NULL, classname, "testplayerstart");
        if (spot)
                return spot;
 
index bf0a955515f1a4dfeb39fea923083571a8a8e0e3..1544fa384a5cbbf16eb4cebf3bf89be17faf1c1f 100644 (file)
@@ -7,10 +7,11 @@
 /**
     Uniform pull towards a point
 **/
-vector steerlib_pull(entity this, vector point)
+#define steerlib_pull(ent,point) normalize(point - (ent).origin)
+/*vector steerlib_pull(entity this, vector point)
 {
     return normalize(point - this.origin);
-}
+}*/
 
 /**
     Uniform push from a point
@@ -143,7 +144,7 @@ vector steerlib_wander(entity this, float range, float tresh, vector oldpoint)
     vector wander_point;
     wander_point = v_forward - oldpoint;
 
-    if (vlen(wander_point) > tresh)
+    if (vdist(wander_point, >, tresh))
         return oldpoint;
 
     range = bound(0,range,1);
@@ -339,24 +340,30 @@ vector steerlib_traceavoid_flat(entity this, float pitch, float length, vector v
     return normalize(leftwish + rightwish + frontwish);
 }
 
-float beamsweep_badpoint(vector point,float waterok)
+bool beamsweep_badpoint(vector point, bool waterok)
 {
-    float pc,pc2;
-
     if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
-        return 1;
-
-    pc  = pointcontents(point);
-    pc2 = pointcontents(point - '0 0 1');
-
-    switch(pc)
+        return true;
+
+    int pc = pointcontents(point);
+    int pc2 = pointcontents(point - '0 0 1');
+    
+    if(pc == CONTENT_EMPTY && pc2 == CONTENT_SOLID)
+        return false;
+    if(pc == CONTENT_EMPTY && pc2 == CONTENT_WATER && waterok)
+        return false;
+    if(pc == CONTENT_WATER && waterok)
+        return false;
+    return true;
+
+    /*switch(pc)
     {
         case CONTENT_SOLID: break;
         case CONTENT_SLIME: break;
         case CONTENT_LAVA:  break;
 
         case CONTENT_SKY:
-            return 1;
+            return true;
 
         case CONTENT_EMPTY:
             if (pc2 == CONTENT_SOLID)
@@ -375,14 +382,14 @@ float beamsweep_badpoint(vector point,float waterok)
             break;
     }
 
-    return 1;
+    return true;*/
 }
 
 //#define BEAMSTEER_VISUAL
 float beamsweep(entity this, vector from, vector dir,float length, float step,float step_up, float step_down)
 {
     float i;
-    vector a,b,u,d;
+    vector a, b, u, d;
 
     u = '0 0 1' * step_up;
     d = '0 0 1' * step_down;
@@ -464,8 +471,8 @@ vector steerlib_beamsteer(entity this, vector dir, float length, float step, flo
 
     if(bm_left + bm_right < 0.15)
     {
-        vr = normalize((v_forward*-1) + v_right * 0.75);
-        vl = normalize((v_forward*-1) - v_right * 0.75);
+        vr = normalize((v_forward*-1) + v_right * 0.90);
+        vl = normalize((v_forward*-1) - v_right * 0.90);
 
         bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
         bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
@@ -556,7 +563,7 @@ void spawn_flocker(entity this)
     setthink(flocker, flocker_think);
     flocker.nextthink  = time + random() * 5;
     PROJECTILE_MAKETRIGGER(flocker);
-    flocker.movetype   = MOVETYPE_BOUNCEMISSILE;
+    set_movetype(flocker, MOVETYPE_BOUNCEMISSILE);
     flocker.effects    = EF_LOWPRECISION;
     flocker.velocity   = randomvec() * 300;
     flocker.angles     = vectoangles(flocker.velocity);
@@ -579,8 +586,7 @@ void flockerspawn_think(entity this)
 void flocker_hunter_think(entity this)
 {
     vector dodgemove,attractmove,newmove;
-    entity e,ee;
-    float d,bd;
+    entity ee;
 
     this.angles_x = this.angles.x * -1;
     makevectors(this.angles);
@@ -597,20 +603,14 @@ void flocker_hunter_think(entity this)
 
     if(!this.enemy)
     {
-        e = findchainfloat(flock_id,this.flock_id);
-        while(e)
+        FOREACH_ENTITY_FLOAT(flock_id, this.flock_id,
         {
-            d = vlen(this.origin - e.origin);
-
-            if(e != this.owner)
-            if(e != ee)
-            if(d > bd)
-            {
-                this.enemy = e;
-                bd = d;
-            }
-            e = e.chain;
-        }
+            if(it == this.owner || it == ee)
+                continue;
+
+            if(!this.enemy || vlen2(this.origin - it.origin) > vlen2(this.origin - this.enemy.origin))
+                this.enemy = it;
+        });
     }
 
     if(this.enemy)
@@ -624,7 +624,6 @@ void flocker_hunter_think(entity this)
     this.velocity = movelib_inertmove_byspeed(this, newmove,1250,0.3,0.7);
     this.velocity = movelib_dragvec(this, 0.01,0.5);
 
-
     this.angles = vectoangles(this.velocity);
     this.nextthink = time + 0.1;
 }
@@ -649,7 +648,7 @@ spawnfunc(flockerspawn)
 
     this.enemy.scale     = 3;
     this.enemy.effects   = EF_LOWPRECISION;
-    this.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
+    set_movetype(this.enemy, MOVETYPE_BOUNCEMISSILE);
     PROJECTILE_MAKETRIGGER(this.enemy);
     setthink(this.enemy, flocker_hunter_think);
     this.enemy.nextthink = time + 10;
index fa21610f042d29c794149cb2a28a0ec02a824569..4beb69f6328ba6c337d3b34e803104464e8a73d3 100644 (file)
@@ -4,4 +4,4 @@
 
 vector steerlib_arrive(entity this, vector point, float maximal_distance);
 vector steerlib_attract2(entity this, vector point, float min_influense, float max_distance, float max_influense);
-vector steerlib_pull(entity this, vector point);
+//vector steerlib_pull(entity this, vector point);
index aac9c497d1dabeaf7b901988a098a7128b14ed77..0c36a77c5b7689665d101e1d676efe67af567e21 100644 (file)
@@ -4,12 +4,11 @@
 #include "g_hook.qh"
 #include "g_world.qh"
 
-#include "bot/bot.qh"
-#include "bot/waypoints.qh"
+#include "bot/api.qh"
 
 #include "command/common.qh"
 
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 #include "weapons/csqcprojectile.qh"
 
 #include "../common/constants.qh"
@@ -19,7 +18,7 @@
 #include "../common/util.qh"
 
 #include "../common/vehicles/all.qh"
-#include "../common/weapons/all.qh"
+#include <common/weapons/_all.qh>
 
 #include "../lib/csqcmodel/sv_model.qh"
 
@@ -96,6 +95,7 @@ void CreatureFrame_Liquids(entity this)
 void CreatureFrame_FallDamage(entity this)
 {
        if(!IS_VEHICLE(this) && !(this.flags & FL_PROJECTILE)) // vehicles don't get falling damage
+       if(this.velocity || this.oldvelocity) // moving or has moved
        {
                // check for falling damage
                float velocity_len = vlen(this.velocity);
@@ -117,8 +117,9 @@ void CreatureFrame_FallDamage(entity this)
 
 void CreatureFrame_All()
 {
-       FOREACH_ENTITY_FLOAT(damagedbycontents, true, {
-               if (it.movetype == MOVETYPE_NOCLIP) continue;
+       IL_EACH(g_damagedbycontents, it.damagedbycontents,
+       {
+               if (it.move_movetype == MOVETYPE_NOCLIP) continue;
                CreatureFrame_Liquids(it);
                CreatureFrame_FallDamage(it);
                it.oldvelocity = it.velocity;
@@ -152,19 +153,20 @@ Called before each frame by the server
 float game_delay;
 float game_delay_last;
 
-bool autocvar_sv_autopause = true;
+bool autocvar_sv_autopause = false;
 float RedirectionThink();
-void PM_Main(Client this);
+void systems_update();
+void sys_phys_update(entity this, float dt);
 void StartFrame()
 {
     // TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
-    FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), PM_Main(it));
-    FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), WITHSELF(it, PlayerPreThink()));
+    IL_EACH(g_players, IS_FAKE_CLIENT(it), sys_phys_update(it, frametime));
+    IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPreThink(it));
 
        execute_next_frame();
        if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true);
 
-       remove = remove_unsafely; // not during spawning!
+       delete_fn = remove_unsafely; // not during spawning!
        serverprevtime = servertime;
        servertime = time;
        serverframetime = frametime;
@@ -190,7 +192,7 @@ void StartFrame()
        }
 #endif
 
-       FOREACH_ENTITY_FLOAT(csqcprojectile_clientanimate, true, CSQCProjectile_Check(it));
+       IL_EACH(g_projectiles, it.csqcprojectile_clientanimate, CSQCProjectile_Check(it));
 
        if (RedirectionThink()) return;
 
@@ -228,7 +230,7 @@ void StartFrame()
        MUTATOR_CALLHOOK(SV_StartFrame);
 
     FOREACH_CLIENT(true, GlobalStats_update(it));
-    FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), WITHSELF(it, PlayerPostThink()));
+    IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPostThink(it));
 }
 
 .vector originjitter;
@@ -237,15 +239,15 @@ void StartFrame()
 .string gametypefilter;
 .string cvarfilter;
 bool DoesQ3ARemoveThisEntity(entity this);
-void SV_OnEntityPreSpawnFunction()
-{ENGINE_EVENT();
+void SV_OnEntityPreSpawnFunction(entity this)
+{
        __spawnfunc_expecting = true;
        __spawnfunc_expect = this;
        if (this)
        if (this.gametypefilter != "")
        if (!isGametypeInFilter(MapInfo_LoadedGametype, teamplay, have_team_spawns, this.gametypefilter))
        {
-               remove(this);
+               delete(this);
                __spawnfunc_expecting = false;
                return;
        }
@@ -354,7 +356,7 @@ LABEL(cvar_fail)
                if (!inv)
                {
                        //print("cvarfilter fail\n");
-                       remove(this);
+                       delete(this);
                        __spawnfunc_expecting = false;
                        return;
                }
@@ -362,11 +364,13 @@ LABEL(cvar_fail)
 
        if(DoesQ3ARemoveThisEntity(this))
        {
-               remove(this);
+               delete(this);
                __spawnfunc_expecting = false;
                return;
        }
 
+       set_movetype(this, this.movetype);
+
        // support special -1 and -2 angle from radiant
        if (this.angles == '0 -1 0')
                this.angles = '-90 0 0';
@@ -390,7 +394,7 @@ LABEL(cvar_fail)
 
        if(MUTATOR_CALLHOOK(OnEntityPreSpawn, this))
        {
-               remove(this);
+               delete(this);
                __spawnfunc_expecting = false;
                return;
        }
@@ -399,8 +403,8 @@ LABEL(cvar_fail)
 void WarpZone_PostInitialize_Callback()
 {
        // create waypoint links for warpzones
-       entity e;
-       for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
+       //for(entity e = warpzone_first; e; e = e.warpzone_next)
+       for(entity e = NULL; (e = find(e, classname, "trigger_warpzone")); )
        {
                vector src, dst;
                src = (e.absmin + e.absmax) * 0.5;
index e24a790a148a2cdebbe4bf3c40302178e6ee6adb..1ccf6695a185a1ad37558818e8ada3ae1205ccd6 100644 (file)
@@ -2,7 +2,6 @@
 
 #undef droptofloor
 #undef sound
-#undef remove
 #undef cvar_set
 #undef cvar_string
 #undef cvar
@@ -10,7 +9,7 @@
 var float(string name) cvar;
 var string(string name) cvar_string;
 var void(string name, string value) cvar_set;
-var void remove(entity e);
+var void delete_fn(entity e);
 
 #undef IT_SHOTGUN
 #undef IT_SUPER_SHOTGUN
index 7ab3f7b9f62763da2566d9409d6f53cb14ea1e80..9799a087ea0814e7a2d52b98f4f47a5c5d58e622 100644 (file)
@@ -1,7 +1,6 @@
 #pragma once
 
 #define droptofloor builtin_droptofloor
-#define remove builtin_remove
 #define cvar_set builtin_cvar_set
 #define cvar_string builtin_cvar_string
 #define cvar builtin_cvar
diff --git a/qcsrc/server/t_halflife.qc b/qcsrc/server/t_halflife.qc
deleted file mode 100644 (file)
index 7719542..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "t_halflife.qh"
-.float  roomtype;
-.float  radius;
-.float  pitch;
-.float  renderamt;
-.float  rendermode;
-.vector rendercolor;
-
-spawnfunc(weapon_crossbow) {}
-spawnfunc(weapon_handgrenade) {}
-spawnfunc(ammo_crossbow) {}
-spawnfunc(ammo_9mmclip) {}
-spawnfunc(ammo_gaussclip) {}
-spawnfunc(weapon_rpg) {}
-spawnfunc(weapon_357) {}
-void ammo_ARgrenades() {}
-spawnfunc(item_battery) {}
-spawnfunc(ammo_rpgclip) {}
-void weapon_9mmAR() {}
-spawnfunc(weapon_tripmine) {}
-spawnfunc(weapon_snark) {}
-spawnfunc(ammo_buckshot) {}
-void ammo_9mmAR() {}
-spawnfunc(ammo_357) {}
-spawnfunc(weapon_gauss) {}
-spawnfunc(weapon_hornetgun) {}
-//spawnfunc(weapon_shotgun) {}
-spawnfunc(item_healthkit) {}
-spawnfunc(item_longjump) {}
-spawnfunc(item_antidote) {}
-spawnfunc(func_recharge) {}
-spawnfunc(info_node) {}
-spawnfunc(env_sound) {}
-spawnfunc(light_spot) {}
-spawnfunc(func_healthcharger) {}
diff --git a/qcsrc/server/t_halflife.qh b/qcsrc/server/t_halflife.qh
deleted file mode 100644 (file)
index 6f70f09..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#pragma once
diff --git a/qcsrc/server/t_quake.qc b/qcsrc/server/t_quake.qc
deleted file mode 100644 (file)
index 8589ecc..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "t_quake.qh"
-
-#include "../common/weapons/all.qh"
-
-spawnfunc(weapon_electro);
-spawnfunc(weapon_hagar);
-spawnfunc(weapon_machinegun);
-spawnfunc(item_bullets);
-spawnfunc(item_armor_large);
-spawnfunc(item_armor_large);
-spawnfunc(item_health_mega);
-spawnfunc(item_health_medium);
-
-//***********************
-//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons
-//***********************
-spawnfunc(weapon_nailgun) {spawnfunc_weapon_electro(this);}
-spawnfunc(weapon_supernailgun) {spawnfunc_weapon_hagar(this);}
-spawnfunc(weapon_supershotgun) {spawnfunc_weapon_machinegun(this);}
-
-spawnfunc(item_spikes) {spawnfunc_item_bullets(this);}
-//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);}  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-spawnfunc(item_armor2) {spawnfunc_item_armor_large(this);}
-spawnfunc(item_armorInv) {spawnfunc_item_armor_large(this);} // TODO: make sure we actually want this
-spawnfunc(item_health) {if (this.spawnflags & 2) spawnfunc_item_health_mega(this);else spawnfunc_item_health_medium(this);}
-
-//spawnfunc_item_spikes
-//spawnfunc_item_health
-
-
-
diff --git a/qcsrc/server/t_quake.qh b/qcsrc/server/t_quake.qh
deleted file mode 100644 (file)
index 6f70f09..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#pragma once
diff --git a/qcsrc/server/t_quake3.qc b/qcsrc/server/t_quake3.qc
deleted file mode 100644 (file)
index 1bdb497..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-#include "t_quake3.qh"
-
-#include "../common/weapons/all.qh"
-
-spawnfunc(weapon_crylink);
-spawnfunc(weapon_electro);
-spawnfunc(weapon_hagar);
-spawnfunc(weapon_machinegun);
-spawnfunc(weapon_vortex);
-
-spawnfunc(target_items);
-
-spawnfunc(item_bullets);
-spawnfunc(item_cells);
-spawnfunc(item_rockets);
-spawnfunc(item_shells);
-
-spawnfunc(item_jetpack);
-
-spawnfunc(item_armor_big);
-spawnfunc(item_armor_large);
-spawnfunc(item_armor_small);
-
-spawnfunc(item_health_medium);
-spawnfunc(item_health_mega);
-
-//***********************
-//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons
-//***********************
-
-// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG
-
-// SG -> SG
-spawnfunc(ammo_shells)         { spawnfunc_item_shells(this);         }
-
-// MG -> MG
-spawnfunc(ammo_bullets)        { spawnfunc_item_bullets(this);        }
-
-// GL -> Mortar
-spawnfunc(ammo_grenades)       { spawnfunc_item_rockets(this);        }
-
-// LG -> Lightning
-spawnfunc(weapon_lightning)    { spawnfunc_weapon_electro(this);      }
-spawnfunc(ammo_lightning)      { spawnfunc_item_cells(this);          }
-
-// Plasma -> Hagar
-spawnfunc(weapon_plasmagun)    { spawnfunc_weapon_hagar(this);        }
-spawnfunc(ammo_cells)          { spawnfunc_item_rockets(this);        }
-
-// Rail -> Vortex
-spawnfunc(weapon_railgun)      { spawnfunc_weapon_vortex(this);          }
-spawnfunc(ammo_slugs)          { spawnfunc_item_cells(this);          }
-
-// BFG -> Crylink
-spawnfunc(weapon_bfg)          { spawnfunc_weapon_crylink(this);      }
-spawnfunc(ammo_bfg)            { spawnfunc_item_cells(this);          }
-
-// RL -> RL
-spawnfunc(ammo_rockets)        { spawnfunc_item_rockets(this);        }
-
-// Armor
-spawnfunc(item_armor_body)     { spawnfunc_item_armor_large(this);    }
-spawnfunc(item_armor_combat)   { spawnfunc_item_armor_big(this);      }
-spawnfunc(item_armor_shard)    { spawnfunc_item_armor_small(this);    }
-spawnfunc(item_enviro)         { spawnfunc_item_invincible(this);     }
-
-.float wait;
-.float delay;
-
-// weapon remove ent from df
-void target_init_verify(entity this)
-{
-       entity trigger, targ;
-       for(trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); )
-               for(targ = NULL; (targ = find(targ, targetname, trigger.target)); )
-                       if (targ.classname == "target_init" || targ.classname == "target_give" || targ.classname == "target_items")
-                       {
-                               trigger.wait = 0;
-                               trigger.delay = 0;
-                               targ.wait = 0;
-                               targ.delay = 0;
-
-                               //setsize(targ, trigger.mins, trigger.maxs);
-                               //setorigin(targ, trigger.origin);
-                               //remove(trigger);
-                       }
-}
-
-spawnfunc(target_init)
-{
-       this.spawnflags = 0; // remove all weapons except the ones listed below
-       this.netname = "shotgun"; // keep these weapons through the remove trigger
-       spawnfunc_target_items(this);
-       InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET);
-}
-
-// weapon give ent from defrag
-void target_give_init(entity this)
-{
-       entity targ;
-       for (targ = NULL; (targ = find(targ, targetname, this.target)); ) {
-               if (targ.classname == "weapon_rocketlauncher" || targ.classname == "weapon_devastator") {
-                       this.ammo_rockets += targ.count * WEP_CVAR(devastator, ammo);
-                       this.netname = "devastator";
-               }
-               else if (targ.classname == "weapon_plasmagun") {
-                       this.ammo_rockets += targ.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
-                       if(this.netname == "")
-                               this.netname = "hagar";
-                       else
-                               this.netname = strcat(this.netname, " hagar");
-               }
-               else if (targ.classname == "weapon_bfg") {
-                       this.ammo_cells += targ.count * WEP_CVAR_PRI(crylink, ammo);
-                       if(this.netname == "")
-                               this.netname = "crylink";
-                       else
-                               this.netname = strcat(this.netname, " crylink");
-               }
-               else if (targ.classname == "weapon_grenadelauncher" || targ.classname == "weapon_mortar") {
-                       this.ammo_rockets += targ.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO
-                       if(this.netname == "")
-                               this.netname = "mortar";
-                       else
-                               this.netname = strcat(this.netname, " mortar");
-               }
-               else if (targ.classname == "item_armor_body")
-                       this.armorvalue = 100;
-               else if (targ.classname == "item_health_mega")
-                       this.health = 200;
-               //remove(targ); // removing ents in init functions causes havoc, workaround:
-        setthink(targ, SUB_Remove);
-        targ.nextthink = time;
-       }
-       this.spawnflags = 2;
-       spawnfunc_target_items(this);
-       InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET);
-}
-
-spawnfunc(target_give)
-{
-       InitializeEntity(this, target_give_init, INITPRIO_FINDTARGET);
-}
-
-//spawnfunc(item_flight)       /* handled by jetpack */
-//spawnfunc(item_haste)        /* handled by buffs mutator */
-//spawnfunc(item_health)       /* handled in t_quake.qc */
-//spawnfunc(item_health_large) /* handled in t_items.qc */
-//spawnfunc(item_health_small) /* handled in t_items.qc */
-//spawnfunc(item_health_mega)  /* handled in t_items.qc */
-//spawnfunc(item_invis)        /* handled by buffs mutator */
-//spawnfunc(item_regen)        /* handled by buffs mutator */
-
-// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
-
-spawnfunc(item_flight)
-{
-       spawnfunc_item_jetpack(this);
-}
-
-.float notteam;
-.float notsingle;
-.float notfree;
-.float notq3a;
-.float notta;
-.string gametype;
-bool DoesQ3ARemoveThisEntity(entity this)
-{
-       // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY)
-
-       if(this.notq3a)
-               if(!teamplay || g_tdm || g_ctf)
-                       return true;
-
-       if(this.notta)
-               if (!(!teamplay || g_tdm || g_ctf))
-                       return true;
-
-       if(this.notsingle)
-               if(maxclients == 1)
-                       return true;
-
-       if(this.notteam)
-               if(teamplay)
-                       return true;
-
-       if(this.notfree)
-               if(!teamplay)
-                       return true;
-
-       if(this.gametype)
-       {
-               string gametypename;
-               // static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"}
-               gametypename = "ffa";
-               if(teamplay)
-                       gametypename = "team";
-               if(g_ctf)
-                       gametypename = "ctf";
-               if(maxclients == 1)
-                       gametypename = "single";
-               // we do not have the other types (oneflag, obelisk, harvester, teamtournament)
-               if(strstrofs(this.gametype, gametypename, 0) < 0)
-                       return true;
-       }
-
-       return false;
-}
diff --git a/qcsrc/server/t_quake3.qh b/qcsrc/server/t_quake3.qh
deleted file mode 100644 (file)
index 6f70f09..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#pragma once
index 8709a6aae6b5be4cc0c1c61f54ef71a7a45d292c..0918130159a737e83e63f5bd076af7320e3dc533 100644 (file)
@@ -1,18 +1,18 @@
 #include "teamplay.qh"
 
-#include "cl_client.qh"
+#include "client.qh"
 #include "race.qh"
 #include "scores.qh"
 #include "scores_rules.qh"
 
-#include "bot/bot.qh"
+#include "bot/api.qh"
 
 #include "command/vote.qh"
 
-#include "mutators/all.qh"
+#include "mutators/_mod.qh"
 
 #include "../common/deathtypes/all.qh"
-#include "../common/gamemodes/all.qh"
+#include "../common/gamemodes/_mod.qh"
 #include "../common/teams.qh"
 
 void TeamchangeFrags(entity e)
@@ -109,7 +109,7 @@ string getwelcomemessage(entity this)
                else
                        modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
        }
-       else if(cvar("g_balance_blaster_weaponstart") == 0)
+       else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
                modifications = strcat(modifications, ", No start weapons");
        if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
                modifications = strcat(modifications, ", Low gravity");
@@ -158,6 +158,16 @@ string getwelcomemessage(entity this)
        return s;
 }
 
+void setcolor(entity this, int clr)
+{
+#if 0
+       this.clientcolors = clr;
+       this.team = (clr & 15) + 1;
+#else
+       builtin_setcolor(this, clr);
+#endif
+}
+
 void SetPlayerColors(entity pl, float _color)
 {
        /*string s;
@@ -206,25 +216,23 @@ void SetPlayerTeam(entity pl, float t, float s, float noprint)
 // set c1...c4 to show what teams are allowed
 void CheckAllowedTeams (entity for_whom)
 {
-       int dm = 0;
+       int teams_mask = 0;
 
        c1 = c2 = c3 = c4 = -1;
        cb1 = cb2 = cb3 = cb4 = 0;
 
        string teament_name = string_null;
 
-       bool mutator_returnvalue = MUTATOR_CALLHOOK(GetTeamCount, dm, teament_name);
-       dm = M_ARGV(0, float);
+       bool mutator_returnvalue = MUTATOR_CALLHOOK(CheckAllowedTeams, teams_mask, teament_name);
+       teams_mask = M_ARGV(0, float);
        teament_name = M_ARGV(1, string);
 
        if(!mutator_returnvalue)
        {
-               if(dm >= 4)
-                       c1 = c2 = c3 = c4 = 0;
-               else if(dm >= 3)
-                       c1 = c2 = c3 = 0;
-               else
-                       c1 = c2 = 0;
+               if(teams_mask & BIT(0)) c1 = 0;
+               if(teams_mask & BIT(1)) c2 = 0;
+               if(teams_mask & BIT(2)) c3 = 0;
+               if(teams_mask & BIT(3)) c4 = 0;
        }
 
        // find out what teams are allowed if necessary
@@ -246,24 +254,46 @@ void CheckAllowedTeams (entity for_whom)
        }
 
        // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line)
-       if(c3==-1 && c4==-1)
+       if(AvailableTeams() == 2)
        if(autocvar_bot_vs_human && for_whom)
        {
                if(autocvar_bot_vs_human > 0)
                {
-                       // bots are all blue
+                       // find last team available
+
                        if(IS_BOT_CLIENT(for_whom))
-                               c1 = c3 = c4 = -1;
+                       {
+                               if(c4 >= 0) { c3 = c2 = c1 = -1; }
+                               else if(c3 >= 0) { c4 = c2 = c1 = -1; }
+                               else { c4 = c3 = c1 = -1; }
+                               // no further cases, we know at least 2 teams exist
+                       }
                        else
-                               c2 = -1;
+                       {
+                               if(c1 >= 0) { c2 = c3 = c4 = -1; }
+                               else if(c2 >= 0) { c1 = c3 = c4 = -1; }
+                               else { c1 = c2 = c4 = -1; }
+                               // no further cases, bots have one of the teams
+                       }
                }
                else
                {
-                       // bots are all red
+                       // find first team available
+
                        if(IS_BOT_CLIENT(for_whom))
-                               c2 = c3 = c4 = -1;
+                       {
+                               if(c1 >= 0) { c2 = c3 = c4 = -1; }
+                               else if(c2 >= 0) { c1 = c3 = c4 = -1; }
+                               else { c1 = c2 = c4 = -1; }
+                               // no further cases, we know at least 2 teams exist
+                       }
                        else
-                               c1 = -1;
+                       {
+                               if(c4 >= 0) { c3 = c2 = c1 = -1; }
+                               else if(c3 >= 0) { c4 = c2 = c1 = -1; }
+                               else { c4 = c3 = c1 = -1; }
+                               // no further cases, bots have one of the teams
+                       }
                }
        }
 
@@ -426,8 +456,12 @@ float TeamSmallerEqThanTeam(float ta, float tb, entity e)
 // NOTE: Assumes CheckAllowedTeams has already been called!
 float FindSmallestTeam(entity pl, float ignore_pl)
 {
-       float totalteams, t;
-       totalteams = 0;
+       int totalteams = 0;
+       int t = 1; // initialize with a random team?
+       if(c4 >= 0) t = 4;
+       if(c3 >= 0) t = 3;
+       if(c2 >= 0) t = 2;
+       if(c1 >= 0) t = 1;
 
        // find out what teams are available
        //CheckAllowedTeams();
@@ -449,8 +483,10 @@ float FindSmallestTeam(entity pl, float ignore_pl)
        {
                if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl))
                        return 1; // special case for campaign and player joining
-               else
-                       error(sprintf("Too few teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
+               else if(totalteams == 1) // single team
+                       LOG_TRACEF("Only 1 team available for %s, you may need to fix your map", MapInfo_Type_ToString(MapInfo_CurrentGametype()));
+               else // no teams, major no no
+                       error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
        }
 
        // count how many players are in each team
@@ -461,7 +497,8 @@ float FindSmallestTeam(entity pl, float ignore_pl)
 
        RandomSelection_Init();
 
-       t = 1;
+       if(TeamSmallerEqThanTeam(1, t, pl))
+               t = 1;
        if(TeamSmallerEqThanTeam(2, t, pl))
                t = 2;
        if(TeamSmallerEqThanTeam(3, t, pl))
@@ -471,13 +508,13 @@ float FindSmallestTeam(entity pl, float ignore_pl)
 
        // now t is the minimum, or A minimum!
        if(t == 1 || TeamSmallerEqThanTeam(1, t, pl))
-               RandomSelection_Add(NULL, 1, string_null, 1, 1);
+               RandomSelection_AddFloat(1, 1, 1);
        if(t == 2 || TeamSmallerEqThanTeam(2, t, pl))
-               RandomSelection_Add(NULL, 2, string_null, 1, 1);
+               RandomSelection_AddFloat(2, 1, 1);
        if(t == 3 || TeamSmallerEqThanTeam(3, t, pl))
-               RandomSelection_Add(NULL, 3, string_null, 1, 1);
+               RandomSelection_AddFloat(3, 1, 1);
        if(t == 4 || TeamSmallerEqThanTeam(4, t, pl))
-               RandomSelection_Add(NULL, 4, string_null, 1, 1);
+               RandomSelection_AddFloat(4, 1, 1);
 
        return RandomSelection_chosen_float;
 }
@@ -515,7 +552,7 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam)
                                SetPlayerColors(this, selectedteam - 1);
 
                                // when JoinBestTeam is called by client.qc/ClientKill_Now_TeamChange the players team is -1 and thus skipped
-                               // when JoinBestTeam is called by cl_client.qc/ClientConnect the player_id is 0 the log attempt is rejected
+                               // when JoinBestTeam is called by client.qc/ClientConnect the player_id is 0 the log attempt is rejected
                                LogTeamchange(this.playerid, this.team, 99);
                        }
                        return selectedteam;
@@ -559,8 +596,8 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam)
 }
 
 //void() ctf_playerchanged;
-void SV_ChangeTeam(float _color)
-{ENGINE_EVENT();
+void SV_ChangeTeam(entity this, float _color)
+{
        float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount;
 
        // in normal deathmatch we can just apply the color and we're done
index c9713984e809a2a1fc49a1c4f7ca61910b0dcdb5..127ac7a6d30caffc887e9571c40104148813eadb 100644 (file)
@@ -48,6 +48,7 @@ float FindSmallestTeam(entity pl, float ignore_pl);
 int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam);
 
 //void() ctf_playerchanged;
-void SV_ChangeTeam(float _color);
 
 void ShufflePlayerOutOfTeam (float source_team);
+
+void setcolor(entity this, int clr);
index 7bbf00dc51c335262f6b3a757b3d97188d5692c5..96674db702943a933786ca954c4ec20498839ae8 100644 (file)
@@ -3,8 +3,8 @@
 void test_weapons_hurt(entity this)
 {
     EXPECT_NE(100, this.health);
-    remove(this.enemy);
-    remove(this);
+    delete(this.enemy);
+    delete(this);
 }
 
 TEST(Weapons, Hurt)
index be6445b48e4c696ca82ffef01dd7207807229754..e6d6f66a0d0442ef426980cf355006af81698fd6 100644 (file)
@@ -1,11 +1,11 @@
 #pragma once
 
 #include "autocvars.qh"
-#include "cl_client.qh"
-#include "command/all.qh"
+#include "client.qh"
+#include "command/_mod.qh"
 #include "weapons/common.qh"
 #include "weapons/selection.qh"
 #include <common/items/item.qh>
 #include <common/physics/player.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/vehicles/all.qh>
index abb41ad06a8e812569217467106b04f8e2877600..7cc06da3e6dc05b32595b85556ff43d4ecb11225 100644 (file)
@@ -1,10 +1,11 @@
 #include "accuracy.qh"
 
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/teams.qh>
 #include <common/util.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 int accuracy_byte(float n, float d)
 {
@@ -47,7 +48,7 @@ void accuracy_init(entity e)
 
 void accuracy_free(entity e)
 {
-       remove(e.accuracy);
+       delete(e.accuracy);
 }
 
 // force a resend of a player's accuracy stats
index 954f82564fb9a5e17a8ee49924987c3c45e22a00..6d163755067def2224a973db9bc311f1dcb60770 100644 (file)
@@ -2,11 +2,12 @@
 
 #include <common/t_items.qh>
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/deathtypes/all.qh>
 #include <common/notifications/all.qh>
 #include <common/util.qh>
-#include <common/weapons/all.qh>
-#include <common/items/all.qc>
+#include <common/weapons/_all.qh>
+#include <common/items/_mod.qh>
 
 void W_GiveWeapon(entity e, int wep)
 {
index 7e7478e9b8ea8aedd9e71f0b49f839084849bf93..9fcfd34c205b712ee1ff716693cd18415a860dbc 100644 (file)
@@ -5,7 +5,8 @@
 #include "../command/common.qh"
 
 #include <common/constants.qh>
-#include <common/weapons/all.qh>
+#include <common/net_linked.qh>
+#include <common/weapons/_all.qh>
 
 .float csqcprojectile_type;
 
@@ -83,7 +84,7 @@ void CSQCProjectile(entity e, float clientanimate, int type, float docull)
 
        e.csqcprojectile_clientanimate = clientanimate;
 
-       if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE)
+       if(e.move_movetype == MOVETYPE_TOSS || e.move_movetype == MOVETYPE_BOUNCE)
        {
                if(e.gravity == 0)
                        e.gravity = 1;
index ec1fd089b1abd6cfeaf7fae56bf9d7546e975b0e..372f7357b4057a95d63cba482b826d87b23a441d 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "../antilag.qh"
 #include "../g_subs.qh"
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/state.qh>
 
 vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v)
index 70284a8f4b421055768b2db0658199ab7d4bd83c..cdb337d145bb948a877141cd610f4a0b74a58acc 100644 (file)
@@ -3,9 +3,10 @@
 #include "weaponsystem.qh"
 #include <common/t_items.qh>
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/util.qh>
 #include <common/items/item.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/state.qh>
 #include <common/mutators/mutator/waypoints/waypointsprites.qh>
 
@@ -21,15 +22,13 @@ void Send_WeaponComplain(entity e, float wpn, float type)
 void Weapon_whereis(Weapon this, entity cl)
 {
        if (!autocvar_g_showweaponspawns) return;
-       for (entity it = NULL; (it = findfloat(it, weapon, this.m_id)); )
+       IL_EACH(g_items, it.weapon == this.m_id && (!it.team || (it.ItemStatus & ITS_AVAILABLE)),
        {
                if (it.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2)
                        continue;
-               if (!(it.flags & FL_ITEM))
-                       continue;
                entity wp = WaypointSprite_Spawn(
                        WP_Weapon,
-                       1, 0,
+                       -2, 0,
                        NULL, it.origin + ('0 0 1' * it.maxs.z) * 1.2,
                        cl, 0,
                        NULL, enemy,
@@ -37,7 +36,7 @@ void Weapon_whereis(Weapon this, entity cl)
                        RADARICON_NONE
                );
                wp.wp_extra = this.m_id;
-       }
+       });
 }
 
 bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain)
@@ -75,7 +74,7 @@ bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain)
 
                                // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
                                if(wpn == WEP_MINE_LAYER)
-                                       FOREACH_ENTITY_CLASS("mine", it.owner == this,
+                                       IL_EACH(g_mines, it.owner == this,
                                        {
                                                f = 1;
                                                break; // no need to continue
@@ -101,7 +100,15 @@ bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain)
                if (weaponsInMap & WepSet_FromWeapon(wpn))
                {
                        Send_WeaponComplain(this, wpn.m_id, 1);
-                       Weapon_whereis(wpn, this);
+                       if(autocvar_g_showweaponspawns < 3)
+                               Weapon_whereis(wpn, this);
+                       else
+                       {
+                               FOREACH(Weapons, it.impulse == wpn.impulse,
+                               {
+                                       Weapon_whereis(it, this);
+                               });
+                       }
                }
                else
                {
@@ -264,8 +271,11 @@ void W_SwitchWeapon(entity this, Weapon w)
        }
        else if(!forbidWeaponUse(this)) {
                entity actor = this;
-               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-               w.wr_reload(w, actor, weaponentity);
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       w.wr_reload(w, actor, weaponentity);
+               }
        }
 }
 
index 748f434eb325dd3cf002279d599cd8ed2dade4ef..00bc0e5d12397c2e608cae53f245a1593d2d3e3c 100644 (file)
@@ -1,9 +1,9 @@
 #include "spawning.qh"
 
 #include "weaponsystem.qh"
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 #include <common/t_items.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 string W_Apply_Weaponreplace(string in)
 {
@@ -29,7 +29,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e)
        {
                if (e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
                {
-                       LOG_MAPWARNF("Attempted to spawn a mutator-blocked weapon rejected: prvm_edict server %i", this);
+                       LOG_WARNF("Attempted to spawn a mutator-blocked weapon rejected: prvm_edict server %i", this);
                        startitem_failed = true;
                        return;
                }
@@ -39,7 +39,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e)
                s = M_ARGV(2, string);
                if (s == "")
                {
-                       remove(this);
+                       delete(this);
                        startitem_failed = true;
                        return;
                }
@@ -76,7 +76,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e)
                }
                if (wpn == WEP_Null)
                {
-                       remove(this);
+                       delete(this);
                        startitem_failed = true;
                        return;
                }
index c69b8e9a9ce1c2ebb70e2c8799f84535a3a2b606..30b700098d4a056d150ba589ce39d57b1376ff99 100644 (file)
@@ -1,7 +1,7 @@
 #include "throwing.qh"
 
 #include "weaponsystem.qh"
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 #include <common/t_items.qh>
 #include "../g_damage.qh"
 #include <common/items/item.qh>
@@ -9,7 +9,7 @@
 #include <common/notifications/all.qh>
 #include <common/triggers/subs.qh>
 #include <common/util.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/state.qh>
 
 void thrown_wep_think(entity this)
@@ -19,7 +19,10 @@ void thrown_wep_think(entity this)
        {
                this.SendFlags |= ISF_LOCATION;
                this.oldorigin = this.origin;
+               this.bot_pickup = false;
        }
+       else
+               this.bot_pickup = true;
        this.owner = NULL;
        float timeleft = this.savenextthink - time;
        if(timeleft > 1)
@@ -45,6 +48,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto
        wep.owner = wep.enemy = own;
        wep.flags |= FL_TOSSED;
        wep.colormap = own.colormap;
+       wep.glowmod = weaponentity_glowmod(info, own, own.clientcolors);
 
        W_DropEvent(wr_drop,own,wpn,wep);
 
@@ -79,7 +83,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto
        weapon_defaultspawnfunc(wep, info);
        if(startitem_failed)
                return string_null;
-       wep.glowmod = weaponentity_glowmod(info, own.clientcolors);
        setthink(wep, thrown_wep_think);
        wep.savenextthink = wep.nextthink;
        wep.nextthink = min(wep.nextthink, time + 0.5);
@@ -165,7 +168,7 @@ bool W_IsWeaponThrowable(entity this, int w)
 }
 
 // toss current weapon
-void W_ThrowWeapon(entity this, vector velo, vector delta, float doreduce)
+void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, float doreduce)
 {
        Weapon w = PS(this).m_weapon;
        if (w == WEP_Null)
@@ -174,7 +177,6 @@ void W_ThrowWeapon(entity this, vector velo, vector delta, float doreduce)
                return;
        if(!autocvar_g_weapon_throwable)
                return;
-       .entity weaponentity = weaponentities[0]; // TODO: unhardcode
        if(this.(weaponentity).state != WS_READY)
                return;
        if(!W_IsWeaponThrowable(this, w.m_id))
index 5a27025bc8bfcd146ed19a9a52eed1600ed66f75..a03968083edbd5e1e0a1c06a8471a1058b8b8843 100644 (file)
@@ -9,6 +9,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto
 bool W_IsWeaponThrowable(entity this, int w);
 
 // toss current weapon
-void W_ThrowWeapon(entity this, vector velo, vector delta, float doreduce);
+void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, float doreduce);
 
 void SpawnThrownWeapon(entity this, vector org, float w);
index 5c64a0ec64d3ff6e89f5d7f2efbf8d0ac7383355..b49ed88f596ca00a7ef4e6b3467b7e5a3f36c2e2 100644 (file)
 #include "../antilag.qh"
 
 #include <common/constants.qh>
+#include <common/net_linked.qh>
 #include <common/util.qh>
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/state.qh>
 
 #include <lib/warpzone/common.qh>
@@ -20,9 +21,9 @@
 // this function calculates w_shotorg and w_shotdir based on the weapon model
 // offset, trueaim and antilag, and won't put w_shotorg inside a wall.
 // make sure you call makevectors first (FIXME?)
-void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range)
+void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range)
 {
-    TC(Sound, snd);
+       TC(Sound, snd);
        float nudge = 1; // added to traceline target and subtracted from result  TOOD(divVerent): do we still need this? Doesn't the engine do this now for us?
        float oldsolid;
        vector vecs, dv;
@@ -58,7 +59,6 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m
        if(IS_PLAYER(ent))
                W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
 
-       .entity weaponentity = weaponentities[0]; // TODO: unhardcode
        vector md = ent.(weaponentity).movedir;
        if(md.x > 0)
                vecs = md;
@@ -300,7 +300,7 @@ void FireRailgunBullet (entity this, vector start, vector end, float bdamage, fl
                ));
 
                if(pseudoprojectile)
-                       remove(pseudoprojectile);
+                       delete(pseudoprojectile);
        }
 
        // find all the entities the railgun hit and hurt them
@@ -377,9 +377,9 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s
        if(lag)
        {
                FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_takeback(it, CS(it), time - lag));
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
-                       if (it != this)
-                               antilag_takeback(it, it, time - lag);
+               IL_EACH(g_monsters, it != this,
+               {
+                       antilag_takeback(it, it, time - lag);
                });
        }
 
@@ -400,7 +400,7 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s
                entity hit = trace_ent;
 
                // When hitting sky, stop.
-               if (pointcontents(start) == CONTENT_SKY)
+               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
                        break;
 
                // can't use noimpact, as we need to pass through walls
@@ -446,7 +446,7 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s
                        }
                }
 
-               if (is_weapclip)
+               if (is_weapclip && !autocvar_g_ballistics_penetrate_clips)
                        break;
 
                // go through solid!
@@ -476,7 +476,10 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s
                        break;
 
                float dist_taken = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - start));
-               solid_penetration_left *= (dist_taken / maxdist);
+               // fraction_used_of_what_is_left = dist_taken / maxdist
+               // solid_penetration_left = solid_penetration_left - solid_penetration_left * fraction_used_of_what_is_left
+               solid_penetration_left *= 1 - dist_taken / maxdist;
+               solid_penetration_left = max(solid_penetration_left, 0);
 
                // Only show effect when going through a player (invisible otherwise)
                if (hit && (hit.solid != SOLID_BSP))
@@ -492,9 +495,9 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s
        if(lag)
        {
                FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_restore(it, CS(it)));
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
-                       if (it != this)
-                               antilag_restore(it, it);
+               IL_EACH(g_monsters, it != this,
+               {
+                       antilag_restore(it, it);
                });
        }
 
index 52e5bd840e21e9adf9eb57a4e8cf3c9366000ab3..21f12c1e4c02633ef9d16f658b99990a87cf74b9 100644 (file)
@@ -7,13 +7,13 @@ vector w_shotend;
 // this function calculates w_shotorg and w_shotdir based on the weapon model
 // offset, trueaim and antilag, and won't put w_shotorg inside a wall.
 // make sure you call makevectors first (FIXME?)
-void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range);
+void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range);
 
-#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE)
-#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage)
-#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
-#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
-#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range)
+#define W_SetupShot_Dir_ProjectileSize(ent,wepent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, wepent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE)
+#define W_SetupShot_ProjectileSize(ent,wepent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, wepent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot_Dir(ent,wepent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, wepent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot(ent,wepent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, wepent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot_Range(ent,wepent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, wepent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range)
 
 vector W_CalculateProjectileVelocity(entity actor, vector pvelocity, vector mvelocity, float forceAbsolute);
 
index 2b4be7e917f32fb71bd0b9f1a375399af03344c2..2ffb1c1ec8dfa60403d672d524f5239af73da64f 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "../g_world.qh"
 
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 
 void WeaponStats_Init()
 {
index 21057e4a0b525a02a14c995b9294f78b64bee52d..ad479824ec76f654a21877f546b0c273a4fff525 100644 (file)
@@ -3,15 +3,16 @@
 #include "selection.qh"
 
 #include "../command/common.qh"
-#include "../mutators/all.qh"
+#include "../mutators/_mod.qh"
 #include "../round_handler.qh"
 #include <common/t_items.qh>
 #include <common/animdecide.qh>
 #include <common/constants.qh>
-#include <common/monsters/all.qh>
+#include <common/net_linked.qh>
+#include <common/monsters/_mod.qh>
 #include <common/notifications/all.qh>
 #include <common/util.qh>
-#include <common/weapons/all.qh>
+#include <common/weapons/_all.qh>
 #include <common/state.qh>
 #include <lib/csqcmodel/sv_model.qh>
 
@@ -21,7 +22,9 @@
 
 float W_WeaponRateFactor(entity this)
 {
-       float t = 1.0 / g_weaponratefactor;
+       float t = 1;
+       if(g_weaponratefactor > 0)
+               t = 1.0 / g_weaponratefactor;
 
        MUTATOR_CALLHOOK(WeaponRateFactor, t, this);
        t = M_ARGV(0, float);
@@ -40,10 +43,10 @@ float W_WeaponSpeedFactor(entity this)
 }
 
 
-bool CL_Weaponentity_CustomizeEntityForClient(entity this)
+bool CL_Weaponentity_CustomizeEntityForClient(entity this, entity client)
 {
        this.viewmodelforclient = this.owner;
-       if (IS_SPEC(other) && other.enemy == this.owner) this.viewmodelforclient = other;
+       if (IS_SPEC(client) && client.enemy == this.owner) this.viewmodelforclient = client;
        return true;
 }
 
@@ -54,7 +57,7 @@ vector CL_Weapon_GetShotOrg(int wpn)
        CL_WeaponEntity_SetModel(e, wi.mdl, false);
        vector ret = e.movedir;
        CL_WeaponEntity_SetModel(e, "", false);
-       remove(e);
+       delete(e);
        return ret;
 }
 
@@ -64,13 +67,13 @@ vector CL_Weapon_GetShotOrg(int wpn)
 void CL_Weaponentity_Think(entity this)
 {
        this.nextthink = time;
-       if (intermission_running) this.frame = this.anim_idle.x;
+       if (gameover) this.frame = this.anim_idle.x;
        .entity weaponentity = this.weaponentity_fld;
        if (this.owner.(weaponentity) != this)
        {
                // owner has new gun; remove old one
-               if (this.weaponchild) remove(this.weaponchild);
-               remove(this);
+               if (this.weaponchild) delete(this.weaponchild);
+               delete(this);
                return;
        }
        if (IS_DEAD(this.owner))
@@ -110,7 +113,7 @@ void CL_ExteriorWeaponentity_Think(entity this)
        this.nextthink = time;
        if (this.owner.exteriorweaponentity != this)
        {
-               remove(this);
+               delete(this);
                return;
        }
        if (IS_DEAD(this.owner))
@@ -150,7 +153,7 @@ void CL_ExteriorWeaponentity_Think(entity this)
        else this.alpha = 1;
 
     Weapon wep = PS(this.owner).m_weapon;
-       if (wep) this.glowmod = weaponentity_glowmod(wep, this.owner.clientcolors);
+       if (wep) this.glowmod = weaponentity_glowmod(wep, this.owner, this.owner.clientcolors);
        this.colormap = this.owner.colormap;
 
        CSQCMODEL_AUTOUPDATE(this);
@@ -214,8 +217,12 @@ bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, bool secondary
        if (ammo) return true;
        // always keep the Mine Layer if we placed mines, so that we can detonate them
        if (thiswep == WEP_MINE_LAYER)
-               for (entity mine; (mine = find(mine, classname, "mine")); )
-                       if (mine.owner == actor) return false;
+       {
+               IL_EACH(g_mines, it.owner == actor,
+               {
+                       return false;
+               });
+       }
 
        if (thiswep == WEP_SHOTGUN)
                if (!secondary && WEP_CVAR(shotgun, secondary) == 1) return false;           // no clicking, just allow
@@ -386,7 +393,9 @@ void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void(
 
        if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
        {
-               int act = (fr == WFRAME_FIRE2 && (PS(actor).m_weapon == WEP_SHOCKWAVE || PS(actor).m_weapon == WEP_SHOTGUN))
+               bool primary_melee = boolean(fr == WFRAME_FIRE1 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI));
+               bool secondary_melee = boolean(fr == WFRAME_FIRE2 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC));
+               int act = (primary_melee || secondary_melee)
                        ? ANIMACTION_MELEE
                        : ANIMACTION_SHOOT
                        ;
@@ -403,18 +412,19 @@ bool forbidWeaponUse(entity player)
        if (time < game_starttime && !autocvar_sv_ready_restart_after_countdown) return true;
        if (round_handler_IsActive() && !round_handler_IsRoundStarted()) return true;
        if (player.player_blocked) return true;
+       if (gameover) return true;
        if (STAT(FROZEN, player)) return true;
        if (player.weapon_blocked) return true;
+       if (MUTATOR_CALLHOOK(ForbidWeaponUse, player)) return true;
        return false;
 }
 
 .bool hook_switchweapon;
 
-void W_WeaponFrame(Player actor)
+void W_WeaponFrame(Player actor, .entity weaponentity)
 {
     TC(Player, actor);
     TC(PlayerState, PS(actor));
-       .entity weaponentity = weaponentities[0];  // TODO: unhardcode
        entity this = actor.(weaponentity);
        if (frametime) actor.weapon_frametime = frametime;
 
@@ -437,7 +447,6 @@ void W_WeaponFrame(Player actor)
                PS(actor).m_switchingweapon = WEP_Null;
                this.state = WS_CLEAR;
                actor.weaponname = "";
-               // actor.items &= ~IT_AMMO;
                return;
        }
 
@@ -452,7 +461,7 @@ void W_WeaponFrame(Player actor)
                switch (this.state)
                {
                        default:
-                               LOG_WARNINGF("unhandled weaponentity (%i) state for player (%i): %d\n", this, actor, this.state);
+                               LOG_WARNF("unhandled weaponentity (%i) state for player (%i): %d", this, actor, this.state);
                                break;
                        case WS_INUSE:
                        case WS_RAISE:
@@ -563,7 +572,7 @@ void W_WeaponFrame(Player actor)
                        }
                        else if (e)
                        {
-                               e.wr_gonethink(e, actor);
+                               e.wr_gonethink(e, actor, weaponentity);
                        }
                }
 
@@ -586,9 +595,8 @@ void W_WeaponFrame(Player actor)
        }
 }
 
-void W_AttachToShotorg(entity actor, entity flash, vector offset)
+void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset)
 {
-       .entity weaponentity = weaponentities[0];
        flash.owner = actor;
        flash.angles_z = random() * 360;
 
@@ -683,10 +691,9 @@ void W_ReloadedAndReady(Weapon thiswep, entity actor, .entity weaponentity, int
        w_ready(wpn, actor, weaponentity, PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1));
 }
 
-void W_Reload(entity actor, float sent_ammo_min, Sound sent_sound)
+void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sent_sound)
 {
     TC(Sound, sent_sound);
-       .entity weaponentity = weaponentities[0];
        // set global values to work with
        Weapon e = PS(actor).m_weapon;
 
index 2aeca53811ecce7b1dae7595aea7b77b4713921a..b7ca597046fea210e290d2a00af5b10ef6a3367b 100644 (file)
@@ -8,17 +8,17 @@ void CL_SpawnWeaponentity(entity e, .entity weaponentity);
 
 vector CL_Weapon_GetShotOrg(float wpn);
 
-float forbidWeaponUse(entity player);
+bool forbidWeaponUse(entity player);
 
-void W_AttachToShotorg(entity actor, entity flash, vector offset);
+void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset);
 
 void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use);
 
 void W_DropEvent(.void(Weapon, entity actor) event, entity player, float weapon_type, entity weapon_item);
 
-void W_Reload(entity actor, float sent_ammo_min, Sound sent_sound);
+void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sent_sound);
 
-void W_WeaponFrame(Player actor);
+void W_WeaponFrame(Player actor, .entity weaponentity);
 
 float W_WeaponRateFactor(entity this);
 
diff --git a/qcsrc/tools/auto-super.pl b/qcsrc/tools/auto-super.pl
new file mode 100644 (file)
index 0000000..00926d0
--- /dev/null
@@ -0,0 +1,101 @@
+my %classoffile = ();
+my %classes = ();
+my %baseclass = ();
+my %methods = ();
+my %attrs = ();
+my %methodnames = ();
+my %old2new = ();
+
+print STDERR "Scanning...\n";
+for my $f(@ARGV)
+{
+       open my $fh, '<', $f;
+       while(<$fh>)
+       {
+               if(/^CLASS\(([^)]*)\)(?:\s*EXTENDS\(([^)]*)\))?/)
+               {
+                       $classes{$1} = defined($2) ? $2 : "Object";
+                       $classoffile{$f} = $1;
+               }
+               if(/^\s*METHOD\(([^),]*),\s*([^),]*)/)
+               {
+                       $methods{$1}{$2} = $1;
+                       $methodnames{"$1"."_"."$2"} = $f;
+                       $old2new{"$2$1"} = "$1"."_"."$2";
+               }
+               if(/^\s*ATTRIB(?:ARRAY)?\(([^),]*),\s*([^),]*)/)
+               {
+                       $attrs{$1}{$2} = $1;
+               }
+       }
+       close $fh;
+}
+
+# propagate down methods etc.
+print STDERR "Propagating...\n";
+for my $class(keys %classes)
+{
+       print STDERR "$class";
+       my $base = $class;
+       for(;;)
+       {
+               $base = $classes{$base};
+               last if not defined $base;
+               print STDERR " -> $base";
+               while(my ($method, $definingclass) = each %{$methods{$base}})
+               {
+                       $methods{$class}{$method} = $definingclass
+                               if not defined $methods{$class}{$method};
+               }
+               while(my ($attr, $definingclass) = each %{$attrs{$base}})
+               {
+                       $attrs{$class}{$attr} = $definingclass
+                               if not defined $attrs{$class}{$attr};
+               }
+       }
+       print STDERR "\n";
+}
+
+# change all calls to base method to super, complain about skipping
+print STDERR "Fixing...\n";
+for my $f(@ARGV)
+{
+       open my $fh, '<', $f;
+       my $s = do { undef local $/; <$fh>; };
+       my $s0 = $s;
+       close $fh;
+
+       my $class = $classoffile{$f};
+       my $base = $classes{$class};
+       next if not defined $base;
+
+       for(keys %old2new)
+       {
+               $s =~ s/\b$_\b/$old2new{$_}/g;
+       }
+
+       my @methods_super = map { [ $methods{$base}{$_} . "_" . $_, "SUPER($class).$_" ]; } keys %{$methods{$base}};
+       for(@methods_super)
+       {
+               my ($search, $replace) = @$_;
+               my $n = ($s =~ s/\b$search\b/$replace/g);
+               print STDERR "[$f] $search -> $replace... $n replacements\n"
+                       if $n;
+       }
+
+       for(grep { $methodnames{$_} ne $f } keys %methodnames)
+       {
+               if($s =~ /\b$_\b/)
+               {
+                       print STDERR "[$f] calls non-super external method directly: $_\n";
+               }
+       }
+
+       if($s ne $s0)
+       {
+               print STDERR "Rewriting $f...\n";
+               open my $fh, '>', $f;
+               print $fh $s;
+               close $fh;
+       }
+}
diff --git a/qcsrc/tools/cloc.txt b/qcsrc/tools/cloc.txt
new file mode 100644 (file)
index 0000000..9b1b3af
--- /dev/null
@@ -0,0 +1,13 @@
+QC
+    filter call_regexp_common C++
+    filter remove_inline //.*$
+    extension qc
+    extension inc
+    3rd_gen_scale 0.5
+    end_of_line_continuation \\$
+QC Header
+    filter call_regexp_common C++
+    filter remove_inline //.*$
+    extension qh
+    3rd_gen_scale 0.5
+    end_of_line_continuation \\$
index 3c794e93fbba747b9eb43b6954a86a3c4a0856cf..ebb232105f51a66d03770486c0afe3950c44f7fb 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
 
 WORKDIR=../.tmp
 
@@ -9,8 +9,8 @@ CPP="cc -xc -E"
 
 declare -a QCCDEFS=(
     -DNDEBUG=1
+    -DXONOTIC=1
     -DWATERMARK="\"$(git describe --tags --dirty='~')\""
-    -DDEBUGPATHING=0
 )
 QCCDEFS="${QCCDEFS[@]}"
 
@@ -34,23 +34,22 @@ QCCFLAGS="${QCCFLAGS[@]} ${NOWARN[@]}"
 cd ..
 
 function check1() {
-    declare -l base="${1}"
-    MODE=${2}
-    declare -l file="${3}"
+    declare -l prog="${1}"
+    declare -l file="${2}"
+    MODE=${prog}
     qpp ${file} test.dat \
-            -include lib/_all.inc -include ${base}/_all.qh \
-            -I. ${QCCIDENT} ${QCCDEFS} -D${MODE} > ${WORKDIR}/${MODE}.qc
-    qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${MODE}.qc >/dev/null
+            -include lib/_all.inc -include ${prog}/_all.qh \
+            -I. ${QCCIDENT} ${QCCDEFS} > ${WORKDIR}/${prog}.qc
+    qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${prog}.qc >/dev/null
 }
 
 function check() {
-    declare -l base="${1}"
-    MODE=${2}
-    find ${base} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do
-        check1 ${base} ${MODE} ${file}
+    declare -l prog="${1}"
+    find ${prog} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do
+        check1 ${prog} ${file}
     done
 }
 
-check client CSQC
-check server SVQC
-check menu MENUQC
+check client
+check server
+check menu
index b4c4fc3048ceea24877006057e5749bc7e314a7c..9a3ba10987b05622ad8873ee16783475a9f09b98 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
 cd ..
 ROOT=$PWD/
 
@@ -12,26 +12,42 @@ function genmod() {
     echo '// generated file; do not modify' > ${MOD}.inc
     echo '// generated file; do not modify' > ${MOD}.qh
     for f in $(ls | sort -k 1,1 -t .); do
-        if [[ "$f" == cl_* ]]; then if [[ -f "${f#cl_}" ]]; then continue; fi; fi
-        if [[ "$f" == sv_* ]]; then if [[ -f "${f#sv_}" ]]; then continue; fi; fi
-        if [[ "$f" == ui_* ]]; then if [[ -f "${f#ui_}" ]]; then continue; fi; fi
+          if [[ "$f" == cl_* ]]; then f="${f#cl_}"; if [[ -f "$f" ]]; then continue; fi
+        elif [[ "$f" == sv_* ]]; then f="${f#sv_}"; if [[ -f "$f" ]]; then continue; fi
+        elif [[ "$f" == ui_* ]]; then f="${f#ui_}"; if [[ -f "$f" ]]; then continue; fi
+        fi
         if [[ "$f" == *.qc ]]; then
-            echo "#include <${CTX}$f>" >> ${MOD}.inc
-            echo "#include <${CTX}${f%.qc}.qh>" >> ${MOD}.qh
+            if [[ -f "$f" ]]; then echo -e "#include <${CTX}$f>" >> ${MOD}.inc; fi
+            if [[ -f "${f%.qc}.qh" ]]; then echo -e "#include <${CTX}${f%.qc}.qh>" >> ${MOD}.qh; fi
             if [[ -f "cl_$f" ]]; then echo -e "#ifdef CSQC\n    #include <${CTX}cl_$f>\n#endif" >> ${MOD}.inc; fi
+            if [[ -f "cl_${f%.qc}.qh" ]]; then echo -e "#ifdef CSQC\n    #include <${CTX}cl_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi
             if [[ -f "sv_$f" ]]; then echo -e "#ifdef SVQC\n    #include <${CTX}sv_$f>\n#endif" >> ${MOD}.inc; fi
+            if [[ -f "sv_${f%.qc}.qh" ]]; then echo -e "#ifdef SVQC\n    #include <${CTX}sv_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi
             if [[ -f "ui_$f" ]]; then echo -e "#ifdef MENUQC\n    #include <${CTX}ui_$f>\n#endif" >> ${MOD}.inc; fi
+            if [[ -f "ui_${f%.qc}.qh" ]]; then echo -e "#ifdef MENUQC\n    #include <${CTX}ui_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi
         fi
     done
-    # echo >> ${MOD}
+    declare -l rec=1
+    if [[ -f "_all.inc" ]]; then rec=0; fi
     for f in *; do if [ -d "$f" ]; then
         (cd -- "$f" && genmod)
-        # echo "#include \"$f/MOD\"" >> ${MOD}
+        if [[ $rec == 1 ]]; then
+            rec=2
+            echo >> ${MOD}.inc
+            echo >> ${MOD}.qh
+        fi
+        if [[ $rec != 0 ]]; then
+            declare -l mod=_mod
+            if [[ -f "$f/_all.inc" ]]; then mod=_all; fi
+            echo "#include <${CTX}$f/${mod}.inc>" >> ${MOD}.inc
+            echo "#include <${CTX}$f/${mod}.qh>" >> ${MOD}.qh
+        fi
     fi; done
 }
 
 (cd lib; genmod)
 (cd common; genmod)
+(cd ecs; genmod)
 (cd client; genmod)
 (cd server; genmod)
 (cd menu; genmod)
index ef1731592646e86da9bb2858b8a769634df7fd07..924083166f08e0650e4c08f624eded6b6835822c 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
 cd ..
 
 function startswith() {
@@ -17,14 +17,14 @@ function check() {
     find "$base" -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do
         echo "$file"
         declare -l file_h="${file%.qc}.qh"
-        if [ ! -f "$file_h" ]; then echo "#pragma once" > "$file_h"; fi
+        if [[ ! -f "$file_h" ]]; then echo "#pragma once" > "$file_h"; fi
 
         include=$(basename "$file")
         include="${include%.qc}.qh"
         include="#include \"${include}\""
         startswith "$file" "$include"
     done
-    find "$base" -type f -name '*.qh' -print0 | sort -z | while read -r -d '' file; do
+    find "$base" -type f -name '*.qh' -a \! -name '_mod.qh' -print0 | sort -z | while read -r -d '' file; do
         echo "$file"
         startswith "$file" "#pragma once"
     done
@@ -33,3 +33,4 @@ function check() {
 check client
 check server
 check menu
+check common
index b115c196ecc75f4bb1ea0bcef52c15f962b9169c..9951ec2fd7837645d812c9bc56e449dde0a05cdb 100755 (executable)
@@ -12,15 +12,23 @@ QCCFLAGS=${QCCFLAGS}
 function qpp() {
     IN=$1
     OUT=$2
-    >&2 echo + ${CPP} ${@:3} ${IN}
+    case ${MODE} in
+        client) DEFS="-DGAMEQC -DCSQC"
+        ;;
+        menu) DEFS="-DMENUQC"
+        ;;
+        server) DEFS="-DGAMEQC -DSVQC"
+        ;;
+    esac
+    >&2 echo + ${CPP} ${@:3} ${DEFS} ${IN}
     set +e
     # additional information
-    ${CPP} ${@:3} \
+    ${CPP} ${@:3} ${DEFS} \
         -dM 1>${WORKDIR}/${MODE}_macros.txt \
         -H 2>${WORKDIR}/${MODE}_includes.txt \
         ${IN}
     # main step
-    ${CPP} ${@:3} -MMD -MP -MT ${OUT} -Wall -Wundef -Werror ${IN} -o ${WORKDIR}/${MODE}.txt
+    ${CPP} ${@:3} ${DEFS} -MMD -MP -MT ${OUT} -Wall -Wundef -Werror ${IN} -o ${WORKDIR}/${MODE}.txt
     err=$?
     set -e
     if [ ${err} -ne 0 ]; then return ${err}; fi
@@ -37,16 +45,6 @@ $(return >/dev/null 2>&1) || {
     MODE=$1
     OUT=$2
     IN=$3
-
-    case ${MODE} in
-        client) PROG=CSQC
-        ;;
-        menu) PROG=MENUQC
-        ;;
-        server) PROG=SVQC
-        ;;
-    esac
-
-    qpp ${IN} ${OUT} -I. ${QCCIDENT} ${QCCDEFS} -D${PROG} > ${WORKDIR}/${MODE}.qc
+    qpp ${IN} ${OUT} -I. ${QCCIDENT} ${QCCDEFS} > ${WORKDIR}/${MODE}.qc
     qcc ${QCCFLAGS} -o ${OUT} ../${WORKDIR}/${MODE}.qc
 }
index 5662337411f3767e852ec0a8b65226cc64826ca6..594c60ea29d8ae509dfd3d748987e74f0fe21b60 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
 cd ..
 
 function check() {
@@ -14,6 +14,7 @@ function check() {
 
 check lib
 check common
+check ecs
 check client
 check server
 check menu
index 98cdab9dd51b3036def6ce08a78f128abdc91d22..b598727223ee7321209bbc9360e9d32cd833a565 100644 (file)
@@ -418,7 +418,7 @@ sp_angle_shift                           = ignore   # ignore/add/remove/force
 sp_permit_cpp11_shift                    = false    # false/true
 
 # Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
-sp_before_sparen                         = add      # ignore/add/remove/force #force
+sp_before_sparen                         = force    # ignore/add/remove/force #force
 
 # Add or remove space inside if-condition '(' and ')'
 # NOTE: is 68 worse than ignore
@@ -431,10 +431,10 @@ sp_inside_sparen_close                   = ignore   # ignore/add/remove/force #f
 sp_inside_sparen_open                    = ignore   # ignore/add/remove/force #force
 
 # Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
-sp_after_sparen                          = add      # ignore/add/remove/force
+sp_after_sparen                          = force    # ignore/add/remove/force
 
 # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
-sp_sparen_brace                          = add      # ignore/add/remove/force
+sp_sparen_brace                          = force    # ignore/add/remove/force
 
 # Add or remove space between 'invariant' and '(' in the D language.
 sp_invariant_paren                       = ignore   # ignore/add/remove/force #ignore
@@ -630,11 +630,11 @@ sp_macro                                 = remove   # ignore/add/remove/force #f
 sp_macro_func                            = remove   # ignore/add/remove/force #force
 
 # Add or remove space between 'else' and '{' if on the same line
-sp_else_brace                            = add      # ignore/add/remove/force
+sp_else_brace                            = force    # ignore/add/remove/force
 
 # Add or remove space between '}' and 'else' if on the same line
 # WARNING: Code doesn't seem to use this feature - delete from the config?
-sp_brace_else                            = ignore   # ignore/add/remove/force
+sp_brace_else                            = force    # ignore/add/remove/force
 
 # Add or remove space between '}' and the name of a typedef on the same line
 sp_brace_typedef                         = add      # ignore/add/remove/force
@@ -1116,17 +1116,17 @@ nl_union_brace                           = ignore   # ignore/add/remove/force
 
 # Add or remove newline between 'if' and '{'
 # NOTE: is 136 worse than ignore
-nl_if_brace                              = add      # ignore/add/remove/force
+nl_if_brace                              = remove   # ignore/add/remove/force
 
 # Add or remove newline between '}' and 'else'
-nl_brace_else                            = add      # ignore/add/remove/force
+nl_brace_else                            = remove   # ignore/add/remove/force
 
 # Add or remove newline between 'else if' and '{'
 # If set to ignore, nl_if_brace is used instead
-nl_elseif_brace                          = add      # ignore/add/remove/force
+nl_elseif_brace                          = remove   # ignore/add/remove/force
 
 # Add or remove newline between 'else' and '{'
-nl_else_brace                            = add      # ignore/add/remove/force
+nl_else_brace                            = remove   # ignore/add/remove/force
 
 # Add or remove newline between 'else' and 'if'
 nl_else_if                               = remove   # ignore/add/remove/force
@@ -1137,7 +1137,7 @@ nl_brace_finally                         = ignore   # ignore/add/remove/force
 
 # Add or remove newline between 'finally' and '{'
 # WARNING: Code doesn't seem to use this feature - delete from the config?
-nl_finally_brace                         = ignore   # ignore/add/remove/force
+nl_finally_brace                         = remove   # ignore/add/remove/force
 
 # Add or remove newline between 'try' and '{'
 # WARNING: Code doesn't seem to use this feature - delete from the config?
@@ -1149,7 +1149,7 @@ nl_getset_brace                          = ignore   # ignore/add/remove/force
 
 # Add or remove newline between 'for' and '{'
 # NOTE: is 109 worse than ignore
-nl_for_brace                             = add      # ignore/add/remove/force
+nl_for_brace                             = remove   # ignore/add/remove/force
 
 # Add or remove newline between 'catch' and '{'
 # WARNING: Code doesn't seem to use this feature - delete from the config?
@@ -1161,7 +1161,7 @@ nl_brace_catch                           = ignore   # ignore/add/remove/force
 
 # Add or remove newline between 'while' and '{'
 # NOTE: is 22 worse than ignore
-nl_while_brace                           = add      # ignore/add/remove/force
+nl_while_brace                           = remove   # ignore/add/remove/force
 
 # Add or remove newline between 'scope (x)' and '{' (D)
 # WARNING: Code doesn't seem to use this feature - delete from the config?
@@ -1570,27 +1570,27 @@ nl_between_annotation                    = ignore   # ignore/add/remove/force
 #
 
 # Add or remove braces on single-line 'do' statement
-mod_full_brace_do                        = add      # ignore/add/remove/force
+mod_full_brace_do                        = force    # ignore/add/remove/force
 
 # Add or remove braces on single-line 'for' statement
 # NOTE: is 3 worse than ignore
-mod_full_brace_for                       = remove   # ignore/add/remove/force
+mod_full_brace_for                       = force    # ignore/add/remove/force
 
 # Add or remove braces on single-line function definitions. (Pawn)
 mod_full_brace_function                  = ignore   # ignore/add/remove/force #ignore
 
 # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
-mod_full_brace_if                        = add      # ignore/add/remove/force
+mod_full_brace_if                        = force    # ignore/add/remove/force
 
 # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
 # If any must be braced, they are all braced.  If all can be unbraced, then the braces are removed.
-mod_full_brace_if_chain                  = true     # false/true #force
+mod_full_brace_if_chain                  = false    # false/true #force
 
 # Don't remove braces around statements that span N newlines
 mod_full_brace_nl                        = 2        # number #force
 
 # Add or remove braces on single-line 'while' statement
-mod_full_brace_while                     = remove   # ignore/add/remove/force
+mod_full_brace_while                     = force    # ignore/add/remove/force
 
 # Add or remove braces on single-line 'using ()' statement
 # WARNING: Code doesn't seem to use this feature - delete from the config?
@@ -1608,7 +1608,7 @@ mod_pawn_semicolon                       = false    # false/true
 mod_full_paren_if_bool                   = false    # false/true
 
 # Whether to remove superfluous semicolons
-mod_remove_extra_semicolon               = false    # false/true
+mod_remove_extra_semicolon               = true     # false/true
 
 # If a function body exceeds the specified number of newlines and doesn't have a comment after
 # the close brace, a comment will be added.
index 822be77f722a64a13f84fb61c961322debbc854a..8df3e2a55883c619162445816cf69237b0210b91 100644 (file)
@@ -1,10 +1,7 @@
-fireball
+fireball_plasma
 {
-       {
-               map textures/fireball
-               tcgen environment
-       }
-       {
-               map $lightmap
-       }
+ {
+       map textures/fireball_plasma.tga
+       tcMod scroll 0.03 0.001
+ }
 }
index ab2d0c61125909fdebcd6abc316ca84529de83d8..b6ee3cd8ae597e31e38b5141b03724a036edb188 100644 (file)
@@ -423,3 +423,25 @@ g_uzi_luma
                blendfunc blend
        }
 }
+
+g_ok_hmg_luma
+{
+       deformVertexes autosprite
+       cull none
+       nopicmip
+       {
+               map models/weapons/g_ok_hmg_luma
+               blendfunc blend
+       }
+}
+
+g_ok_rl_luma
+{
+       deformVertexes autosprite
+       cull none
+       nopicmip
+       {
+               map models/weapons/g_ok_rl_luma
+               blendfunc blend
+       }
+}
index 92e9e5affd847cbdc578d8d41239ff7d3ba9c4d0..3a7d11eafe739237f63844571cb895614c38c7eb 100644 (file)
@@ -300,6 +300,32 @@ g_tuba_simple // tuba
        }
 }
 
+g_ok_hmg_simple // heavy machinegun
+{
+       deformVertexes autosprite
+       cull none
+       nopicmip
+
+       {
+               map models/weapons/g_ok_hmg_simple
+               blendfunc blend
+
+       }
+}
+
+g_ok_rl_simple // rocket propelled chainsaw
+{
+       deformVertexes autosprite
+       cull none
+       nopicmip
+
+       {
+               map models/weapons/g_ok_rl_simple
+               blendfunc blend
+
+       }
+}
+
 ////////// ARMOR + HEALTH ITEMS //////////
 
 ///// ARMOR /////
index 53b47a192cc15ed112834ccc703459006e1c0260..c631e8c880dc98a903d13d2683e7ef0dacd54cf6 100644 (file)
Binary files a/textures/fireball.tga and b/textures/fireball.tga differ
index 8cd8b25bdc50041f5a84b7519551802a32a5c3db..eaf50d0a2e63d8f4a51c8ff2f26fe4d33d158d1d 100644 (file)
Binary files a/textures/fireball_gloss.tga and b/textures/fireball_gloss.tga differ
index c949f92a47d69059cfdadd94a810b87785abadb0..358784d8fd784613e6766861f34404b496b92150 100644 (file)
Binary files a/textures/fireball_glow.tga and b/textures/fireball_glow.tga differ
diff --git a/textures/fireball_norm.tga b/textures/fireball_norm.tga
new file mode 100644 (file)
index 0000000..b0bd3f9
Binary files /dev/null and b/textures/fireball_norm.tga differ
diff --git a/textures/fireball_pants.tga b/textures/fireball_pants.tga
new file mode 100644 (file)
index 0000000..2fbc09a
Binary files /dev/null and b/textures/fireball_pants.tga differ
diff --git a/textures/fireball_shirt.tga b/textures/fireball_shirt.tga
new file mode 100644 (file)
index 0000000..3d93916
Binary files /dev/null and b/textures/fireball_shirt.tga differ
diff --git a/textures/items/a_rocket_bottom.jpg b/textures/items/a_rocket_bottom.jpg
new file mode 100644 (file)
index 0000000..b2db595
Binary files /dev/null and b/textures/items/a_rocket_bottom.jpg differ