]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/infomessages_panel_update' into 'master'
authorMario <zacjardine@y7mail.com>
Wed, 3 Aug 2016 00:24:08 +0000 (00:24 +0000)
committerMario <zacjardine@y7mail.com>
Wed, 3 Aug 2016 00:24:08 +0000 (00:24 +0000)
Infomessages panel update; Display command names everywhere in the HUD instead of the commands theirselves

* Infomessages panel:
  * Allow message wrapping
  * Reduce number of messages displayed when spectating / observing by showing 3 less important messages in rotation
  * Make command names translatable

* Display command names everywhere in the HUD instead of the commands theirselves

See merge request !342

148 files changed:
.gitlab-ci.yml
bal-wep-mario.cfg
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
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
qcsrc/client/commands/cl_cmd.qc
qcsrc/client/hud/panel/modicons.qc
qcsrc/client/hud/panel/radar.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/teamradar.qc
qcsrc/client/view.qc
qcsrc/client/wall.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/debug.qh
qcsrc/common/effects/qc/casings.qc
qcsrc/common/effects/qc/gibs.qc
qcsrc/common/effects/qc/modeleffects.qc
qcsrc/common/effects/qc/rubble.qh
qcsrc/common/gamemodes/gamemode/nexball/nexball.qc
qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc
qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/damagetext/damagetext.qc
qcsrc/common/mutators/mutator/itemstime.qc
qcsrc/common/mutators/mutator/nades/net.qc
qcsrc/common/mutators/mutator/overkill/hmg.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/physics/movetypes/movetypes.qh
qcsrc/common/physics/movetypes/toss.qc
qcsrc/common/physics/movetypes/walk.qc
qcsrc/common/physics/player.qc
qcsrc/common/physics/player.qh
qcsrc/common/stats.qh
qcsrc/common/t_items.qc
qcsrc/common/triggers/func/conveyor.qc
qcsrc/common/triggers/func/plat.qc
qcsrc/common/triggers/func/pointparticles.qc
qcsrc/common/triggers/func/rainsnow.qc
qcsrc/common/triggers/func/train.qc
qcsrc/common/triggers/misc/laser.qc
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/turrets/cl_turrets.qc
qcsrc/common/turrets/turret/tesla_weapon.qc
qcsrc/common/vehicles/cl_vehicles.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc
qcsrc/common/vehicles/vehicle/raptor_weapons.qc
qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/electro.qc
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/machinegun.qc
qcsrc/common/weapons/weapon/minelayer.qc
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/dpdefs/doc.md
qcsrc/dpdefs/menudefs.qh
qcsrc/dpdefs/post.qh
qcsrc/dpdefs/pre.qh
qcsrc/ecs/README.md
qcsrc/ecs/_lib.inc
qcsrc/ecs/_lib.qh [new file with mode: 0644]
qcsrc/ecs/components/input.qh
qcsrc/ecs/components/physics.qh
qcsrc/ecs/events/_mod.inc
qcsrc/ecs/events/_mod.qh
qcsrc/ecs/events/physics.qc [new file with mode: 0644]
qcsrc/ecs/events/physics.qh [new file with mode: 0644]
qcsrc/ecs/systems/_mod.inc
qcsrc/ecs/systems/_mod.qh
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
qcsrc/ecs/systems/physics.qh
qcsrc/ecs/systems/sv_physics.qc [new file with mode: 0644]
qcsrc/lib/_all.inc
qcsrc/lib/intrusivelist.qh [new file with mode: 0644]
qcsrc/lib/iter.qh
qcsrc/lib/log.qh
qcsrc/lib/oo.qh
qcsrc/lib/self.qh
qcsrc/lib/stats.qh
qcsrc/lib/vector.qh
qcsrc/menu/menu.qc
qcsrc/server/_all.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/bot.qc
qcsrc/server/bot/bot.qh
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/bot/navigation.qc
qcsrc/server/bot/navigation.qh
qcsrc/server/bot/scripting.qc
qcsrc/server/bot/waypoints.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.qc
qcsrc/server/command/common.qc
qcsrc/server/defs.qh
qcsrc/server/g_subs.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/mutator/gamemode_assault.qc
qcsrc/server/mutators/mutator/gamemode_ca.qc
qcsrc/server/mutators/mutator/gamemode_ctf.qc
qcsrc/server/mutators/mutator/gamemode_domination.qc
qcsrc/server/mutators/mutator/gamemode_freezetag.qc
qcsrc/server/mutators/mutator/gamemode_invasion.qc
qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
qcsrc/server/mutators/mutator/gamemode_race.qc
qcsrc/server/mutators/mutator/gamemode_tdm.qc
qcsrc/server/pathlib/main.qc
qcsrc/server/pathlib/path_waypoint.qc
qcsrc/server/pathlib/pathlib.qh
qcsrc/server/scores.qc
qcsrc/server/scores_rules.qc
qcsrc/server/scores_rules.qh
qcsrc/server/steerlib.qc
qcsrc/server/sv_main.qc
qcsrc/server/sys-post.qh
qcsrc/server/sys-pre.qh
qcsrc/server/teamplay.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qc
qcsrc/uncrustify.cfg

index 7909c3286a6dcbd28402519f993afc9a33a59f16..db275cd39edfd04ef89f812477015dd701d2dfe5 100644 (file)
@@ -24,7 +24,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=9b2513f29762de886296f998ac8725c9
+    - EXPECT=56bf79831a0a35ab6a3a420e26b09945
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index 9bfca55f37ed586b8ee44cc5e1f00fd1baa441e9..5979b78b84947acfbd6ca3790b0bb7e76ec7f4c1 100644 (file)
@@ -2,7 +2,7 @@
 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 17
+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
@@ -34,8 +34,8 @@ 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 8
-set g_balance_shotgun_primary_damage 6
+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
@@ -43,7 +43,7 @@ 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
+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
@@ -61,7 +61,7 @@ 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_weaponstart 0
 set g_balance_shotgun_weaponstartoverride -1
 set g_balance_shotgun_weaponthrowable 1
 // }}}
@@ -92,7 +92,7 @@ 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_weaponreplace "arc"
 set g_balance_machinegun_weaponstart 0
 set g_balance_machinegun_weaponstartoverride -1
 set g_balance_machinegun_weaponthrowable 1
@@ -102,7 +102,7 @@ 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 40
+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
@@ -192,8 +192,8 @@ 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.3
-set g_balance_electro_primary_midaircombo_radius 150
+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
@@ -218,8 +218,9 @@ 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_touchexplode 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 ""
@@ -232,7 +233,7 @@ 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 8
+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
@@ -242,7 +243,7 @@ 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 1
+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
@@ -259,9 +260,9 @@ 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_damage 35
 set g_balance_crylink_secondary_edgedamage 5
-set g_balance_crylink_secondary_force -200
+set g_balance_crylink_secondary_force -300
 set g_balance_crylink_secondary_joindelay 0
 set g_balance_crylink_secondary_joinexplode 0
 set g_balance_crylink_secondary_joinexplode_damage 0
@@ -276,7 +277,7 @@ 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_shots 1
 set g_balance_crylink_secondary_speed 3000
 set g_balance_crylink_secondary_spread 0.01
 set g_balance_crylink_secondary_spreadtype 1
@@ -288,11 +289,11 @@ set g_balance_crylink_weaponstartoverride -1
 set g_balance_crylink_weaponthrowable 1
 // }}}
 // {{{ #8: Vortex
-set g_balance_vortex_charge 1
+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 50
+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
@@ -302,12 +303,12 @@ 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 80
+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 450
+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
@@ -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.2
-set g_balance_vortex_switchdelay_raise 0.2
+set g_balance_vortex_switchdelay_drop 0.25
+set g_balance_vortex_switchdelay_raise 0.25
 set g_balance_vortex_weaponreplace ""
 set g_balance_vortex_weaponstart 0
 set g_balance_vortex_weaponstartoverride -1
@@ -360,14 +361,14 @@ 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 1
+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.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 ""
@@ -382,7 +383,7 @@ 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 450
+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
@@ -390,21 +391,21 @@ 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 150
+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 400
+set g_balance_devastator_remote_force 450
 set g_balance_devastator_remote_jump_damage 70
 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
 set g_balance_devastator_remote_jump_velocity_z_min 400
-set g_balance_devastator_remote_radius 150
-set g_balance_devastator_speed 1200
-set g_balance_devastator_speedaccel 1200
+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
@@ -433,6 +434,7 @@ 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
@@ -674,23 +676,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
@@ -719,37 +721,50 @@ 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_weaponstart 1
 set g_balance_shockwave_weaponstartoverride -1
 set g_balance_shockwave_weaponthrowable 0
 // }}}
 // {{{ #20: Arc
-set g_balance_arc_beam_ammo 4
-set g_balance_arc_beam_animtime 0.2
+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 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_healing_amax 100
+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 1
+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.5
+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
index 8a47e40d50dc73ebf56bf40bbf450c8f6c2afbe0..d4b47594d7061462769246cb79af1a180c4b84d1 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
index d0cfa361d269c1aae66c89cefc2e3e660f65dd24..d7463338aa0f2746550ab2c40468fa8838e89e7a 100644 (file)
@@ -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
index af2234f0d6c898c02d110d98273a46019bdb7c9d..fe3b9ed8711e62bb64ba564310ae573008677f6c 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
index 931a63c6a2264d068110f90817f63be68fc5ac64..c9bdb2aa367e5b11629c5778825d3c895e4f0539 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
index 779c7129c68009e6493df50b05af29aef33c6581..64d79e150aee00e1e9abfbb90d420edede443993 100644 (file)
@@ -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 1
 set g_balance_electro_switchdelay_drop 0.2
 set g_balance_electro_switchdelay_raise 0.2
index 779c7129c68009e6493df50b05af29aef33c6581..64d79e150aee00e1e9abfbb90d420edede443993 100644 (file)
@@ -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 1
 set g_balance_electro_switchdelay_drop 0.2
 set g_balance_electro_switchdelay_raise 0.2
index 9995e5ec5c8f47c8792823590560569e3fe40331..022d62eaa0ff475cb8ec1d6d73f3c5734fe8d749 100644 (file)
@@ -2,8 +2,8 @@ g_mod_balance Mario
 
 // {{{ starting gear
 set g_balance_health_start 100
-set g_balance_armor_start 50
-set g_start_ammo_shells 20
+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
@@ -44,7 +44,7 @@ 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 20
+set g_pickup_shells 15
 set g_pickup_shells_weapon 15
 set g_pickup_shells_max 60
 set g_pickup_nails 80
@@ -138,7 +138,7 @@ set g_balance_fuel_limit 999
 // }}}
 
 // {{{ misc
-set g_balance_selfdamagepercent 0.5
+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"
@@ -175,8 +175,8 @@ set g_projectiles_spread_style 7
 // 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.07
-set g_balance_falldamage_maxdamage 250
+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
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
index dce058c816da9f2c4abbfae97c4bad68d34931dd..100bebf5fa697bd5c0cd3d57acf11ee0dd6fa147 100644 (file)
@@ -183,6 +183,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;
                }
index b869f96194534954de800f66802e31917832e309..5901a16326ca40047480f5e71d67ae7d783f7cfd 100644 (file)
@@ -108,6 +108,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;
@@ -122,6 +123,8 @@ void HUD_Mod_CTF(vector pos, vector mySize)
        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;
@@ -137,9 +140,9 @@ void HUD_Mod_CTF(vector pos, vector mySize)
        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?
        }
@@ -201,18 +204,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;
@@ -267,6 +276,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) \
index ad0fa28f15765d9d4e2c2da5050849aed8894cdc..e935d0cb2b27eba1b395593ca192b412afc13c2e 100644 (file)
@@ -351,9 +351,9 @@ 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 || !(serverflags & SERVERFLAG_TEAMPLAY) )
index 5ce3eada7614a5d3b186747105e09e82862db3ea..abe11df51d35a23825d8386be38003e75d35581e 100644 (file)
@@ -701,6 +701,7 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        this.drawmask = MASK_NORMAL;
                        //this.move_movetype = MOVETYPE_NOCLIP;
                        //this.draw = Spawn_Draw;
+                       IL_PUSH(g_drawables, this);
                }*/
                if(autocvar_cl_spawn_point_particles)
                {
@@ -718,6 +719,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;
index 1265331ae17b3d6c9430f9bf62be86e1f428e69a..ffb853d2f8379d93d47bac930923b1813defb23e 100644 (file)
@@ -85,11 +85,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;
index a8708f2460f809a579f96db0428fbc1522432c61..ab14cd3bf38d04a89d48ead845339bcbf582e271 100644 (file)
@@ -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 200bcfde715373d44105d516894ad72745de8778..1458efe15f13563e9e869cb653c9e5c80791318c 100644 (file)
@@ -363,6 +363,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;
 }
 
@@ -860,7 +861,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;
@@ -1871,10 +1872,7 @@ void CSQC_UpdateView(entity this, float w, float h)
           mousepos = mousepos*0.5 + getmousepos();
         */
 
-       FOREACH_ENTITY_FLOAT(pure_data, false,
-       {
-               if(it.draw) { it.draw(it); }
-       });
+       IL_EACH(g_drawables, it.draw, it.draw(it));
 
        addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
        renderscene();
@@ -2204,10 +2202,7 @@ void CSQC_UpdateView(entity this, float w, float h)
          } else */
 
        // draw 2D entities
-       FOREACH_ENTITY_FLOAT(pure_data, false,
-       {
-               if(it.draw2d) { it.draw2d(it); }
-       });
+       IL_EACH(g_drawables_2d, it.draw2d, it.draw2d(it));
        Draw_ShowNames_All();
        Debug_Draw();
 
@@ -2264,6 +2259,8 @@ void CSQC_UpdateView(entity this, 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..01a86bb1ee44d2490402cac124a41b35c1229715 100644 (file)
@@ -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 56a172b10b193ae2a326b5c722d5f161091bbb92..4015828e555fc6a41db87329254197072e3d9d58 100644 (file)
@@ -338,8 +338,8 @@ 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.mins = '-4 -4 -4';
+                               this.maxs = '4 4 4';
                                this.move_movetype = MOVETYPE_BOUNCE;
                                settouch(this, func_null);
                                this.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
@@ -477,6 +477,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 83cbc302dcd4f745e79ba58c550658fca92313d4..052e00f07c4df4e0548c5c3fdba01b02bceff18c 100644 (file)
@@ -309,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 30543adee55b35c77467a233dc0dcf481718bd22..a4ade38366e709e8a14e90d086fb60258c4ceeca 100644 (file)
@@ -151,6 +151,7 @@ NET_HANDLE(casings, bool isNew)
     casing.drawmask = MASK_NORMAL;
 
     casing.draw = Casing_Draw;
+    if (isNew) IL_PUSH(g_drawables, casing);
     casing.velocity = casing.velocity + 2 * prandomvec();
     casing.avelocity = '0 250 0' + 100 * prandomvec();
     casing.move_movetype = MOVETYPE_BOUNCE;
index aaecf18f91a5bd0ffade1db74bff71c23d4fcbbb..78f1a27cc6c0a0eba824fa7899e1a099d117b3e3 100644 (file)
@@ -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
index f577762906b6737427a9f0edd2289123f78729ac..28b8bb496bc6438f81367081ec6f28e54be9cc9b 100644 (file)
@@ -156,6 +156,7 @@ 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
        return true;
index 7848b7b1f3a31d34d6069c9920ed81359f608952..1665e4ccef38e8c37db1d984dacea596214ff6b2 100644 (file)
@@ -8,34 +8,25 @@ class(Rubble).float creationtime;
 
 void RubbleLimit(string cname, float limit, void(entity) deleteproc)
 {
-       entity e;
-       entity oldest;
-       float c;
-       float oldesttime;
-
        // 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)
+               FOREACH_ENTITY_CLASS(cname, true,
                {
-                       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;
index 21d2d858b08b038a9f2d0d250493bb889f47601b..35fe31c2bd6ea49295a0697d68cf0e7d55c85cc0 100644 (file)
@@ -72,7 +72,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);
@@ -376,7 +376,7 @@ void GoalTouch(entity this, entity toucher)
        EXACTTRIGGER_TOUCH(this, toucher);
 
 
-       if(nb_teams == 2)
+       if(NumTeams(nb_teams) == 2)
                otherteam = OtherTeam(ball.team);
        else
                otherteam = 0;
@@ -395,7 +395,7 @@ void GoalTouch(entity this, entity toucher)
        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 +421,7 @@ void GoalTouch(entity this, entity toucher)
 
        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);
@@ -466,7 +466,7 @@ void nb_spawnteam(string teamname, float teamcolor)
        e.netname = teamname;
        e.cnt = teamcolor;
        e.team = e.cnt + 1;
-       nb_teams += 1;
+       //nb_teams += 1;
 }
 
 void nb_spawnteams()
@@ -481,6 +481,7 @@ void nb_spawnteams()
                        if(!t_red)
                        {
                                nb_spawnteam("Red", e.team-1)   ;
+                               nb_teams |= BIT(0);
                                t_red = true;
                        }
                        break;
@@ -489,6 +490,7 @@ void nb_spawnteams()
                        {
                                nb_spawnteam("Blue", e.team-1)  ;
                                t_blue = true;
+                               nb_teams |= BIT(1);
                        }
                        break;
                case NUM_TEAM_3:
@@ -496,6 +498,7 @@ void nb_spawnteams()
                        {
                                nb_spawnteam("Yellow", e.team-1);
                                t_yellow = true;
+                               nb_teams |= BIT(2);
                        }
                        break;
                case NUM_TEAM_4:
@@ -503,6 +506,7 @@ void nb_spawnteams()
                        {
                                nb_spawnteam("Pink", e.team-1)  ;
                                t_pink = true;
+                               nb_teams |= BIT(3);
                        }
                        break;
                }
index a22815b1682e18eea42f40204b988990d37441a0..0135f467ed0eb5fb84e9180048ce2df87bb67077 100644 (file)
@@ -39,6 +39,7 @@ void ons_generator_ray_spawn(vector org)
        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)
index 6242414ae0b275e8b1ded5dcf01880e6c4a32bee..89c5eef51dc2edb0897676f0e7de8e1a3289e7f7 100644 (file)
@@ -358,13 +358,11 @@ void onslaught_updatelinks()
                }
                ons_ControlPoint_UpdateSprite(l);
        }
-       l = findchain(classname, "ons_captureshield");
-       while(l)
+       FOREACH_ENTITY_CLASS("ons_captureshield", true,
        {
-               l.team = l.enemy.team;
-               l.colormap = l.enemy.colormap;
-               l = l.chain;
-       }
+               it.team = it.enemy.team;
+               it.colormap = it.enemy.colormap;
+       });
 }
 
 
@@ -1289,8 +1287,6 @@ void Onslaught_RoundStart()
 
 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?
@@ -1298,7 +1294,7 @@ void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector
                needarmor = true;
 
        // Needs weapons?
-       c = 0;
+       int c = 0;
        FOREACH(Weapons, it != WEP_Null, {
                if(this.weapons & (it.m_wepset))
                if(++c >= 4)
@@ -1315,20 +1311,18 @@ void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector
        LOG_DEBUG(strcat(this.netname, " needs armor ", ftos(needarmor) , "\n"));
 
        // See what is around
-       head = findchainfloat(bot_pickup, true);
-       while (head)
+       FOREACH_ENTITY_FLOAT(bot_pickup, true,
        {
                // gather health and armor only
-               if (head.solid)
-               if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
-               if (vdist(head.origin - org, <, sradius))
+               if (it.solid)
+               if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) )
+               if (vdist(it.origin - org, <, sradius))
                {
-                       t = head.bot_pickupevalfunc(this, head);
+                       int t = it.bot_pickupevalfunc(this, it);
                        if (t > 0)
-                               navigation_routerating(this, head, t * ratingscale, 500);
+                               navigation_routerating(this, it, t * ratingscale, 500);
                }
-               head = head.chain;
-       }
+       });
 }
 
 void havocbot_role_ons_setrole(entity this, int role)
@@ -1597,26 +1591,22 @@ void havocbot_ons_reset_role(entity this)
  */
 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)
+       entity closest_target = NULL;
+       FOREACH_ENTITY_CLASS("onslaught_controlpoint", true,
        {
-               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(it, this))
+               if(it.iscaptured)
+               if(max_dist <= 0 || vdist(it.origin - pos, <=, max_dist))
+               if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
+                       closest_target = it;
+       });
+       FOREACH_ENTITY_CLASS("onslaught_generator", true,
        {
-               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;
-       }
+               if(SAME_TEAM(it, this))
+               if(max_dist <= 0 || vdist(it.origin - pos, <, max_dist))
+               if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL)
+                       closest_target = it;
+       });
 
        return closest_target;
 }
@@ -1628,45 +1618,39 @@ entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist)
  */
 entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist)
 {
-       entity tmp_entity, closest_target = NULL;
+       entity closest_target = NULL;
        vector delta;
        float smallest_distance = 0, distance;
 
-       tmp_entity = findchain(classname, "onslaught_controlpoint");
-       while(tmp_entity)
+       FOREACH_ENTITY_CLASS("onslaught_controlpoint", true,
        {
-               delta = tmp_entity.origin - pos;
+               delta = it.origin - pos;
                delta_z = 0;
                distance = vlen(delta);
 
-               if(SAME_TEAM(tmp_entity, this))
-               if(tmp_entity.iscaptured)
+               if(SAME_TEAM(it, this))
+               if(it.iscaptured)
                if(max_dist <= 0 || distance <= max_dist)
                if(closest_target == NULL || distance <= smallest_distance )
                {
-                       closest_target = tmp_entity;
+                       closest_target = it;
                        smallest_distance = distance;
                }
-
-               tmp_entity = tmp_entity.chain;
-       }
-       tmp_entity = findchain(classname, "onslaught_generator");
-       while(tmp_entity)
+       });
+       FOREACH_ENTITY_CLASS("onslaught_generator", true,
        {
-               delta = tmp_entity.origin - pos;
+               delta = it.origin - pos;
                delta_z = 0;
                distance = vlen(delta);
 
-               if(SAME_TEAM(tmp_entity, this))
+               if(SAME_TEAM(it, this))
                if(max_dist <= 0 || distance <= max_dist)
                if(closest_target == NULL || distance <= smallest_distance )
                {
-                       closest_target = tmp_entity;
+                       closest_target = it;
                        smallest_distance = distance;
                }
-
-               tmp_entity = tmp_entity.chain;
-       }
+       });
 
        return closest_target;
 }
@@ -1675,23 +1659,18 @@ entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist)
  */
 int ons_Count_SelfControlPoints(entity this)
 {
-       entity tmp_entity;
-       tmp_entity = findchain(classname, "onslaught_controlpoint");
        int n = 0;
-       while(tmp_entity)
+       FOREACH_ENTITY_CLASS("onslaught_controlpoint", true,
        {
-               if(SAME_TEAM(tmp_entity, this))
-               if(tmp_entity.iscaptured)
+               if(SAME_TEAM(it, this))
+               if(it.iscaptured)
                        n++;
-               tmp_entity = tmp_entity.chain;
-       }
-       tmp_entity = findchain(classname, "onslaught_generator");
-       while(tmp_entity)
+       });
+       FOREACH_ENTITY_CLASS("onslaught_generator", true,
        {
-               if(SAME_TEAM(tmp_entity, this))
+               if(SAME_TEAM(it, this))
                        n++;
-               tmp_entity = tmp_entity.chain;
-       }
+       });
        return n;
 }
 
@@ -2250,7 +2229,12 @@ spawnfunc(onslaught_generator)
 void ons_ScoreRules()
 {
        CheckAllowedTeams(NULL);
-       ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, 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, 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);
index ae7dc51570700f2124e6ba7f6b3cbd8daf3fba2e..60c2777ca7bd12b5774f953999a0dad99b729687 100644 (file)
@@ -1290,6 +1290,9 @@ bool Monster_Spawn(entity this, int mon_id)
 
        if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
 
+       if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
+               IL_PUSH(g_monsters, this);
+
        if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
 
        if(!this.monster_skill)
index 4694bcbf7305b7c6ba3674196e865fbefb0810ef..8de4f2b8549d09d9a99f12903a3870b9629fee04 100644 (file)
@@ -86,6 +86,7 @@ CLASS(DamageText, Object)
         this.m_group = _group;
         this.m_friendlyfire = _friendlyfire;
         DamageText_update(this, _origin, _health, _armor, _deathtype);
+               IL_PUSH(g_drawables_2d, this);
     }
 ENDCLASS(DamageText)
 #endif
index a504c9b1e950053e82cfd79e0f5ef0e87d4c9c5b..c6afa33161c954d70f75fda41e2841afa4f20273 100644 (file)
@@ -130,7 +130,7 @@ void Item_ItemsTime_SetTime(entity e, float t)
 
 void Item_ItemsTime_SetTimesForAllPlayers()
 {
-    FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it)), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it)));
+    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)
@@ -180,14 +180,14 @@ MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST)
        {
                // client became player on connection skipping putObserverInServer step
                if (IS_REAL_CLIENT(player))
-               if (warmup_stage)
+               if (warmup_stage || autocvar_sv_itemstime == 2)
                        Item_ItemsTime_SetTimesForPlayer(player);
        }
 }
 
 MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn)
 {
-    if (warmup_stage) return;
+    if (warmup_stage || autocvar_sv_itemstime == 2) return;
     entity player = M_ARGV(0, entity);
 
     Item_ItemsTime_ResetTimesForPlayer(player);
@@ -270,7 +270,7 @@ void HUD_ItemsTime()
     {
         if (!(
             (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0)
-        ||     (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage))
+        ||     (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2))
             )) { return; }
     }
     else
index cbb8e4abfc2fe62a7bf18b045c2b32c1bd22b95b..9488e02a527a098620f9848cd971e412a205f92e 100644 (file)
@@ -29,6 +29,7 @@ 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.move_movetype = MOVETYPE_NONE;
        e.solid = SOLID_NOT;
index b8fd7430988464796cd9e9f7aa8af43dfd11b2c9..2000ffd5dd758ccdeb05beb2ce6ee099662e57bf 100644 (file)
@@ -90,7 +90,10 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone
        W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
 
        if (autocvar_g_casings >= 2) // casing code
+       {
+               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);
+       }
 
        int slot = weaponslot(weaponentity);
        ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(actor);
index 56c4c5b5daa313b9914ea88918a1847e4b88fb14..070379b4ae7b00caf0c674f0517a6eb572c8585b 100644 (file)
@@ -100,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;
 }
 
@@ -114,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();
@@ -123,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;
@@ -482,7 +486,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;
@@ -935,7 +939,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)
index fa9f4fbdf6571f0cdfbfda2187eddeeaafe0752d..26e4058d24975ba686619ca785271826065e6a0d 100644 (file)
@@ -108,7 +108,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);
 
index ccdf2e70397b483f0e493bce1eb4933b38c5ca38..090144dd16069f16573489d72f8a8cae53c7cdb0 100644 (file)
@@ -122,7 +122,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma
                                }
 
                                SET_ONGROUND(this);
-                               this.move_groundentity = trace_ent;
+                               this.groundentity = trace_ent;
                        }
                }
                else if(stepheight)
@@ -502,7 +502,7 @@ float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid)  //
        this.origin = trace_endpos;
 
        if(trace_fraction < 1)
-               if(this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || (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;
index cd0f09c213b9fe4c4d5cd86d6c98383cc32859d3..35a73d3331755d5f24b04ef175ce3f3208bf7420 100644 (file)
 .float bouncefactor;
 #endif
 
-#ifdef SVQC
-.bool move_qcphysics;
-#endif
-
 void set_movetype(entity this, int mt);
 
 .float move_movetype;
@@ -33,7 +29,7 @@ void set_movetype(entity this, int mt);
 .entity aiment;
 .vector punchangle;
 
-.entity move_groundentity;  // FIXME add move_groundnetworkentity?
+.entity groundentity;  // FIXME add move_groundnetworkentity?
 .float move_suspendedinair;
 .float move_didgravity;
 
@@ -79,6 +75,9 @@ 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 MOVETYPE_FAKEPUSH         = 13;
index db3ff72daa84551704c9930ed0955e9f9a03e13d..4821e34f6ac8929ae610ddc00ab35cd188223ce6 100644 (file)
@@ -8,13 +8,13 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
                {
                        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;
                }
        }
@@ -78,7 +78,7 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
                        if (trace_plane_normal.z > 0.7 && d < bstop && d > -bstop)
                        {
                                SET_ONGROUND(this);
-                               this.move_groundentity = trace_ent;
+                               this.groundentity = trace_ent;
                                this.velocity = '0 0 0';
                                this.avelocity = '0 0 0';
                        }
@@ -93,7 +93,7 @@ void _Movetype_Physics_Toss(entity this, float dt)  // SV_Physics_Toss
                        if (trace_plane_normal.z > 0.7)
                        {
                                SET_ONGROUND(this);
-                               this.move_groundentity = trace_ent;
+                               this.groundentity = trace_ent;
                                if (trace_ent.solid == SOLID_BSP)
                                        this.move_suspendedinair = true;
                                this.velocity = '0 0 0';
index 45b1dc39ccbe4c18a978a617bc7b90120df357b1..bf58ca5824419938d4b7ff5dd598273507860516 100644 (file)
@@ -61,7 +61,7 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
        vector originalvelocity = this.velocity;
        // originalmove_clip = clip;
        int originalflags = this.flags;
-       entity originalmove_groundentity = this.move_groundentity;
+       entity originalmove_groundentity = this.groundentity;
 
        // if move didn't block on a step, return
        if (clip & 2)
@@ -124,7 +124,7 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                        this.velocity = originalvelocity;
                        // clip = originalmove_clip;
                        this.flags = originalflags;
-                       this.move_groundentity = originalmove_groundentity;
+                       this.groundentity = originalmove_groundentity;
                        // now try to unstick if needed
                        // clip = SV_TryUnstick (ent, oldvel);
                        return;
@@ -168,7 +168,7 @@ void _Movetype_Physics_Walk(entity this, float dt)  // SV_WalkMove
                this.origin = originalorigin;
                this.velocity = originalvelocity;
                this.flags = originalflags;
-               this.move_groundentity = originalmove_groundentity;
+               this.groundentity = originalmove_groundentity;
        }
 
        _Movetype_CheckVelocity(this);
index 991573ff31cbeec22378c7474b51ba47bf4ed15f..74a44252fe8bd6d7fc8a11a177ca7ad9c1dfd8ee 100644 (file)
@@ -134,7 +134,9 @@ void PM_ClientMovement_UpdateStatus(entity this)
                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)
+       if((activeweapon.spawnflags & WEP_TYPE_MELEE_PRI) && viewmodel.animstate_startframe == viewmodel.anim_fire1_x && time < viewmodel.weapon_nextthink)
+               do_crouch = false;
+       if((activeweapon.spawnflags & WEP_TYPE_MELEE_SEC) && viewmodel.animstate_startframe == viewmodel.anim_fire2_x && time < viewmodel.weapon_nextthink)
                do_crouch = false;
 
        if (do_crouch)
@@ -152,8 +154,8 @@ void PM_ClientMovement_UpdateStatus(entity this)
                }
        }
 
-       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
 }
 
@@ -432,7 +434,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
                }
        }
@@ -679,182 +681,7 @@ void PM_check_blocked(entity this)
 #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))
-#elif defined(CSQC)
-       if(pmove_waterjumptime <= 0)
-#endif
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-}
-
-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;
-                               UNSET_ONGROUND(this);
-                               SET_JUMP_HELD(this);
-                       }
-               }
-       }
-       makevectors(this.v_angle);
-       float wishdown = this.movement.z;
-       if(PHYS_INPUT_BUTTON_CROUCH(this))
-               wishdown = -PHYS_MAXSPEED(this);
-       //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' * wishdown;
-       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 (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;
-               }
-
-               // 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
-                               if(this.waterlevel >= WATERLEVEL_SUBMERGED)
-                                       this.velocity_z = PHYS_MAXSPEED(this);
-                               else
-                                       this.velocity_z = 200;
-#if 0
-                               else
-                                       this.velocity_z = 100;
-                       }
-#endif
-               }
-       }
-       if(this.viewloc)
-       {
-               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;
-               }
-       }
-       else
-       {
-               // water acceleration
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-       }
-}
-
 .vector oldmovement;
-void PM_ladder(entity this, float maxspd_mod)
-{
-       // 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);
-#ifdef SVQC
-       if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
-       if(pmove_waterjumptime <= 0)
-#endif
-               // water acceleration
-               PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
-}
 
 void PM_jetpack(entity this, float maxspd_mod)
 {
@@ -964,146 +791,6 @@ void PM_jetpack(entity this, float maxspd_mod)
        }
 }
 
-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;
-       }
-}
-
-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);
-       }
-}
-
 // used for calculating airshots
 bool IsFlying(entity this)
 {
@@ -1117,267 +804,15 @@ 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);
-#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;
-#elif defined(CSQC)
-       if(hud != HUD_NORMAL)
-               return; // no vehicle prediction (yet)
-#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';
-                       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;
-               }
-       }
-#endif
-
-#ifdef SVQC
-       if (this.move_movetype == MOVETYPE_NONE)
-               return;
-
-       // when we get here, disableclientprediction cannot be 2
-       this.disableclientprediction = (this.move_qcphysics) ? -1 : 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 (this.waterlevel == WATERLEVEL_NONE
-               #ifdef CSQC
-                       || pmove_waterjumptime <= 0
-               #elif defined(SVQC)
-                       || time > PHYS_TELEPORT_TIME(this)
-               #endif
-                       )
-               {
-                       this.flags &= ~FL_WATERJUMP;
-               #ifdef CSQC
-                       pmove_waterjumptime = 0;
-               #elif defined(CSQC)
-                       PHYS_TELEPORT_TIME(this) = 0;
-               #endif
-               }
-       }
-
-       else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
-               { }
-
-#ifdef SVQC
-       else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_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(entity this)
 #elif defined(CSQC)
 void CSQC_ClientMovement_PlayerMove_Frame(entity this)
 #endif
 {
-       PM_Main(this);
+       sys_phys_update(this, PHYS_INPUT_TIMELENGTH);
 
 #ifdef SVQC
        this.pm_frametime = frametime;
index c74602daba5ab22ccc529f2a54cab83e5ff404ce..f1553f2f130c7e4f2f6f07098ac2a84242b84cd4 100644 (file)
@@ -160,14 +160,19 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
 #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')
+       #define PHYS_MOVETYPE(s) ((s).move_movetype)
+
        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
 
@@ -191,8 +196,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
@@ -241,6 +244,9 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK)
 
 #elif defined(SVQC)
 
+       #define PHYS_FIXANGLE(s) ((s).fixangle)
+       #define PHYS_MOVETYPE(s) ((s).movetype)
+
        bool Physics_Valid(string thecvar);
 
        .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
@@ -250,8 +256,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
index bbf315183d12cd24c0f4749fdbb72919b31a1ed5..c7fc313a8802b082147098b3c1e391570ebff0ab 100644 (file)
@@ -124,6 +124,7 @@ REGISTER_STAT(BUFF_TIME, float)
 REGISTER_STAT(CTF_FLAGSTATUS, int)
 REGISTER_STAT(ENTRAP_ORB, float)
 REGISTER_STAT(ENTRAP_ORB_ALPHA, float)
+REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime)
 
 #ifdef SVQC
 int autocvar_g_multijump;
index 0de2fe1cf5003da09c5a04d89fb8daccf4dfcbb3..8563349fd920f74e725c66bd4cd9609ae8142fa3 100644 (file)
@@ -195,6 +195,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
     {
         this.drawmask  = MASK_NORMAL;
                this.move_movetype = MOVETYPE_TOSS;
+               if (isnew) IL_PUSH(g_drawables, this);
         this.draw       = ItemDraw;
         this.solid = SOLID_TRIGGER;
         //this.flags |= FL_ITEM;
index 95f9ebc697a36932390a1856791be8adaa5134ad..8bd6016f28a5498f33cb0a0c544fbeca29a93d3d 100644 (file)
@@ -151,6 +151,7 @@ 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.move_movetype = MOVETYPE_NONE;
index 07709df33e8cb0bbf31997724e3dfe2ecfb510d5..341ca460feb28eef5bcf954e59b3f9aee7c18c30 100644 (file)
@@ -171,6 +171,7 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew)
                this.move_movetype = 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;
 
index 1705d4081ead4bafe1a874992dbe379843e5f5b8..0cf0615da8e66fae26ea186239ede6f0c28ca7b0 100644 (file)
@@ -378,6 +378,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
index d2cf18963a4af4f5d5fe8bcef658fa0873dec380..cc7dc09228b872efe428a0704d62764e353f6889 100644 (file)
@@ -134,6 +134,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
index 6bf6b0b1a46428ea463293d10e5414e08b73703b..29c7d0a7b125898ae519ced47c7bd42fc1ac850f 100644 (file)
@@ -310,6 +310,7 @@ NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew)
                this.move_movetype = 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))
index 399ba5baf5b9f134476ae64fcba2147ccbbd3b62..d10ff11280b2aa82efd6babfd450c7e996786422 100644 (file)
@@ -377,5 +377,6 @@ NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
 
        InterpolateOrigin_Note(this);
        this.draw = Draw_Laser;
+       if (isnew) IL_PUSH(g_drawables, this);
 }
 #endif
index 4a30ca69510d59a5c1aac0577f6a92fed64095d1..5ec7b41fd484c84fe3cbbe8ce7f32452038c8d9e 100644 (file)
@@ -235,6 +235,7 @@ void trigger_push_touch(entity this, entity toucher)
        if (toucher.flags & FL_PROJECTILE)
        {
                toucher.angles = vectoangles (toucher.velocity);
+               toucher.com_phys_gravity_factor = 1;
                switch(toucher.move_movetype)
                {
                        case MOVETYPE_FLY:
index e2deb39803ab05b66b8f3f24d65134338f3ca585..8fd5cb08cf363e9dac1ee8f074082083234e0b3e 100644 (file)
@@ -238,6 +238,7 @@ void turret_construct(entity this)
        this.tur_head.drawmask                  = MASK_NORMAL;
        this.anim_start_time                    = 0;
        this.draw2d = turret_draw2d;
+       IL_PUSH(g_drawables_2d, this);
        this.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
        this.teamradar_color = '1 0 0';
        this.alpha = 1;
index e21cd498f71738dc66921e209e83b28282ebbb55..38a9335f7a8937d67a07802629f556cc0c2736e2 100644 (file)
@@ -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 d050509b93dc2702913a4fda6408e36f77c87ec8..9706d01995cf8eff514f51a50277b8de4634b42f 100644 (file)
@@ -48,12 +48,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 +63,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.origin_x    = ReadCoord();
-       axh.origin_y    = ReadCoord();
-       axh.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 +91,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;
+                               remove(axh);
+                       }
+               }
                return;
        }
 
@@ -103,6 +126,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)
index 82b20a6bc21776e29c5a966720bfe032ffb008b7..4fd9a0d5231d38abfe5941e419717db50f2ae98d 100644 (file)
@@ -3,20 +3,30 @@
 bool SendAuxiliaryXhair(entity this, entity to, int sf)
 {
        WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
+       WriteByte(MSG_ENTITY, sf);
 
        WriteByte(MSG_ENTITY, this.cnt);
 
-       WriteCoord(MSG_ENTITY, this.origin_x);
-       WriteCoord(MSG_ENTITY, this.origin_y);
-       WriteCoord(MSG_ENTITY, this.origin_z);
+       if(sf & 2)
+       {
+               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));
+       if(sf & 4)
+       {
+               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))
@@ -27,17 +37,26 @@ void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, int axh_id)
 
        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)
index d5a7bcedf669a8a8aebcf3173de98f1816abe7f3..9cb8b74c2b5ed03c8b1b083c26f7640dfb614e3e 100644 (file)
@@ -71,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);
     }
 
 
index 14405460ed4b9db955973cfb900744c479522019..86f4b22b8b6735dcc7c69a2f28bc97754008bc31 100644 (file)
@@ -201,15 +201,13 @@ void raptor_flare_damage(entity this, entity inflictor, entity attacker, float d
 void raptor_flare_think(entity this)
 {
     this.nextthink = time + 0.1;
-    entity _missile = findchainentity(enemy, this.owner);
-    while(_missile)
+    FOREACH_ENTITY_ENT(enemy, this.owner,
     {
-        if(_missile.flags & FL_PROJECTILE)
-        if(vdist(this.origin - _missile.origin, <, autocvar_g_vehicle_raptor_flare_range))
+        if(it.flags & FL_PROJECTILE)
+        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);
@@ -246,6 +244,7 @@ void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
     sfrag.solid = SOLID_CORPSE;
 
     sfrag.draw = RaptorCBShellfragDraw;
+    IL_PUSH(g_drawables, sfrag);
 
     sfrag.velocity = _vel;
     sfrag.avelocity = prandomvec() * vlen(sfrag.velocity);
index fbe82436c7ceb6f97a8eec7caf80878dc5fe63db..dc1f6ba60a29e16190c54d930e285af683b314d0 100644 (file)
@@ -48,21 +48,17 @@ 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)
+    FOREACH_ENTITY_ENT(realowner, this.owner,
     {
-        if(getthink(rkt) == spiderbot_rocket_guided)
+        if(i == 0) // something exists, let's trace!
+            crosshair_trace(this.owner);
+
+        if(getthink(it) == spiderbot_rocket_guided)
         {
-            rkt.pos1 = trace_endpos;
-            setthink(rkt, spiderbot_rocket_unguided);
+            it.pos1 = trace_endpos;
+            setthink(it, spiderbot_rocket_unguided);
         }
-        rkt = rkt.chain;
-    }
+    });
 }
 
 float spiberbot_calcartillery_flighttime;
index 4de39db1772f31ab462467f55a0b76e9f3eeb3ec..8b844f13e08e39d8b0585ac94122733b3597e6ee 100644 (file)
@@ -191,6 +191,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;
index 1eaf4062d462d6860df66d0001b719e4ec0eeb27..3e3af5e0f1e0f18f89d1b2a25ab044693b07c7d9 100644 (file)
@@ -1296,6 +1296,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);
index bcc81c82e981eeea588da0f631c672f8617ef981..3049225c4ffef66b20f13adfce03616ee2ac13e2 100644 (file)
@@ -441,8 +441,7 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
     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);
@@ -451,25 +450,23 @@ 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,
         {
             if(it.classname != "rocket") continue;
 
-            targ = targetlist;
-            while(targ)
+            entity rocket = it;
+            FOREACH_ENTITY_FLOAT(bot_attack, true,
             {
-                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;
@@ -478,36 +475,35 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor))
         if(teamplay && actor.team)
             desirabledamage = desirabledamage - teamdamage;
 
+        makevectors(actor.v_angle);
         FOREACH_ENTITY_ENT(realowner, actor,
         {
             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;
+                   FOREACH_ENTITY_FLOAT(bot_attack, true,
+                   {
+                       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!
index a1d6eb39a7a5af43cea4cc42fbabdfee928e9286..1f8cc19441db45195999fe3262d577a09e9a8b4a 100644 (file)
@@ -50,6 +50,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) \
@@ -156,7 +157,7 @@ void W_Electro_Explode(entity this, entity directhitentity)
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
 
-       if(this.move_movetype == MOVETYPE_BOUNCE)
+       if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway?
        {
                RadiusDamage(
                        this,
@@ -202,8 +203,12 @@ void W_Electro_TouchExplode(entity this, entity 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,6 +257,7 @@ 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)
@@ -285,6 +291,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
        proj.projectiledeathtype = WEP_ELECTRO.m_id;
        setorigin(proj, w_shotorg);
 
+       // if (IS_CSQC)
        set_movetype(proj, MOVETYPE_FLY);
        W_SetupProjVelocity_PRI(proj, electro);
        proj.angles = vectoangles(proj.velocity);
@@ -296,6 +303,45 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
        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;
+
+       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;
+
+       remove(this);
+
+       if(to)
+               SetMovetypeFollow(this, to);
 }
 
 void W_Electro_Orb_Touch(entity this, entity toucher)
@@ -308,6 +354,9 @@ void W_Electro_Orb_Touch(entity this, entity toucher)
                //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);
        }
 }
 
@@ -359,8 +408,8 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor)
 
        W_SetupShot_ProjectileSize(
                actor,
-               '0 0 -4',
-               '0 0 -4',
+               '-4 -4 -4',
+               '4 4 4',
                false,
                2,
                SND_ELECTRO_FIRE2,
@@ -388,7 +437,7 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor)
        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);
index 8926ffa306ea0bd5bbfbe68438deed9481b460f4..01a3497d06f83497c934117fdba52966e3a66bf9 100644 (file)
@@ -541,6 +541,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)
index 331e2dd92925c8d768351e8cf572390cc6323706..59dfc10e20ecfbb9f31b856d47a058ea432ab27e 100644 (file)
@@ -130,7 +130,10 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we
 
        // casing code
        if(autocvar_g_casings >= 2)
+       {
+               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);
+       }
 
        if(actor.misc_bulletcounter == 1)
                W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo));
@@ -202,7 +205,10 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity
        W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
 
        if(autocvar_g_casings >= 2) // casing code
+       {
+               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);
+       }
 
        int slot = weaponslot(weaponentity);
        ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
@@ -226,7 +232,10 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit
        W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
 
        if(autocvar_g_casings >= 2) // casing code
+       {
+               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);
+       }
 
        actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
        if(actor.misc_bulletcounter == 0)
index ffa9793e80d15317eae7d355d20ec86dfcf70957..8b1e0cd2e12bbc268551ee8a62e117483b621639 100644 (file)
@@ -415,8 +415,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);
@@ -425,31 +424,26 @@ 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)
+
+        FOREACH_ENTITY_ENT(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);
+               if(it.classname != "mine") continue;
+
+               entity mine = it;
+               FOREACH_ENTITY_FLOAT(bot_attack, true,
+               {
+                       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)
@@ -457,41 +451,37 @@ 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);
+        FOREACH_ENTITY_ENT(realowner, actor,
         {
-            if(mine.realowner != actor)
-            {
-                mine = find(mine, classname, "mine");
-                continue;
-            }
-            makevectors(mine.v_angle);
-            targ = targetlist;
+            if(it.classname != "mine") continue;
+
             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;
+                   FOREACH_ENTITY_FLOAT(bot_attack, true,
+                   {
+                       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
index 13e44531029ae8b1c47d7adb2146a8e238653a67..c46b8f2b2cc8cd859add380552083e8839a7c879 100644 (file)
@@ -76,7 +76,10 @@ 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)
+    {
+        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);
+    }
 }
 
 void W_Rifle_Attack(entity actor)
index 499453ecd6f63d71b81760073bcbc6b1712ebe15..539ccfdb72c444d8686be62258616cd9008155db 100644 (file)
@@ -2,7 +2,7 @@
 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);
+/* flags     */ ATTRIB(Shockwave, spawnflags, int, WEP_FLAG_NORMAL | 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");
@@ -407,9 +407,9 @@ void W_Shockwave_Attack(entity actor)
        if(lag)
        {
                FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag));
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
-                       if (it != actor)
-                               antilag_takeback(it, it, time - lag);
+               IL_EACH(g_monsters, it != actor,
+               {
+                       antilag_takeback(it, it, time - lag);
                });
        }
 
@@ -687,9 +687,9 @@ void W_Shockwave_Attack(entity actor)
        if(lag)
        {
                FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it)));
-               FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
-                       if (it != actor)
-                               antilag_restore(it, it);
+               IL_EACH(g_monsters, it != actor,
+               {
+                       antilag_restore(it, it);
                });
        }
 }
@@ -862,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();
index 34d2e462c724008eda9063150624d9547b3248dc..f89ef6872b1db46ce3706b4a0799e2dc00ecb3b7 100644 (file)
@@ -2,7 +2,7 @@
 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);
+/* 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");
@@ -73,8 +73,11 @@ void W_Shotgun_Attack(Weapon thiswep, entity actor, float isprimary)
 
        // casing code
        if(autocvar_g_casings >= 1)
+       {
+               makevectors(actor.v_angle); // for some reason, this is lost
                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);
+       }
 
        // muzzle flash for 1st person view
        flash = spawn();
index 362209f919698e6cb8070fa25f95db208a178af4..8a8e858ecb5ce197e1ce6b1a55ade7e718c85cbc 100644 (file)
@@ -140,6 +140,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();
index 240eec0c1cf5d7fd1f8a79e170eb407917347e06..01fa43aaf28d2669bdc0132b2ecc5df5ad503c80 100644 (file)
@@ -55,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;
index c0e6d3ba728e738b5cc7f431e2025501df0278e4..939a86adb359c502e8ddc767da306077ec6256f7 100644 (file)
@@ -40,6 +40,8 @@ int(string s1, string s2, int len) _strncasecmp = #230;
 int() _buf_create = #440;
 #define buf_create _buf_create
 
+bool(entity ent) wasfreed = #353;
+
 #pragma noref 0
 
 #endif
index db8752d2bb8075d2f9611c3c776830c6d682540d..9419dceea18aafb7f8e75f1063b0ab8a3d79c523 100644 (file)
@@ -6,6 +6,7 @@
 #undef error
 #undef movetogoal
 #undef objerror
+#undef remove
 #undef walkmove
 
 #ifdef MENUQC
index b24d0120a49e56a0bee597379b3522e454013949..801b8731bc89bf08bdcf8d5ed52c445763986c3e 100644 (file)
@@ -6,4 +6,5 @@
 #define error builtin_error
 #define movetogoal builtin_movetogoal
 #define objerror builtin_objerror
+#define remove builtin_remove
 #define walkmove builtin_walkmove
index 9fd6ad6948ac5e7b2501d338ea2b0f338743759c..d49094f7b48c68019752dd871f46ee360bd7478e 100644 (file)
@@ -44,5 +44,4 @@
 ### listening
 
     entity listener = new_pure(someListener);
-    listener.evt_$event = void(entity this) { code; };
-    subscribe(listener, $event);
+    subscribe(listener, $event, void(entity this) { code; });
index 32db1f3d3e6cb1f1b0a4f7672dfc1a058d522f07..726f693e9e8149e36ce0af9c29c6d81942c40382 100644 (file)
@@ -1,58 +1,4 @@
-/** 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) \
-       MACRO_BEGIN \
-       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); \
-       float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
-       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
-
+#include "_lib.qh"
 
 #include "_mod.inc"
 #include "components/_mod.inc"
diff --git a/qcsrc/ecs/_lib.qh b/qcsrc/ecs/_lib.qh
new file mode 100644 (file)
index 0000000..a617c73
--- /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); \
+       float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
+       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
index 37944ca5c3f8d026ce1ae1b5a7c9da7188ec5b08..bc1c66ace4cb1746ed0b723bcfa1d7dcb4b6374a 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 COMPONENT(in);
-.vector com_in_move;
+.vector com_in_move, com_in_move_prev;
 .vector com_in_angles;
 .bool   com_in_jump;
+.bool   com_in_crouch;
index 502657e4bcbeb52db0fa319c1c41161a21f13480..f150a296c299e8d46980c1ba2d4fb278665947d4 100644 (file)
@@ -4,4 +4,23 @@ 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;
index 98fb4815c1ce28cc699a429537ea75c2643b4487..c139d72b51ed4738fb396b3407fd335324cd921a 100644 (file)
@@ -1 +1,2 @@
 // generated file; do not modify
+#include <ecs/events/physics.qc>
index 98fb4815c1ce28cc699a429537ea75c2643b4487..1e8c791ce9b403aafc843acd2e95b6aebff0e082 100644 (file)
@@ -1 +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));
index f79d7f065134aaff00519783a734cb1bba4ea8b7..a5ada6cfbed8684281f77a42c6ee4b24c0c161bd 100644 (file)
@@ -1,2 +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
index e71e0591c747dfe996fa2295a73d1b363ac395dc..869aefd5571e4907ba47f65867eb415a276f6767 100644 (file)
@@ -1,2 +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..206c80d
--- /dev/null
@@ -0,0 +1,30 @@
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+       this.team = myteam + 1; // is this correct?
+       PHYS_WATERJUMP_TIME(this) -= dt;
+       this.oldmovement = this.movement;
+       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)
+{
+       // no vehicle prediction
+       return hud != HUD_NORMAL;
+}
+
+void sys_phys_monitor(entity this) {}
+
+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);
index 8348b87851cc82f8e3bf6999d54bd01a6246116b..8646b29cc983827a48be0387de3373d8f3295bf9 100644 (file)
@@ -1,6 +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)
 {
-       PM_Main(this);
+       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)) { return; } sys_phys_monitor(this);
+
+       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 (PHYS_MOVETYPE(this) == 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);
+
+       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;
+       }
+
+       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)) {
+               // handled
+       } else if (PHYS_MOVETYPE(this) == MOVETYPE_NOCLIP
+           || PHYS_MOVETYPE(this) == MOVETYPE_FLY
+           || PHYS_MOVETYPE(this) == 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);
+       } 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)
+{
+       const vector g = -this.com_phys_gravity;
+       const bool jump = this.com_in_jump;
+
+       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) {
+                       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 (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.oldmovement.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 * 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(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, 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);
+                       }
+               }
+       } 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 (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, 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, 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);
 }
index 38d4e55e55e94da8a31ba67c1ff3f832a286932c..16c996adee6ccf97ae82f3400a0fd71eeb14fb7f 100644 (file)
@@ -1,3 +1,11 @@
 #pragma once
 
 SYSTEM(phys, 30, 10);
+
+void sys_phys_fix(entity this, float dt);
+bool sys_phys_override(entity this);
+void sys_phys_monitor(entity this);
+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..0df3411
--- /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)
+{
+       int buttons = PHYS_INPUT_BUTTON_MASK(this);
+       if (PM_check_specialcommand(this, buttons)) { return true; }
+       if (this.PlayerPhysplug && this.PlayerPhysplug(this)) { return true; }
+       return false;
+}
+
+void sys_phys_monitor(entity this)
+{
+       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);
+}
+
+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);
+       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;
+       }
+}
+
+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 61755fe94322e1d360390792823fcb0d9ed77d6e..f52c9a3c735437ef6195f8c1499baceba51842af 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
@@ -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"
@@ -111,6 +124,22 @@ void    isnt_bool(float this) { print(ftos(this)); }
 
 #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_WARNING("======OBJECT ERROR======"); \
+       entity _e = (this); \
+       eprint(_e); \
+       objerror_safe(_e); \
+       delete(_e); \
+       LOG_WARNINGF("%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(); }
@@ -236,3 +265,8 @@ void    isnt_bool(float this) { print(ftos(this)); }
        #define CSQC_Ent_Remove _CSQC_Ent_Remove
 #endif
 #undef ENGINE_EVENT
+
+#ifndef MENUQC
+       #include <ecs/_lib.qh>
+       #include <ecs/components/_mod.qh>
+#endif
diff --git a/qcsrc/lib/intrusivelist.qh b/qcsrc/lib/intrusivelist.qh
new file mode 100644 (file)
index 0000000..1d7b251
--- /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, NULL);
+       ATTRIB(IntrusiveList, il_tail, entity, NULL);
+       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\n", 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, dtor) \
+       MACRO_BEGIN \
+       { \
+               remove(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_WARNINGF("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..7183e90feab6bad7a26ce4ba98f8e01886ea8bca 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)
 
 #ifndef MENUQC
 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)
index 370d96be5629f2fdc3e6a26b767799a7c1c2c7b4..a8874adbd4630d69a03458346404391ac23bd2a6 100644 (file)
@@ -82,13 +82,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 3708552d5968c5d5bcfb1c976bd56cae9779fa63..e3206dcf3ee799b584e85bf71457fbe44d20ee3e 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
 
@@ -271,7 +278,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 5d42bdccd74c2c76927b7c8cf07734037d69d8b3..bc545b0d0f53721c338fe7b9d6af93b6a5e8bc6b 100644 (file)
@@ -102,12 +102,6 @@ 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
index bd8b792967a5cfbba7365455c12aa6044b19b2a4..8aaacdec428e366123d197810684927ed97cf030 100644 (file)
@@ -27,7 +27,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 +38,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)
index adb6d6a95b272fdfc59f6c650e318411ad3738c3..10e8ed8c437f98983b55945e0e938bf9f5d73e0f 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';
@@ -124,6 +131,17 @@ 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))
+
 #ifndef MENUQC
        vector get_corner_position(entity box, int corner)
        {
index 9b617ab8a16eaa421a050b3732bb7890801af3b6..f32b85298ed7e23a8a70c04f46aa8d1fedad6dde 100644 (file)
@@ -849,6 +849,7 @@ void m_draw(float width, float height)
        postMenuDraw();
 
        frametime = 0;
+       IL_ENDFRAME();
 }
 
 void m_display()
index a32f8b5832cf01e9ecccc534be2d794d6672118f..ed3083a1203fd94c69943efb17341c80c45127d1 100644 (file)
@@ -43,7 +43,7 @@ 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; });
+// 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 78dfc8062cf335e5576102bd4466c203a8d68805..f66b77a068145eadb0460bfdd776d531aed6b454 100644 (file)
@@ -357,7 +357,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;
index 8707a03bd4d9b7d0070f6a71b6503f2ed9aef5cc..308cd95101ab09aa8ccb1203dcc6cc07af3e2504 100644 (file)
@@ -346,31 +346,28 @@ void bot_endgame()
 
 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)
+
+       entity prevbot = NULL;
+       FOREACH_CLIENT(true,
        {
-               player_count = player_count + 1;
-               e.nextplayer = e.chain;
-               if (IS_BOT_CLIENT(e))
+               ++player_count;
+
+               if(IS_BOT_CLIENT(it))
                {
-                       if (prevbot)
-                               prevbot.nextbot = e;
+                       if(prevbot)
+                               prevbot.nextbot = it;
                        else
                        {
-                               bot_list = e;
+                               bot_list = it;
                                bot_list.nextbot = NULL;
                        }
-                       prevbot = e;
-                       currentbots = currentbots + 1;
+                       prevbot = it;
+                       ++currentbots;
                }
-               e = e.chain;
-       }
+       });
        LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
        bot_strategytoken = bot_list;
        bot_strategytoken_taken = true;
@@ -427,70 +424,84 @@ void bot_clientconnect(entity 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)
+
+       entity best = NULL;
+       float besttime = 0;
+       int bestcount = 0;
+
+       int bcount = 0;
+       FOREACH_ENTITY_FLOAT(isbot, true,
        {
-               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)
+               ++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 = head.createdtime;
-                       best = head;
+                       besttime = it.createdtime;
+                       best = it;
                }
-               else if (thiscount == bestcount && besttime < head.createdtime)
+               else if(thiscount == bestcount && besttime < it.createdtime)
                {
-                       besttime = head.createdtime;
-                       best = head;
+                       besttime = it.createdtime;
+                       best = it;
                }
-               head = head.chain;
-       }
+       });
+       if(!bcount)
+               return; // no bots to remove
        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)
+       float besttime = 0;
+       entity best = NULL;
+       int bcount = 0;
+
+       FOREACH_ENTITY_FLOAT(isbot, true,
        {
-               if (besttime < head.createdtime)
+               ++bcount;
+
+               if(!best)
                {
-                       besttime = head.createdtime;
-                       best = head;
+                       best = it;
+                       besttime = it.createdtime;
                }
-               head = head.chain;
-       }
+
+               if(besttime < it.createdtime)
+               {
+                       besttime = it.createdtime;
+                       best = it;
+               }
+       });
+
+       if(!bcount)
+               return; // no bots to remove
+
        currentbots = currentbots - 1;
        dropclient(best);
 }
@@ -573,7 +584,7 @@ float bot_fixcount()
        // 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))
+       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)))
        {
@@ -659,9 +670,11 @@ void bot_serverframe()
                else
                {
                        // TODO: Make this check cleaner
-                       entity wp = findchain(classname, "waypoint");
-                       if(time - wp.nextthink > 10)
+                       IL_EACH(g_waypoints, time - it.nextthink > 10,
+                       {
                                waypoint_save_links();
+                               break;
+                       });
                }
        }
        else
index a28c94b7f729a8744241489008dc324caae6f51d..e9dd92668995f895482c59dd310fcb4138f1d09d 100644 (file)
@@ -52,9 +52,7 @@ 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;
index c3603e9ff1bdc7390bde112b1e67d876d4fd0921..b22ded20fb618f0e7c58ca0b74612c327e47483e 100644 (file)
@@ -41,41 +41,30 @@ void havocbot_ai(entity this)
                // 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))
+               if(!this.goalcurrent)
+               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)
+                       entity newgoal = NULL;
+                       IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 10000),
                        {
-                               distance = vlen(head.origin - this.origin);
-                               if(distance>10000)
-                                       continue;
-
-                               if(head.origin.z < this.origin.z)
+                               if(it.origin.z < this.origin.z)
                                        continue;
 
-                               if(head.origin.z - this.origin.z - this.view_ofs.z > 100)
+                               if(it.origin.z - this.origin.z - this.view_ofs.z > 100)
                                        continue;
 
-                               if (pointcontents(head.origin + head.maxs + '0 0 1') != CONTENT_EMPTY)
+                               if (pointcontents(it.origin + it.maxs + '0 0 1') != CONTENT_EMPTY)
                                        continue;
 
-                               traceline(this.origin + this.view_ofs , head.origin, true, head);
+                               traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this);
 
-                               if(trace_fraction<1)
+                               if(trace_fraction < 1)
                                        continue;
 
-                               if(distance<bestdistance)
-                               {
-                                       newgoal = head;
-                                       bestdistance = distance;
-                               }
-                       }
+                               if(!newgoal || vlen2(it.origin - this.origin) < vlen2(newgoal.origin - this.origin))
+                                       newgoal = it;
+                       });
 
                        if(newgoal)
                        {
@@ -496,27 +485,17 @@ void havocbot_movetogoal(entity this)
                {
                        if(fabs(this.velocity.z)<50)
                        {
-                               entity head, newgoal = NULL;
-                               float distance, bestdistance = 0;
-
-                               for (head = findchain(classname, "waypoint"); head; head = head.chain)
+                               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);
 
-                                       distance = vlen(head.origin - this.origin);
-                                       if(distance>1000)
+                                       if(trace_fraction < 1)
                                                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 || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
+                                               newgoal = it;
+                               });
 
                                if(newgoal)
                                {
index 034e29fabf4a254307abefa73d737131fa545093..9ea6a3d904e6da3347805a3a05ea61827bf9fb6c 100644 (file)
 
 void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
 {
-       entity head;
-       float rating, d, discard, distance, friend_distance, enemy_distance;
+       float rating, d, discard, friend_distance, enemy_distance;
        vector o;
        ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
-       head = findchainfloat(bot_pickup, true);
 
-       while (head)
+       FOREACH_ENTITY_FLOAT(bot_pickup, true,
        {
-               o = (head.absmin + head.absmax) * 0.5;
-               distance = vlen(o - org);
+               o = (it.absmin + it.absmax) * 0.5;
                friend_distance = 10000; enemy_distance = 10000;
                rating = 0;
 
-               if(!head.solid || distance > sradius || (head == this.ignoregoal && time < this.ignoregoaltime) )
-               {
-                       head = head.chain;
+               if(!it.solid || vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) )
                        continue;
-               }
 
                // Check if the item can be picked up safely
-               if(head.classname == "droppedweapon")
+               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)
-                       {
-                               head = head.chain;
                                continue;
-                       }
-                       if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
-                       {
-                               head = head.chain;
+                       if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
                                continue;
-                       }
                }
                else
                {
                        // Ignore items under water
-                       traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
+                       traceline(it.origin + it.maxs, it.origin + it.maxs, MOVE_NORMAL, it);
                        if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
-                       {
-                               head = head.chain;
                                continue;
-                       }
                }
 
                if(teamplay)
                {
                        discard = false;
 
-                       FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it), LAMBDA(
+                       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 )
@@ -79,29 +66,29 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float
 
                                        discard = true;
 
-                                       if( head.health && it.health > this.health )
+                                       if( picker.health && it.health > this.health )
                                                continue;
 
-                                       if( head.armorvalue && it.armorvalue > this.armorvalue)
+                                       if( picker.armorvalue && it.armorvalue > this.armorvalue)
                                                continue;
 
-                                       if( head.weapons )
-                                       if( head.weapons & ~it.weapons )
+                                       if( picker.weapons )
+                                       if( picker.weapons & ~it.weapons )
                                                continue;
 
-                                       if (head.ammo_shells && it.ammo_shells > this.ammo_shells)
+                                       if (picker.ammo_shells && it.ammo_shells > this.ammo_shells)
                                                continue;
 
-                                       if (head.ammo_nails && it.ammo_nails > this.ammo_nails)
+                                       if (picker.ammo_nails && it.ammo_nails > this.ammo_nails)
                                                continue;
 
-                                       if (head.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
+                                       if (picker.ammo_rockets && it.ammo_rockets > this.ammo_rockets)
                                                continue;
 
-                                       if (head.ammo_cells && it.ammo_cells > this.ammo_cells)
+                                       if (picker.ammo_cells && it.ammo_cells > this.ammo_cells)
                                                continue;
 
-                                       if (head.ammo_plasma && it.ammo_plasma > this.ammo_plasma)
+                                       if (picker.ammo_plasma && it.ammo_plasma > this.ammo_plasma)
                                                continue;
 
                                        discard = false;
@@ -113,40 +100,33 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float
                                        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) ||
+                       if ( (enemy_distance < friend_distance && vdist(o - org, <, enemy_distance)) ||
                                (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
-                               rating = head.bot_pickupevalfunc(this, head);
+                               rating = it.bot_pickupevalfunc(this, it);
 
                }
                else
-                       rating = head.bot_pickupevalfunc(this, head);
+                       rating = it.bot_pickupevalfunc(this, it);
 
                if(rating > 0)
-                       navigation_routerating(this, head, rating * ratingscale, 2000);
-               head = head.chain;
-       }
+                       navigation_routerating(this, it, rating * ratingscale, 2000);
+       });
 }
 
 void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius)
 {
-       entity head;
-       head = findchain(classname, "dom_controlpoint");
-       while (head)
+       FOREACH_ENTITY_CLASS("dom_controlpoint", vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius),
        {
-               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;
-       }
+               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_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius)
index 8deefdb39716c527be0bc1e36512cccfb07471cf..c9418e2e31e072f054c84e9b4cb5f450873bb979 100644 (file)
@@ -373,25 +373,17 @@ float navigation_waypoint_will_link(vector v, vector org, entity ent, float walk
 // 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");
+       vector pm1 = ent.origin + ent.mins;
+       vector pm2 = ent.origin + ent.maxs;
 
        // do two scans, because box test is cheaper
-       w = waylist;
-       while (w)
+       IL_EACH(g_waypoints, it != ent && it != except,
        {
-               // if object is touching spawnfunc_waypoint
-               if(w != ent && w != except)
-                       if (boxesoverlap(pm1, pm2, w.absmin, w.absmax))
-                               return w;
-               w = w.chain;
-       }
+               if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
+                       return it;
+       });
 
-       org = ent.origin + 0.5 * (ent.mins + ent.maxs);
+       vector 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)
@@ -399,34 +391,28 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
        if (navigation_testtracewalk)
                te_plasmaburn(org);
 
-       best = NULL;
+       entity best = NULL;
+       vector v;
 
        // box check failed, try walk
-       w = waylist;
-       while (w)
+       IL_EACH(g_waypoints, it != ent,
        {
-               // if object can walk from spawnfunc_waypoint
-               if(w != ent)
+               if(it.wpisbox)
                {
-                       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;
-                       }
+                       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);
                }
-               w = w.chain;
-       }
+               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)
@@ -442,46 +428,39 @@ entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
 }
 
 // finds the waypoints near the bot initiating a navigation query
-float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist)
+float navigation_markroutes_nearestwaypoints(entity this, float maxdist)
 {
-       entity head;
-       vector v, m1, m2, diff;
-       float c;
+       vector v, m1, m2;
 //     navigation_testtracewalk = true;
-       c = 0;
-       head = waylist;
-       while (head)
+       int c = 0;
+       IL_EACH(g_waypoints, !it.wpconsidered,
        {
-               if (!head.wpconsidered)
+               if (it.wpisbox)
                {
-                       if (head.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))
                        {
-                               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;
-                               }
+                               it.wpnearestpoint = v;
+                               it.wpcost = vlen(v - this.origin) + it.dmg;
+                               it.wpfire = 1;
+                               it.enemy = NULL;
+                               c = c + 1;
                        }
                }
-               head = head.chain;
-       }
+       });
        //navigation_testtracewalk = false;
        return c;
 }
@@ -515,19 +494,17 @@ void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vecto
 // 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;
+       float cost, cost2;
        vector p;
-       w = waylist = findchain(classname, "waypoint");
-       while (w)
+
+       IL_EACH(g_waypoints, true,
        {
-               w.wpconsidered = false;
-               w.wpnearestpoint = '0 0 0';
-               w.wpcost = 10000000;
-               w.wpfire = 0;
-               w.enemy = NULL;
-               w = w.chain;
-       }
+               it.wpconsidered = false;
+               it.wpnearestpoint = '0 0 0';
+               it.wpcost = 10000000;
+               it.wpfire = 0;
+               it.enemy = NULL;
+       });
 
        if(fixed_source_waypoint)
        {
@@ -541,7 +518,7 @@ void navigation_markroutes(entity this, entity fixed_source_waypoint)
        {
                // 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;
+               float increment, maxdistance;
                if(IS_ONGROUND(this))
                {
                        increment = 750;
@@ -553,77 +530,70 @@ void navigation_markroutes(entity this, entity fixed_source_waypoint)
                        maxdistance = 1500;
                }
 
-               for(i=increment;!navigation_markroutes_nearestwaypoints(this, waylist, i)&&i<maxdistance;i+=increment);
+               for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment);
        }
 
-       searching = true;
+       bool searching = true;
        while (searching)
        {
                searching = false;
-               w = waylist;
-               while (w)
+               IL_EACH(g_waypoints, it.wpfire,
                {
-                       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;
-               }
+                       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)
 {
-       entity w, wp, waylist;
-       float searching, cost, cost2;
+       float cost, cost2;
        vector p;
-       w = waylist = findchain(classname, "waypoint");
-       while (w)
+       IL_EACH(g_waypoints, true,
        {
-               w.wpconsidered = false;
-               w.wpnearestpoint = '0 0 0';
-               w.wpcost = 10000000;
-               w.wpfire = 0;
-               w.enemy = NULL;
-               w = w.chain;
-       }
+               it.wpconsidered = false;
+               it.wpnearestpoint = '0 0 0';
+               it.wpcost = 10000000;
+               it.wpfire = 0;
+               it.enemy = NULL;
+       });
 
        if(fixed_source_waypoint)
        {
@@ -638,36 +608,32 @@ void navigation_markroutes_inverted(entity fixed_source_waypoint)
                error("need to start with a waypoint\n");
        }
 
-       searching = true;
+       bool searching = true;
        while (searching)
        {
                searching = false;
-               w = waylist;
-               while (w)
+               IL_EACH(g_waypoints, it.wpfire,
                {
-                       if (w.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,
                        {
-                               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;
-               }
+                               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);
+                       });
+               });
        }
 }
 
@@ -1024,40 +990,34 @@ void navigation_goalrating_end(entity this)
 
 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)
+       IL_EACH(g_waypoints, true,
        {
                danger = 0;
-               m1 = botframe_dangerwaypoint.mins;
-               m2 = botframe_dangerwaypoint.maxs;
-               head = bot_dodgelist;
-               while (head)
+               m1 = it.mins;
+               m2 = it.maxs;
+               FOREACH_ENTITY_FLOAT(bot_dodge, true,
                {
-                       v = head.origin;
+                       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 = (head.absmin + head.absmax) * 0.5;
-                       d = head.bot_dodgerating - vlen(o - v);
+                       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;
                        }
-                       head = head.chain;
-               }
-               botframe_dangerwaypoint.dmg = danger;
+               });
+               it.dmg = danger;
                c = c + 1;
                if (c >= maxupdate)
                        break;
-               botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint");
-       }
+       });
 }
 
 void navigation_unstuck(entity this)
@@ -1118,27 +1078,18 @@ void navigation_unstuck(entity this)
                // 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);
+               entity first = NULL;
 
-               while(head)
+               FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
                {
-                       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;
-                       }
+                       if(bot_waypoint_queue_goal)
+                               bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it;
+                       else
+                               first = it;
 
-                       head = head.chain;
-               }
+                       bot_waypoint_queue_goal = it;
+                       bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
+               });
 
                if (first)
                        bot_waypoint_queue_goal = first;
index 7fef6afef3a3f7a549f15f3753416ab5d5866a83..ad01776652b4de67098af9f77e5057d8df13e196 100644 (file)
@@ -10,7 +10,6 @@ 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
@@ -57,7 +56,7 @@ 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_markroutes_nearestwaypoints(entity this, float maxdist);
 float navigation_routetogoal(entity this, entity e, vector startposition);
 
 void navigation_clearroute(entity this);
index 01a1117c9f018332b598f4b39cb6bec4a13220c2..291b22f5fa25433274a8c0b3184f3bda92fb487a 100644 (file)
@@ -252,7 +252,7 @@ entity find_bot_by_number(float number)
        if(!number)
                return NULL;
 
-       bot = findchainflags(flags, FL_CLIENT);
+       bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities?
        while (bot)
        {
                if(IS_BOT_CLIENT(bot))
index 58bb0b46ba5d5419bf40767b212f93cdda63b22d..586f2a8c04758b8e5d9c5c3c6501c1996cf40f1f 100644 (file)
@@ -19,16 +19,14 @@ entity waypoint_spawn(vector m1, vector m2, float f)
 {
        if(!(f & WAYPOINTFLAG_PERSONAL))
        {
-               for(entity wp = findchain(classname, "waypoint"); wp; wp = wp.chain)
+               IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
                {
-                       // if a matching spawnfunc_waypoint already exists, don't add a duplicate
-                       if(boxesoverlap(m1, m2, wp.absmin, wp.absmax))
-                               return wp;
-               }
+                       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;
@@ -173,7 +171,7 @@ void waypoint_think(entity this)
        //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
        sm1 = this.origin + this.mins;
        sm2 = this.origin + this.maxs;
-       FOREACH_ENTITY_CLASS("waypoint", true,
+       IL_EACH(g_waypoints, true,
        {
                if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax))
                {
@@ -295,6 +293,8 @@ void waypoint_schedulerelink(entity wp)
 // 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);
@@ -344,28 +344,21 @@ void waypoint_remove(entity e)
 // empties the map of waypoints
 void waypoint_removeall()
 {
-       entity head, next;
-       head = findchain(classname, "waypoint");
-       while (head)
+       IL_EACH(g_waypoints, true,
        {
-               next = head.chain;
-               remove(head);
-               head = next;
-       }
+               remove(it);
+       });
 }
 
 // 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)
+       IL_EACH(g_waypoints, true,
        {
-               waypoint_schedulerelink(head);
-               head = head.chain;
-       }
+               waypoint_schedulerelink(it);
+       });
 }
 
 // Load waypoint links from file
@@ -588,87 +581,71 @@ entity waypoint_get_link(entity w, float i)
 // 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);
+       string filename = sprintf("maps/%s.waypoints.cache", mapname);
+       int file = fopen(filename, FILE_WRITE);
        if (file < 0)
        {
-               LOG_INFO("waypoint links save to ");
-               LOG_INFO(filename);
-               LOG_INFO(" failed\n");
+               LOG_INFOF("waypoint link save to %s failed\n", filename);
+               return;
        }
-       c = 0;
-       w = findchain(classname, "waypoint");
-       while (w)
+
+       int c = 0;
+       IL_EACH(g_waypoints, true,
        {
-               for(i=0;i<32;++i)
+               for(int j = 0; j < 32; ++j)
                {
-                       // :S
-                       link = waypoint_get_link(w, i);
-                       if(link==NULL)
-                               continue;
-
-                       s = strcat(vtos(w.origin), "*", vtos(link.origin), "\n");
-                       fputs(file, s);
-                       ++c;
+                       entity link = waypoint_get_link(it, j);
+                       if(link)
+                       {
+                               string s = strcat(vtos(it.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");
+       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, s;
-       float file, c;
-       entity w;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints");
-       file = fopen(filename, FILE_WRITE);
-       if (file >= 0)
+       string filename = sprintf("maps/%s.waypoints", mapname);
+       int 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");
+               waypoint_save_links(); // save anyway?
+               botframe_loadedforcedlinks = false;
+
+               LOG_INFOF("waypoint links: save to %s failed\n", filename);
+               return;
        }
-       else
+
+       int c = 0;
+       IL_EACH(g_waypoints, true,
        {
-               bprint("waypoint save to ");
-               bprint(filename);
-               bprint(" failed\n");
-       }
+               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
@@ -723,33 +700,30 @@ vector waypoint_fixorigin(vector 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)
+       IL_EACH(g_waypoints, true,
        {
-               if (w.wpisbox)
+               if(it.wpisbox)
                {
-                       if (boxesoverlap(org, org, w.absmin, w.absmax))
+                       if(boxesoverlap(org, org, it.absmin, it.absmax))
                        {
-                               e.nearestwaypoint = w;
+                               e.nearestwaypoint = it;
                                return;
                        }
                }
                else
                {
-                       if (vlen(w.origin - org) < 16)
+                       if(vdist(it.origin - org, <, 16))
                        {
-                               e.nearestwaypoint = w;
+                               e.nearestwaypoint = it;
                                return;
                        }
                }
-               w = w.chain;
-       }
+       });
+
        e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM);
 }
 
@@ -810,19 +784,20 @@ entity waypoint_spawnpersonal(entity this, vector position)
 
 void botframe_showwaypointlinks()
 {
-       entity head, w;
        if (time < botframe_waypointeditorlightningtime)
                return;
        botframe_waypointeditorlightningtime = time + 0.5;
-       FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot, LAMBDA(
+       FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot,
+       {
                if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
                {
                        //navigation_testtracewalk = true;
-                       head = navigation_findnearestwaypoint(it, false);
+                       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);
@@ -858,7 +833,7 @@ void botframe_showwaypointlinks()
                                w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin);
                        }
                }
-       ));
+       });
 }
 
 float botframe_autowaypoints_fixdown(vector v)
@@ -871,17 +846,11 @@ float botframe_autowaypoints_fixdown(vector v)
 
 float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f)
 {
-       entity w;
-
-       w = find(NULL, classname, "waypoint");
-       while (w)
+       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
-               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");
-       }
+               return 0;
+       });
 
        waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f));
        return 1;
@@ -924,25 +893,18 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
                        maxdist = 2100;
                }
 
-               float bestdist;
-               bestdist = maxdist;
-               w = find(NULL, classname, "waypoint");
-               while (w)
+               float bestdist = maxdist;
+               IL_EACH(g_waypoints, it != wp && !(it.wpflags & WAYPOINTFLAG_NORELINK),
                {
-                       if(w != wp && !(w.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))
                        {
-                               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;
-                                               }
+                               bestdist = d;
+                               p.(fld) = it;
                        }
-                       w = find(w, classname, "waypoint");
-               }
+               });
                if(bestdist < maxdist)
                {
                        LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
@@ -1048,84 +1010,80 @@ void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld)
 
 void botframe_deleteuselesswaypoints()
 {
-       entity w, w1, w2;
-       float i, j, k;
-       for (w = NULL; (w = findfloat(w, bot_pickup, true)); )
+       FOREACH_ENTITY_FLOAT(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")); )
+               navigation_findnearestwaypoint(it, false);  // Walk TO item.
+               navigation_findnearestwaypoint(it, true);  // Walk FROM item.
+       });
+       IL_EACH(g_waypoints, true,
        {
-               w.wpflags |= WAYPOINTFLAG_DEAD_END;
-               w.wpflags &= ~WAYPOINTFLAG_USEFUL;
+               it.wpflags |= WAYPOINTFLAG_DEAD_END;
+               it.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;
+               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.
-       for (w1 = NULL; (w1 = find(w1, classname, "waypoint")); )
+       IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_PERSONAL),
        {
-               if (w1.wpflags & WAYPOINTFLAG_PERSONAL)
-                       continue;
-               for (i = 0; i < 32; ++i)
+               for (int m = 0; m < 32; ++m)
                {
-                       w = waypoint_get_link(w1, i);
+                       entity w = waypoint_get_link(it, m);
                        if (!w)
                                break;
                        if (w.wpflags & WAYPOINTFLAG_PERSONAL)
                                continue;
                        if (w.wpflags & WAYPOINTFLAG_USEFUL)
                                continue;
-                       for (j = 0; j < 32; ++j)
+                       for (int j = 0; j < 32; ++j)
                        {
-                               w2 = waypoint_get_link(w, j);
+                               entity w2 = waypoint_get_link(w, j);
                                if (!w2)
                                        break;
-                               if (w1 == w2)
+                               if (it == w2)
                                        continue;
                                if (w2.wpflags & WAYPOINTFLAG_PERSONAL)
                                        continue;
-                               // If we got here, w1 != w2 exist with w1 -> w
+                               // 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 (k = 0; k < 32; ++k)
+                               for (int k = 0; k < 32; ++k)
                                {
-                                       if (waypoint_get_link(w1, k) == w2)
+                                       if (waypoint_get_link(it, k) == w2)
                                                continue;
                                        // IF WE GET HERE, w is proven useful
-                                       // to get from w1 to w2!
+                                       // 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.
 
-       for (w = NULL; (w = find(w, classname, "waypoint")); )
+       IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)),
        {
-               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
+               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()
index 60f331bb30570d85ec1ce9e8110e004d6014a751..fe307fc01ac5872538b0ffd5aada4cdd964cab24 100644 (file)
@@ -1147,7 +1147,7 @@ void ClientConnect(entity this)
        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 (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();
 
@@ -2292,8 +2292,9 @@ void PlayerPreThink (entity this)
                        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
+        } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI) && this.(weaponentity).wframe == WFRAME_FIRE1 && time < this.(weaponentity).weapon_nextthink) {
+                       do_crouch = false;
+        } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
                        do_crouch = false;
         }
 
index 517ee3796100c028220a10e0842d64d9f675e7ce..5e276c4a3a3a16024b914cec67800e4c65e59841 100644 (file)
@@ -512,96 +512,93 @@ IMPULSE(navwaypoint_save)
 IMPULSE(navwaypoint_unreachable)
 {
        if (!autocvar_g_waypointeditor) return;
-       for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
+       IL_EACH(g_waypoints, true,
        {
-               e.colormod = '0.5 0.5 0.5';
-               e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
-       }
+               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 i, m;
+       int j, m;
 
-       i = 0;
+       j = 0;
        m = 0;
-       for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
+       IL_EACH(g_waypoints, it.wpcost >= 10000000,
        {
-               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;
+               LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
+               it.colormod_z = 8;
+               it.effects |= EF_NODEPTHTEST | EF_BLUE;
+               ++j;
                ++m;
-       }
-       if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
+       });
+       if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
        navigation_markroutes_inverted(e2);
 
-       i = 0;
-       for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
+       j = 0;
+       IL_EACH(g_waypoints, it.wpcost >= 10000000,
        {
-               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
+               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;
-               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);
+               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);
 
-       i = 0;
-       for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
+       j = 0;
+       FOREACH_ENTITY_CLASS("info_player_deathmatch", true,
        {
-               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))
+               vector org = it.origin;
+               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
+               setorigin(it, trace_endpos);
+               if (navigation_findnearestwaypoint(it, false))
                {
-                       setorigin(e, org);
-                       e.effects &= ~EF_NODEPTHTEST;
-                       e.model = "";
+                       setorigin(it, org);
+                       it.effects &= ~EF_NODEPTHTEST;
+                       it.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;
+                       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 (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
+       });
+       if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
 
-       i = 0;
-       entity start = findchainflags(flags, FL_ITEM);
-       for (entity e = start; e; e = e.chain)
+       j = 0;
+       FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
        {
-               e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
-               e.colormod = '0.5 0.5 0.5';
-       }
-       for (entity e = start; e; e = e.chain)
+               it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
+               it.colormod = '0.5 0.5 0.5';
+       });
+       FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
        {
-               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);
+               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);
 
-       i = 0;
-       for (entity e = start; e; e = e.chain)
+       j = 0;
+       FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
        {
-               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);
+               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);
 }
index 3c97737e541d559ab13723d0091a6d361986adcd..81dc1a60ea1549d58c4f909b03ff2cbee4b030f6 100644 (file)
@@ -364,10 +364,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; }
@@ -436,10 +436,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 df791a62627fa7986966e1e3d15b94334bef1a07..a1c180cef831779830352ecfe6c0ad67c43409a7 100644 (file)
@@ -443,3 +443,9 @@ const int MIF_GUIDED_CONFUSABLE = MIF_GUIDED_HEAT | MIF_GUIDED_AI;
 
 .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(); }
index f3fb1a3224ad9ebd186bdd630476baa733b6e980..e4affe265f8b7dd768089358b36198fa027c1ce1 100644 (file)
@@ -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);
                });
        }
 
index 409e33cd0c0495a68aa1f4a2769a784813eca76f..7d477ac577daf27f5074f1b4194766172b6512b3 100644 (file)
@@ -540,7 +540,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);
@@ -657,7 +657,7 @@ 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
 
@@ -2004,7 +2004,7 @@ void Physics_Frame()
                        it.movetype = mt;
                        continue;
                }
-               
+
                it.movetype = ((it.move_qcphysics) ? MOVETYPE_NONE : it.move_movetype);
 
                if(it.move_movetype == MOVETYPE_NONE)
@@ -2025,6 +2025,7 @@ void Physics_Frame()
        });
 }
 
+void systems_update();
 void EndFrame()
 {
        anticheat_endframe();
@@ -2051,13 +2052,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();
 }
 
 
index 2686e05539db85165b9d043b4009b25e01c1e029..212e8cffb6dd60de37196936df8722dc9682668c 100644 (file)
@@ -33,20 +33,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)
 {
@@ -881,7 +886,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 +917,7 @@ void InitializeEntitiesRun()
         }
         e = next;
     }
-    remove = remove_unsafely;
+    delete_fn = remove_unsafely;
 }
 
 .float(entity) isEliminated;
@@ -1031,8 +1036,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)
index cab30262a02d2c5f8d77a1cb1a9bfbec287308d0..7285d7db36607738a58aa7c898a11a6c2cd5af55 100644 (file)
@@ -429,68 +429,48 @@ spawnfunc(target_assault_roundstart)
 // legacy bot code
 void havocbot_goalrating_ast_targets(entity this, float ratingscale)
 {
-       entity ad, best, wp;
-       float radius, bestvalue;
-       bool found;
-       vector p;
-
-       ad = findchain(classname, "func_assault_destructible");
-
-       for (; ad; ad = ad.chain)
+       FOREACH_ENTITY_CLASS("func_assault_destructible", it.bot_attack,
        {
-               if (ad.target == "")
-                       continue;
-
-               if (!ad.bot_attack)
+               if (it.target == "")
                        continue;
 
-               found = false;
-               FOREACH_ENTITY_STRING(targetname, ad.target,
+               bool found = false;
+               FOREACH_ENTITY_STRING(targetname, it.target,
                {
-                       if(it.classname == "target_objective_decrease")
+                       if(it.classname != "target_objective_decrease")
+                               continue;
+
+                       if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE)
                        {
-                               if(it.enemy.health > 0 && it.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)
@@ -504,14 +484,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)
@@ -687,7 +667,11 @@ MUTATOR_HOOKFUNCTION(as, OnEntityPreSpawn)
 // 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();
index 6e302a177909ba45f86578aeaea202bc1267bef4..209c8b85673b09a587379821121c72fe69daf7ca 100644 (file)
@@ -36,7 +36,15 @@ REGISTER_MUTATOR(ca, false)
                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);
+               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();
 
@@ -117,7 +125,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 +187,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);
index 0383d2e50986537500f104709d771cef84e0d625..55ec4a462ed991dbc89c7bc787bbc3bc719f60b5 100644 (file)
@@ -1528,23 +1528,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)
+       FOREACH_ENTITY_FLOAT(bot_pickup, true,
        {
                // 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)
@@ -2422,10 +2418,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;
                        }
                }
 
@@ -2608,27 +2604,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)
+               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);
        }
 
index 7acddd32dfe1e75533eb7b8a6ad8ca30d6f45252..3f75db9f387c320ab72a94840fa1badd366138a7 100644 (file)
@@ -600,7 +600,7 @@ spawnfunc(dom_team)
 }
 
 // scoreboard setup
-void ScoreRules_dom(float teams)
+void ScoreRules_dom(int teams)
 {
        if(domination_roundbased)
        {
@@ -684,7 +684,14 @@ void dom_DelayedInit(entity this) // Do this check with a delay so we can wait f
        }
 
        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;
 
index 4f8f0fde7bf99406ec137d8df295edacc73c94ee..93547989097eb43aad853fa417268146eb14d1e9 100644 (file)
@@ -62,7 +62,7 @@ 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 +91,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 +110,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);
@@ -617,6 +623,14 @@ void freezetag_Initialize()
        if(freezetag_teams < 2)
                freezetag_teams = autocvar_g_freezetag_teams;
        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);
index 9d31506e6dfe77134f2dcf8fb225fa1e2a53f475..b1a74f2aa998c92e28614b46905f4e9479eb8ca2 100644 (file)
@@ -193,7 +193,11 @@ float 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);
@@ -203,23 +207,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)
        {
@@ -272,7 +274,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)
        {
@@ -479,7 +485,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);
@@ -491,7 +497,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;
 
index ab23b686face51b85fe4d849b9eefb35bb5148a7..1ffe6e60c07518e44d625b9551a7ac1db74db995 100644 (file)
@@ -144,7 +144,7 @@ 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);
@@ -959,9 +959,15 @@ 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;
+                       int missing_teams_mask = 0;
+                       if(kh_teams & BIT(0))
+                               missing_teams_mask += boolean(p1) * 1;
+                       if(kh_teams & BIT(1))
+                               missing_teams_mask += boolean(p2) * 2;
+                       if(kh_teams & BIT(2))
+                               missing_teams_mask += boolean(p3) * 4;
+                       if(kh_teams & BIT(3))
+                               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);
@@ -1056,6 +1062,14 @@ void kh_Initialize()  // sets up th KH environment
                kh_teams = autocvar_g_keyhunt_teams;
        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);
index 3043d47145d0dceb7ab99d30274fec3cf72046b3..63fbd15a272476c1184fd26786daf3ce478b5d9c 100644 (file)
@@ -463,6 +463,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
index 0d516b262623c6f5489d665977ed68c34b7f312a..0c7c87c030644f24c062a06bd1a5158f05c788e4 100644 (file)
@@ -58,12 +58,12 @@ spawnfunc(tdm_team)
 }
 
 // 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_pure(tdm_team);
        this.netname = teamname;
-       this.cnt = teamcolor;
-       this.team = this.cnt + 1;
+       this.cnt = teamcolor - 1;
+       this.team = teamcolor;
        this.spawnfunc_checked = true;
        //spawnfunc_tdm_team(this);
 }
@@ -75,14 +75,25 @@ void tdm_DelayedInit(entity this)
        {
                LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway.\n");
 
-               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);
        }
 }
 
index b6e1350789c3904bcd1e7f97a0bee5db9d82c70b..0a28f206d0b4cb8e0e9ceab1c0f9738903741b47 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
@@ -218,9 +214,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 +222,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;
 }
@@ -432,7 +420,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;
 
@@ -561,7 +549,7 @@ entity pathlib_astar(entity this, vector from,vector to)
             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 bestopen searched: ", ftos(pathlib_bestopen_searched),"\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");
index 35dcce5b25030cad21e169218900308c5c1f6f55..a2aaf55635d119e08959f93ae9f3a6292834a989 100644 (file)
@@ -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);
@@ -167,20 +162,18 @@ entity pathlib_waypointpath(entity wp_from, entity wp_to, float callback)
     LOG_TRACE("pathlib_waypointpath init\n");
 
     // 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;
index a5f36da4a349d0059bfdc7846e98409847951b52..339a2e7e0e43c73346741e002c58621874ed9864 100644 (file)
@@ -46,7 +46,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;
index aa108dd985d9622e167a3db2649d08fec0dfc0d4..89815b71b132af3f5f3a9a122aec0defbf693c27 100644 (file)
@@ -203,7 +203,7 @@ bool ScoreInfo_SendEntity(entity this, entity to, int sf)
        return true;
 }
 
-void ScoreInfo_Init(float teams)
+void ScoreInfo_Init(int teams)
 {
        if(scores_initialized)
        {
@@ -214,13 +214,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");
 }
 
index 72eeb9a1cccaf10d4ce3142a28a69cee0139ca53..b93c114c06eb40f1c18fa829d3196a93a32ac795 100644 (file)
@@ -3,12 +3,25 @@
 #include "cl_client.qh"
 #include "scores.qh"
 
+int ScoreRules_teams;
+
 void CheckAllowedTeams (entity for_whom);
 
+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: SP_ constants may not be >= MAX_SCORE; 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)
@@ -44,7 +57,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 ef95e7b64d64e6687788e9c88d86e8f795ea06f8..8064cd9ae12ecde6f8672058a7cfb798520e6d13 100644 (file)
@@ -579,8 +579,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 +596,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 +617,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;
 }
index bf24e09b71f350ed58210f1a98c8363a5a777ea1..a10edf45b4173a8a01df3b1632aa0578430ca027 100644 (file)
@@ -153,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), sys_phys_update(it, frametime));
     FOREACH_ENTITY_CLASS(STR_PLAYER, 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;
index e24a790a148a2cdebbe4bf3c40302178e6ee6adb..32024aec5f258fe6c0615652d4838b2bc7357b98 100644 (file)
@@ -10,7 +10,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
index 3dda784eb3d38bfddb6b68f3fa2abbc2cbe2f8ef..e2156705a03b0cd1de67161f0b61ed6ba7d8eb42 100644 (file)
@@ -219,12 +219,10 @@ void CheckAllowedTeams (entity for_whom)
 
        if(!mutator_returnvalue)
        {
-               if(dm >= 4)
-                       c1 = c2 = c3 = c4 = 0;
-               else if(dm >= 3)
-                       c1 = c2 = c3 = 0;
-               else
-                       c1 = c2 = 0;
+               if(dm & BIT(0)) c1 = 0;
+               if(dm & BIT(1)) c2 = 0;
+               if(dm & BIT(2)) c3 = 0;
+               if(dm & BIT(3)) c4 = 0;
        }
 
        // find out what teams are allowed if necessary
@@ -246,24 +244,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 +446,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();
@@ -461,7 +485,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))
index 5c64a0ec64d3ff6e89f5d7f2efbf8d0ac7383355..6e7279618cc497a2b0ae99d6baa6785a112851bd 100644 (file)
@@ -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);
                });
        }
 
@@ -492,9 +492,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 32e9a5670299b9dc00de52256f5081c3b77d49ca..7c8061e94e02d24cfd2e08510983296e89ea0ee1 100644 (file)
@@ -391,7 +391,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
                        ;
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.