Merge branch 'master' into martin-t/shuffleteams 393/head
authorMario <mario@smbclan.net>
Sat, 31 Dec 2016 01:29:51 +0000 (11:29 +1000)
committerMario <mario@smbclan.net>
Sat, 31 Dec 2016 01:29:51 +0000 (11:29 +1000)
138 files changed:
.gitlab-ci.yml
_hud_common.cfg
bal-wep-xonotic.cfg
bal-wep-xpm.cfg
balance-mario.cfg
balance-nexuiz25.cfg
balance-overkill.cfg
balance-samual.cfg
balance-xdf.cfg
balance-xonotic.cfg
balance-xpm.cfg
defaultXonotic.cfg
gamemodes.cfg
mutators.cfg
notifications.cfg
qcsrc/client/defs.qh
qcsrc/client/hud/panel/infomessages.qc
qcsrc/client/hud/panel/modicons.qc
qcsrc/client/hud/panel/powerups.qc
qcsrc/client/hud/panel/radar.qc
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/client/hud/panel/timer.qc
qcsrc/client/miscfunctions.qh
qcsrc/client/mutators/events.qh
qcsrc/client/shownames.qc
qcsrc/client/teamradar.qc
qcsrc/client/view.qc
qcsrc/common/constants.qh
qcsrc/common/ent_cs.qc
qcsrc/common/ent_cs.qh
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
qcsrc/common/items/all.qc
qcsrc/common/items/all.qh
qcsrc/common/items/inventory.qh
qcsrc/common/items/item.qh
qcsrc/common/items/item/armor.qh
qcsrc/common/items/item/health.qh
qcsrc/common/items/item/powerup.qh
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc
qcsrc/common/mutators/mutator/buffs/all.inc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
qcsrc/common/mutators/mutator/instagib/items.qh
qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
qcsrc/common/mutators/mutator/itemstime/itemstime.qc
qcsrc/common/mutators/mutator/midair/sv_midair.qc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/nix/sv_nix.qc
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc
qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
qcsrc/common/mutators/mutator/waypoints/all.inc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc
qcsrc/common/notifications/all.inc
qcsrc/common/physics/player.qc
qcsrc/common/sounds/all.inc
qcsrc/common/stats.qh
qcsrc/common/t_items.qc
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/turrets/turret/hk_weapon.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/vehicle/bumblebee.qc
qcsrc/common/vehicles/vehicle/racer.qc
qcsrc/common/vehicles/vehicle/raptor.qc
qcsrc/common/vehicles/vehicle/spiderbot.qc
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/ecs/lib.qh
qcsrc/lib/csqcmodel/cl_player.qc
qcsrc/lib/vector.qh
qcsrc/menu/xonotic/credits.qc
qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc
qcsrc/menu/xonotic/dialog_multiplayer_create.qc
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc
qcsrc/server/_all.inc
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/bot/api.qh
qcsrc/server/bot/default/bot.qc
qcsrc/server/bot/default/bot.qh
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/bot/default/havocbot/roles.qc
qcsrc/server/bot/default/navigation.qc
qcsrc/server/bot/default/scripting.qc
qcsrc/server/bot/default/waypoints.qc
qcsrc/server/cheats.qc
qcsrc/server/client.qc
qcsrc/server/command/cmd.qh
qcsrc/server/command/getreplies.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/compat/_mod.inc [new file with mode: 0644]
qcsrc/server/compat/_mod.qh [new file with mode: 0644]
qcsrc/server/compat/halflife.qc [new file with mode: 0644]
qcsrc/server/compat/halflife.qh [new file with mode: 0644]
qcsrc/server/compat/quake.qc [new file with mode: 0644]
qcsrc/server/compat/quake.qh [new file with mode: 0644]
qcsrc/server/compat/quake3.qc [new file with mode: 0644]
qcsrc/server/compat/quake3.qh [new file with mode: 0644]
qcsrc/server/compat/wop.qc [new file with mode: 0644]
qcsrc/server/compat/wop.qh [new file with mode: 0644]
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_world.qc
qcsrc/server/impulse.qc
qcsrc/server/mapvoting.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/events.qh
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_ctf.qh
qcsrc/server/mutators/mutator/gamemode_cts.qc
qcsrc/server/mutators/mutator/gamemode_invasion.qc
qcsrc/server/mutators/mutator/gamemode_keepaway.qc
qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
qcsrc/server/mutators/mutator/gamemode_lms.qc
qcsrc/server/mutators/mutator/gamemode_lms.qh
qcsrc/server/player.qc
qcsrc/server/portals.qc
qcsrc/server/race.qc
qcsrc/server/round_handler.qc
qcsrc/server/t_halflife.qc [deleted file]
qcsrc/server/t_halflife.qh [deleted file]
qcsrc/server/t_quake.qc [deleted file]
qcsrc/server/t_quake.qh [deleted file]
qcsrc/server/t_quake3.qc [deleted file]
qcsrc/server/t_quake3.qh [deleted file]
qcsrc/server/teamplay.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/weaponsystem.qc

index 3c4126b..39414d1 100644 (file)
@@ -30,7 +30,7 @@ test_sv_game:
     - wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache
     - wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired
     - make
-    - EXPECT=93decd0a82cf911f02fb5572197db7f3
+    - EXPECT=37be9dc5489925451eb497390fdf58b9
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index c7befdd..6e88873 100644 (file)
@@ -97,7 +97,7 @@ seta hud_panel_physics_speed_unit "1" "speed unit (1 = qu/s, 2 = m/s, 3 = km/h,
 
 seta hud_panel_itemstime_progressbar_maxtime "30" "when left time is at least this amount, the status bar is full"
 seta hud_panel_itemstime_hidespawned "1" "if 1 hide an item from the panel when all the occurrences of it are available again; if 2 hide it when at least one occurrence is available again"
-seta hud_panel_itemstime_hidelarge "0" "if 1 hide large armor and health from the panel"
+seta hud_panel_itemstime_hidebig "0" "if 1 hide big armor and health from the panel"
 
 seta hud_panel_quickmenu_file "" "load the quick menu from this file (empty or 0 to disable)"
 seta hud_panel_quickmenu_translatecommands 0 "when the game is translated, translate strings inside commands too (useful for chat commands)"
@@ -108,7 +108,7 @@ seta hud_panel_infomessages_group_time 6 "number of seconds a message of a group
 seta hud_panel_infomessages_group_fadetime 0.4 "group message fade in/out time"
 
 seta hud_panel_scoreboard_namesize 15 "size limit of player names and relative column (multiplied by fontsize)"
-seta hud_panel_scoreboard_maxheight 0.5 "max height of the scoreboard; a few players that wouldn't fit into the scoreboard are listed in the last row"
+seta hud_panel_scoreboard_maxheight 0.6 "max height of the scoreboard; a few players that wouldn't fit into the scoreboard are listed in the last row"
 seta hud_panel_scoreboard_others_showscore 1 "show scores of players listed in the last row when the scoreboard reaches the max height"
 seta hud_panel_scoreboard_spectators_showping 1 "show ping of spectators"
 seta hud_panel_scoreboard_spectators_aligned 0 "align spectators in columns"
index 9e1f4cb..57b632b 100644 (file)
@@ -456,7 +456,7 @@ set g_balance_vaporizer_switchdelay_raise 0.2
 set g_balance_vaporizer_weaponreplace ""
 set g_balance_vaporizer_weaponstart 0
 set g_balance_vaporizer_weaponstartoverride -1
-set g_balance_vaporizer_weaponthrowable 0
+set g_balance_vaporizer_weaponthrowable 1
 // }}}
 // {{{ #13: Grappling Hook
 set g_balance_hook_primary_ammo 5
index 9e1f4cb..57b632b 100644 (file)
@@ -456,7 +456,7 @@ set g_balance_vaporizer_switchdelay_raise 0.2
 set g_balance_vaporizer_weaponreplace ""
 set g_balance_vaporizer_weaponstart 0
 set g_balance_vaporizer_weaponstartoverride -1
-set g_balance_vaporizer_weaponthrowable 0
+set g_balance_vaporizer_weaponthrowable 1
 // }}}
 // {{{ #13: Grappling Hook
 set g_balance_hook_primary_ammo 5
index 022d62e..d290be0 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
index ebce2fa..39405f3 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 0
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 999
 set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 999
-set g_pickup_armorlarge_anyway 0
+set g_pickup_armormega 100
+set g_pickup_armormega_max 999
+set g_pickup_armormega_anyway 0
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 999
 set g_pickup_healthsmall_anyway 0
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 999
 set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 999
-set g_pickup_healthlarge_anyway 0
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 999
+set g_pickup_healthbig_anyway 0
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 999
 set g_pickup_healthmega_anyway 0
index c552419..da17e0a 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 100
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 100
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 100
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 0
index 58a0f37..ad71922 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
index ff2ec18..79344fd 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
index e9fb999..f317d2e 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 200
 set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 1
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 1
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 200
 set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 200
+set g_pickup_healthbig_anyway 1
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 1
index 0c24cf2..f5ec292 100644 (file)
@@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 0
 set g_pickup_armorbig 50
 set g_pickup_armorbig_max 100
 set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 0
+set g_pickup_armormega 100
+set g_pickup_armormega_max 200
+set g_pickup_armormega_anyway 0
 set g_pickup_healthsmall 5
 set g_pickup_healthsmall_max 200
 set g_pickup_healthsmall_anyway 0
 set g_pickup_healthmedium 25
 set g_pickup_healthmedium_max 100
 set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 100
-set g_pickup_healthlarge_anyway 0
+set g_pickup_healthbig 50
+set g_pickup_healthbig_max 100
+set g_pickup_healthbig_anyway 0
 set g_pickup_healthmega 100
 set g_pickup_healthmega_max 200
 set g_pickup_healthmega_anyway 0
index 8ed6aa1..a5d81ad 100644 (file)
@@ -382,17 +382,17 @@ set bot_ai_friends_aware_pickup_radius "500"      "Bots will not pickup items if a te
 set bot_ai_ignoregoal_timeout 3        "Ignore goals making bots to get stuck in front of a wall for N seconds"
 set bot_ai_bunnyhop_skilloffset 7      "Bots with skill equal or greater than this value will perform the  \"bunnyhop\" technique"
 set bot_ai_bunnyhop_startdistance 200 "Run to goals located further than this distance"
-set bot_ai_bunnyhop_stopdistance 200 "Stop jumping after reaching this distance to the goal"
+set bot_ai_bunnyhop_stopdistance 300 "Stop jumping after reaching this distance to the goal"
 set bot_ai_bunnyhop_firstjumpdelay 0.2 "Start running to the goal only if it was seen for more than N seconds"
 set bot_god 0 "god mode for bots"
 set bot_ai_navigation_jetpack 0 "Enable bots to navigate maps using the jetpack"
 set bot_ai_navigation_jetpack_mindistance 3500 "Bots will try fly to objects located farther than this distance"
 // Better don't touch these, there are hard to tweak!
 set bot_ai_aimskill_order_mix_1st 0.01 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 1st filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 2nd filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 3th filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 4th filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 5th filter output to apply to the aiming angle"
 set bot_ai_aimskill_order_filter_1st 0.4 "Position filter"
 set bot_ai_aimskill_order_filter_2nd 0.4 "Movement filter"
 set bot_ai_aimskill_order_filter_3th 0.2 "Acceleration filter"
@@ -1072,7 +1072,7 @@ alias gl_flashblend_update "_gl_flashblend_update_$r_shadow_realtime_dlight$r_sh
 
 set sv_clones 0        "number of clones a player may make (reset by the \"kill\" command)"
 
-set cl_handicap 1      "the higher, the more damage you will receive (client setting) NOTE: reconnect or use sendcvar command to update the choice."
+set cl_handicap 1      "multiplies damage received and divides damage dealt NOTE: reconnect or use 'sendcvar cl_handicap' to update the choice."
 
 seta cl_clippedspectating 1 "movement collision for spectators so that you can't pass through walls and such. (client setting) NOTE: reconnect or use sendcvar command to update the choice."
 
index 71f0dc1..630eb7a 100644 (file)
@@ -18,7 +18,7 @@ alias asay_drop "say_team (%l) dropped %w ; impulse 17"
 // =================
 //  gamestart hooks
 // =================
-seta cl_matchcount 0 // incremented by cl_hook_gameend and used by playerstats to know when to 
+seta cl_matchcount 0 // incremented by cl_hook_gameend and used by playerstats to know when to
 alias _cl_hook_gamestart "set _cl_hook_gametype $1; _cl_hook_gamestart_stage2"
 alias _cl_hook_gamestart_stage2 "cl_hook_gamestart_all; cl_hook_gamestart_${_cl_hook_gametype}"
 alias cl_hook_gamestart_all
@@ -278,7 +278,7 @@ set g_ctf_dropped_capture_delay 1 "dropped capture delay"
 set g_ctf_dropped_capture_radius 100 "allow dropped flags to be automatically captured by base flags if the dropped flag is within this radius of it"
 set g_ctf_flag_damageforcescale 2
 set g_ctf_portalteleport 0 "allow flag carriers to go through portals made in portal gun without dropping the flag"
-set g_ctf_reverse 0 "if enabled, flags positions are switched: you have to capture the enemy's flag from your own base by bringing it to your own flag in the enemy base"
+set g_ctf_reverse 0 "if enabled, you score by bringing your own flag to an enemy's flag in their base"
 set g_ctf_flag_collect_delay 1
 set g_ctf_flag_health 0
 set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players"
index 0031904..cd6a9e2 100644 (file)
@@ -53,7 +53,7 @@ set g_overkill_powerups_replace 1
 set g_overkill_filter_healthmega 0
 set g_overkill_filter_armormedium 0
 set g_overkill_filter_armorbig 0
-set g_overkill_filter_armorlarge 0
+set g_overkill_filter_armormega 0
 
 set g_overkill_ammo_charge 0
 set g_overkill_ammo_charge_notice 1
@@ -304,7 +304,7 @@ set g_new_toys_use_pickupsound 1 "play the 'new toys, new toys!' roflsound when
 //  buffs
 // =======
 set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another"
-set g_buffs 0 "enable buffs (requires buff items or powerups)"
+set g_buffs -1 "enable buffs (requires buff items or powerups)"
 set g_buffs_effects 1 "show particle effects from carried buffs"
 set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item"
 set g_buffs_randomize 1 "randomize buff type when player drops buff"
@@ -312,7 +312,7 @@ set g_buffs_random_lifetime 30 "re-spawn the buff again if it hasn't been touche
 set g_buffs_random_location 0 "randomize buff location on start and when reset"
 set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up"
 set g_buffs_spawn_count 0 "how many buffs to spawn on the map if none exist already"
-set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs"
+set g_buffs_replace_powerups 0 "replace powerups on the map with random buffs"
 set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated"
 set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading"
 set g_buffs_ammo 1 "ammo buff: infinite ammunition"
@@ -377,6 +377,8 @@ set g_buffs_luck 1 "luck buff: randomly increased damage"
 set g_buffs_luck_time 60 "luck buff carry time"
 set g_buffs_luck_chance 0.15 "chance for 'critical' hit (multiplied damage) with luck buff"
 set g_buffs_luck_damagemultiplier 3 "luck damage multiplier"
+set g_buffs_flight 0 "flight buff: crouch jump to reverse your gravity!"
+set g_buffs_flight_time 60 "flight buff carry time"
 
 
 // ==============
index 8583592..8ca401b 100644 (file)
@@ -424,10 +424,11 @@ seta notification_INFO_WEAPON_TUBA_SUICIDE "1" "0 = off, 1 = print to console, 2
 seta notification_INFO_WEAPON_VAPORIZER_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_WEAPON_VORTEX_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 
-// MSG_CENTER notifications (count = 230):
+// MSG_CENTER notifications (count = 231):
 seta notification_CENTER_ALONE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_ASSAULT_ATTACKING "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_ASSAULT_DEFENDING "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_ASSAULT_OBJ_DESTROYED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_CAMPCHECK "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_COINTOSS "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_COUNTDOWN_BEGIN "1" "0 = off, 1 = centerprint"
@@ -886,4 +887,4 @@ seta notification_show_sprees_info "3" "Show spree information in MSG_INFO messa
 seta notification_show_sprees_info_newline "1" "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
 seta notification_show_sprees_info_specialonly "1" "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
 
-// Notification counts (total = 820): MSG_ANNCE = 89, MSG_INFO = 320, MSG_CENTER = 230, MSG_MULTI = 153, MSG_CHOICE = 28
+// Notification counts (total = 821): MSG_ANNCE = 89, MSG_INFO = 320, MSG_CENTER = 231, MSG_MULTI = 153, MSG_CHOICE = 28
index 437e1dd..309ed1d 100644 (file)
@@ -120,7 +120,7 @@ int serverflags;
 
 float uid2name_dialog;
 
-float gameover_time;
+float intermission_time;
 
 .bool csqcmodel_isdead; // used by shownames and miscfunctions (entcs_IsDead) to know when a player is dead
 
index cd49f09..2d6c952 100644 (file)
@@ -114,7 +114,9 @@ void HUD_InfoMessages()
                                InfoMessage(s);
                        }
 
-                       if(gametype == MAPINFO_TYPE_LMS)
+                       MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize);
+
+                       if(!warmup_stage && gametype == MAPINFO_TYPE_LMS)
                        {
                                entity sk;
                                sk = playerslots[player_localnum];
index c3310b2..bf302fd 100644 (file)
@@ -283,8 +283,9 @@ void HUD_Mod_KH(vector pos, vector mySize)
        mod_active = 1; // keyhunt should never hide the mod icons panel
 
        // Read current state
-
        int state = STAT(KH_KEYS);
+       if(!state) return;
+
        int i, key_state;
        int all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
        all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
@@ -315,9 +316,7 @@ void HUD_Mod_KH(vector pos, vector mySize)
        }
 
        // Calculate slot measurements
-
        vector slot_size;
-
        if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x)
        {
                // Quadratic arrangement
@@ -347,11 +346,10 @@ void HUD_Mod_KH(vector pos, vector mySize)
 
        // Make icons blink in case of RUN HERE
 
-       float blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1
-       float alpha;
-       alpha = 1;
-
+       float alpha = 1;
        if(carrying_keys)
+       {
+               float blink = 0.6 + sin(2 * M_PI * time) * 0.4; // Oscillate between 0.2 and 1
                switch(myteam)
                {
                        case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break;
@@ -359,6 +357,7 @@ void HUD_Mod_KH(vector pos, vector mySize)
                        case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break;
                        case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break;
                }
+       }
 
        // Draw icons
 
index 7c454f9..076ce91 100644 (file)
@@ -63,7 +63,7 @@ void HUD_Powerups()
 {
        int allItems = STAT(ITEMS);
        int allBuffs = STAT(BUFFS);
-       int strengthTime, shieldTime, superTime;
+       float strengthTime, shieldTime, superTime;
 
        // Initialize items
        if(!autocvar__hud_configure)
@@ -72,7 +72,7 @@ void HUD_Powerups()
                        return;
                if(STAT(HEALTH) <= 0 && autocvar_hud_panel_powerups_hide_ondeath)
                        return;
-               if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return;
+               //if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return;
 
                strengthTime = bound(0, STAT(STRENGTH_FINISHED) - time, 99);
                shieldTime = bound(0, STAT(INVINCIBLE_FINISHED) - time, 99);
index 269d870..3bc537c 100644 (file)
@@ -312,10 +312,10 @@ void HUD_Radar()
        else
        {
                vector c0, c1, c2, c3, span;
-               c0 = rotate(mi_min, teamradar_angle * DEG2RAD);
-               c1 = rotate(mi_max, teamradar_angle * DEG2RAD);
-               c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
-               c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
+               c0 = Rotate(mi_min, teamradar_angle * DEG2RAD);
+               c1 = Rotate(mi_max, teamradar_angle * DEG2RAD);
+               c2 = Rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
+               c3 = Rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
                span = '0 0 0';
                span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x);
                span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y);
index 934fe71..35de219 100644 (file)
@@ -59,7 +59,7 @@ bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
 
 bool autocvar_hud_panel_scoreboard_dynamichud = false;
 
-float autocvar_hud_panel_scoreboard_maxheight = 0.5;
+float autocvar_hud_panel_scoreboard_maxheight = 0.6;
 bool autocvar_hud_panel_scoreboard_others_showscore = true;
 bool autocvar_hud_panel_scoreboard_spectators_showping = true;
 bool autocvar_hud_panel_scoreboard_spectators_aligned = false;
@@ -670,7 +670,7 @@ string Scoreboard_GetField(entity pl, PlayerScoreField field)
                case SP_DMG: case SP_DMGTAKEN:
                        return sprintf("%.1f k", pl.(scores(field)) / 1000);
 
-               default:
+               default: case SP_SCORE:
                        tmp = pl.(scores(field));
                        f = scores_flags(field);
                        if(field == ps_primary)
@@ -951,7 +951,7 @@ vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity
                                field = Scoreboard_GetField(pl, SP_PING);
                }
                else if(autocvar_hud_panel_scoreboard_others_showscore)
-                       field = ftos(pl.(scores(ps_primary)));
+                       field = Scoreboard_GetField(pl, SP_SCORE);
 
                string str = textShortenToWidth(entcs_GetName(pl.sv_entnum), namesize, hud_fontsize, stringwidth_colors);
                float column_width = stringwidth(str, true, hud_fontsize);
@@ -1009,10 +1009,16 @@ vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
        int max_players = 999;
        if(autocvar_hud_panel_scoreboard_maxheight > 0)
        {
-               max_players = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
+               float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
                if(teamplay)
-                       max_players = (max_players - hud_fontsize.y * 1.25 - panel_bg_padding * 2) / 2;
-               max_players = floor(max_players / (hud_fontsize.y * 1.25));
+               {
+                       height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
+                       height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
+                       height /= team_count;
+               }
+               else
+                       height -= panel_bg_padding * 2; // - padding
+               max_players = floor(height / (hud_fontsize.y * 1.25));
                if(max_players <= 1)
                        max_players = 1;
                if(max_players == tm.team_size)
index fab0392..45f46b4 100644 (file)
@@ -50,15 +50,15 @@ void HUD_Timer()
        }
 
        vector timer_color;
-       if(gameover_time || minutesLeft >= 5 || warmup_stage || timelimit == 0)
+       if(intermission_time || minutesLeft >= 5 || warmup_stage || timelimit == 0)
                timer_color = '1 1 1'; //white
        else if(minutesLeft >= 1)
                timer_color = '1 1 0'; //yellow
        else
                timer_color = '1 0 0'; //red
 
-       if (gameover_time) {
-               timer = seconds_tostring(max(0, floor(gameover_time - STAT(GAMESTARTTIME))));
+       if (intermission_time) {
+               timer = seconds_tostring(max(0, floor(intermission_time - STAT(GAMESTARTTIME))));
        } else if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) {
                if (time < STAT(GAMESTARTTIME))
                        timer = seconds_tostring(0); //while restart is still active, show 00:00
index 9d792f6..62de456 100644 (file)
@@ -31,7 +31,7 @@ vector HUD_GetFontsize(string cvarname);
 
 float PreviewExists(string name);
 
-vector rotate(vector v, float a);
+vector Rotate(vector v, float a);
 
 
 #define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : ((s).health <= 0))
index 74090e1..edd1781 100644 (file)
@@ -157,3 +157,10 @@ MUTATOR_HOOKABLE(DrawCrosshair, EV_NO_ARGS);
 
 /** Return true to not draw scoreboard */
 MUTATOR_HOOKABLE(DrawScoreboard, EV_NO_ARGS);
+
+/** Called when drawing info messages, allows adding new info messages */
+#define EV_DrawInfoMessages(i, o) \
+       /** pos */                          i(vector, MUTATOR_ARGV_0_vector) \
+       /** mySize */                   i(vector, MUTATOR_ARGV_1_vector) \
+       /**/
+MUTATOR_HOOKABLE(DrawInfoMessages, EV_DrawInfoMessages);
index 76125e6..e46a97c 100644 (file)
@@ -49,34 +49,41 @@ void Draw_ShowNames(entity this)
                hit = !(trace_fraction < 1 && (trace_networkentity != this.sv_entnum && trace_ent.entnum != this.sv_entnum));
        }
        // handle tag fading
-       bool overlap = false;
+       int overlap = -1;
        vector o = project_3d_to_2d(this.origin + eZ * autocvar_hud_shownames_offset);
+       if (autocvar_hud_shownames_crosshairdistance)
+       {
+               float d = autocvar_hud_shownames_crosshairdistance;
+               float w = o.x - vid_conwidth / 2;
+               float h = o.y - vid_conheight / 2;
+               if (d * d > w * w + h * h) this.pointtime = time;
+               if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time)
+                       overlap = 1;
+               else if(!autocvar_hud_shownames_crosshairdistance_antioverlap)
+                       overlap = 0;
+       }
+
        float dist = vlen(this.origin - view_origin);
-       if (autocvar_hud_shownames_antioverlap)
+       if (overlap == -1 && autocvar_hud_shownames_antioverlap)
        {
                // fade tag out if another tag that is closer to you overlaps
-               LL_EACH(shownames_ent, it != this && entcs_receiver(i), {
+               entity entcs = NULL;
+               LL_EACH(shownames_ent, it != this, {
+                       entcs = entcs_receiver(i);
+                       if (!(entcs && entcs.has_sv_origin))
+                               continue;
                        vector eo = project_3d_to_2d(it.origin);
                        if (eo.z < 0 || eo.x < 0 || eo.y < 0 || eo.x > vid_conwidth || eo.y > vid_conheight) continue;
                        eo.z = 0;
                        if (vdist(((eX * o.x + eY * o.y) - eo), <, autocvar_hud_shownames_antioverlap_distance)
                            && vdist((it.origin - view_origin), <, dist))
                        {
-                               overlap = true;
+                               overlap = 1;
                                break;
                        }
                });
        }
        bool onscreen = (o.z >= 0 && o.x >= 0 && o.y >= 0 && o.x <= vid_conwidth && o.y <= vid_conheight);
-       if (autocvar_hud_shownames_crosshairdistance)
-       {
-               float d = autocvar_hud_shownames_crosshairdistance;
-               float w = o.x - vid_conwidth / 2;
-               float h = o.y - vid_conheight / 2;
-               if (d * d > w * w + h * h) this.pointtime = time;
-               if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) overlap = true;
-               else overlap = (autocvar_hud_shownames_crosshairdistance_antioverlap ? overlap : false); // override what antioverlap says unless allowed by cvar.
-       }
        if (!this.fadedelay) this.fadedelay = time + SHOWNAMES_FADEDELAY;
        if (this.csqcmodel_isdead)                                                                   // dead player, fade out slowly
        {
@@ -87,7 +94,7 @@ void Draw_ShowNames(entity this)
                this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime);
                this.fadedelay = 0;                         // reset fade in delay, enemy has left the view
        }
-       else if (overlap)                               // tag overlap detected, fade out
+       else if (overlap > 0) // tag overlap detected, fade out
        {
                this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime);
        }
@@ -193,8 +200,6 @@ void Draw_ShowNames_All()
                        it.sameteam = false;
                }
                bool dead = entcs_IsDead(i) || entcs_IsSpectating(i);
-               if(gametype == MAPINFO_TYPE_CA)
-                       dead = (dead || entcs_IsEliminated(i));
                if (!it.csqcmodel_isdead) setorigin(it, entcs.origin);
                it.csqcmodel_isdead = dead;
                Draw_ShowNames(it);
index abd700e..782776e 100644 (file)
@@ -20,7 +20,7 @@ vector teamradar_texcoord_to_2dcoord(vector in)
        vector out;
        in -= teamradar_origin3d_in_texcoord;
 
-       out = rotate(in, teamradar_angle * DEG2RAD);
+       out = Rotate(in, teamradar_angle * DEG2RAD);
        out.y = - out.y; // screen space is reversed
 
        out = out * teamradar_size;
@@ -42,7 +42,7 @@ vector teamradar_2dcoord_to_texcoord(vector in)
        out = out / teamradar_size;
 
        out_y = - out_y; // screen space is reversed
-       out = rotate(out, -teamradar_angle * DEG2RAD);
+       out = Rotate(out, -teamradar_angle * DEG2RAD);
 
        out += teamradar_origin3d_in_texcoord;
 
index 2ca5311..5f88e78 100644 (file)
@@ -747,7 +747,7 @@ bool WantEventchase(entity this)
 {
        if(autocvar_cl_orthoview)
                return false;
-       if(intermission)
+       if(STAT(GAMEOVER) || intermission)
                return true;
        if(this.viewloc)
                return true;
@@ -940,7 +940,7 @@ void HUD_Crosshair(entity this)
 {
        float f, i, j;
        vector v;
-       if(!scoreboard_active && !camera_active && intermission != 2 &&
+       if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAMEOVER) &&
                spectatee_status != -1 && !csqcplayer.viewloc && !MUTATOR_CALLHOOK(DrawCrosshair) &&
                !HUD_MinigameMenu_IsOpened() )
        {
@@ -1748,8 +1748,8 @@ void CSQC_UpdateView(entity this, float w, float h)
        if(!postinit)
                PostInit();
 
-       if(intermission && !gameover_time)
-               gameover_time = time;
+       if(intermission && !intermission_time)
+               intermission_time = time;
 
        if(intermission && !isdemo() && !(calledhooks & HOOK_END))
        {
index 14a02b4..910f34e 100644 (file)
@@ -233,7 +233,7 @@ const int SPECIES_RESERVED = 15;
 const int FRAGS_PLAYER = 0;
 const int FRAGS_SPECTATOR = -666;
 const int FRAGS_LMS_LOSER = -616;
-const int FRAGS_PLAYER_NONSOLID = -616;
+const int FRAGS_PLAYER_NONSOLID = FRAGS_LMS_LOSER;
 // we can use this frags value for both
 
 // water levels
index da53f68..5a5c6ac 100644 (file)
@@ -13,9 +13,9 @@ MACRO_END
 // #define PROP(public, fld, set, sv, cl)
 #define ENTCS_NETPROPS(ent, PROP) PROP(false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */ \
        PROP(false, origin, ENTCS_SET_NORMAL, \
-       { WriteShort(chan, ent.origin.x);  WriteShort(chan, ent.origin.y); \
-         WriteShort(chan, ent.origin.z); }, \
-       { ent.has_sv_origin = true; vector v; v.x = ReadShort(); v.y = ReadShort(); v.z = ReadShort(); setorigin(ent, v); }) \
+       { WriteCoord(chan, ent.origin.x);  WriteCoord(chan, ent.origin.y); \
+         WriteCoord(chan, ent.origin.z); }, \
+       { ent.has_sv_origin = true; vector v; v.x = ReadCoord(); v.y = ReadCoord(); v.z = ReadCoord(); setorigin(ent, v); }) \
     \
        PROP(false, angles_y, ENTCS_SET_NORMAL, \
        { WriteByte(chan, ent.angles.y / 360 * 256); }, \
@@ -73,6 +73,11 @@ MACRO_END
                entity player = this.owner;
                sf |= BIT(0); // assume private
                do {
+                       if (!(IS_PLAYER(player)))
+                       {
+                               sf &= ENTCS_PUBLICMASK; // no private updates
+                               break;
+                       }
                        if (radar_showennemies) break;
                        if (SAME_TEAM(to, player)) break;
                        if (!(IS_PLAYER(to) || to.caplayer) && time > game_starttime) break;
index ac06dc4..65cdd83 100644 (file)
@@ -69,17 +69,6 @@ REGISTER_NET_TEMP(CLIENT_ENTCS)
        /**
      * @param i zero indexed player
      */
-    .int frags;
-       bool entcs_IsEliminated(int i)
-       {
-               bool unconnected = !playerslots[i].gotscores;
-               entity e = entcs_receiver(i);
-               return unconnected || ((e) ? e.frags : stof(getplayerkeyvalue(i, "frags"))) == FRAGS_LMS_LOSER;
-       }
-
-       /**
-     * @param i zero indexed player
-     */
        int entcs_GetClientColors(int i)
        {
                entity e = entcs_receiver(i);
index 572611c..195a3bd 100644 (file)
@@ -1638,7 +1638,7 @@ bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effe
 
                loc += tele_target.origin + '0 0 128' * iteration_scale;
 
-               tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player);
+               tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player);
                if(trace_fraction == 1.0 && !trace_startsolid)
                {
                        traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL
@@ -1757,7 +1757,7 @@ MUTATOR_HOOKFUNCTION(ons, PlayerSpawn)
                                iteration_scale -= i / 10;
                                loc = closest_target.origin + '0 0 96' * iteration_scale;
                                loc += ('0 1 0' * random()) * 128 * iteration_scale;
-                               tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player);
+                               tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player);
                                if(trace_fraction == 1.0 && !trace_startsolid)
                                {
                                        traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL
@@ -1808,7 +1808,7 @@ MUTATOR_HOOKFUNCTION(ons, PlayerSpawn)
                                iteration_scale -= i / 10;
                                loc = closest_target.origin + '0 0 128' * iteration_scale;
                                loc += ('0 1 0' * random()) * 256 * iteration_scale;
-                               tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player);
+                               tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player);
                                if(trace_fraction == 1.0 && !trace_startsolid)
                                {
                                        traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL
index 7782cda..0a26a75 100644 (file)
@@ -14,3 +14,14 @@ string Item_Model(string item_mdl)
 #endif
     return output;
 }
+
+string Item_Sound(string it_snd)
+{
+       string output = strcat("misc/", it_snd);
+#ifdef SVQC
+               MUTATOR_CALLHOOK(ItemSound, it_snd, output);
+               return M_ARGV(1, string);
+#else
+               return output;
+#endif
+}
index d377776..dc8cf21 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "item.qh"
 
-REGISTRY(Items, BITS(5))
+REGISTRY(Items, BITS(7))
 #define Items_from(i) _Items_from(i, NULL)
 REGISTER_REGISTRY(Items)
 #define REGISTER_ITEM(id, class) REGISTER(Items, ITEM, id, m_id, NEW(class))
index 811f716..a022979 100644 (file)
@@ -15,17 +15,33 @@ ENDCLASS(Inventory)
 
 REGISTER_NET_LINKED(ENT_CLIENT_INVENTORY)
 
+const int Inventory_groups_major = 16;
+const int Inventory_groups_minor = 8; // ceil(Items_MAX / Inventory_groups_major)
+
+#define G_MAJOR(id) (floor((id) / Inventory_groups_minor))
+#define G_MINOR(id) ((id) % Inventory_groups_minor)
+
 #ifdef CSQC
 NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew)
 {
     make_pure(this);
-    const int bits = ReadInt24_t();
-    FOREACH(Items, bits & BIT(it.m_id), {
-        .int fld = inv_items[it.m_id];
-        int prev = this.(fld);
-        int next = this.(fld) = ReadByte();
-        LOG_TRACEF("%s: %.0f -> %.0f", it.m_name, prev, next);
-    });
+    const int majorBits = ReadShort();
+    for (int i = 0; i < Inventory_groups_major; ++i) {
+        if (!(majorBits & BIT(i))) {
+            continue;
+        }
+        const int minorBits = ReadByte();
+        for (int j = 0; j < Inventory_groups_minor; ++j) {
+            if (!(minorBits & BIT(j))) {
+                continue;
+            }
+            const GameItem it = Items_from(Inventory_groups_minor * i + j);
+            .int fld = inv_items[it.m_id];
+            int prev = this.(fld);
+            int next = this.(fld) = ReadByte();
+            LOG_TRACEF("%s: %.0f -> %.0f", it.m_name, prev, next);
+        }
+    }
     return true;
 }
 #endif
@@ -34,22 +50,56 @@ NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew)
 void Inventory_Write(Inventory data)
 {
     if (!data) {
-        WriteInt24_t(MSG_ENTITY, 0);
+        WriteShort(MSG_ENTITY, 0);
         return;
     }
     TC(Inventory, data);
-    int bits = 0;
+
+    int majorBits = 0;
     FOREACH(Items, true, {
         .int fld = inv_items[it.m_id];
-        bits = BITSET(bits, BIT(it.m_id), data.inventory.(fld) != (data.inventory.(fld) = data.(fld)));
+        const bool changed = data.inventory.(fld) != data.(fld);
+        if (changed) {
+            majorBits = BITSET(majorBits, BIT(G_MAJOR(it.m_id)), true);
+        }
     });
-    WriteInt24_t(MSG_ENTITY, bits);
-    FOREACH(Items, bits & BIT(it.m_id), {
-        WriteByte(MSG_ENTITY, data.inv_items[it.m_id]);
+    WriteShort(MSG_ENTITY, majorBits);
+
+    int minorBits = 0;
+    int lastMaj = 0;
+    int maj = 0;
+    FOREACH(Items, majorBits & BIT(maj = G_MAJOR(it.m_id)), {
+        .int fld = inv_items[it.m_id];
+        const bool changed = data.inventory.(fld) != (data.inventory.(fld) = data.(fld));
+        if (changed) {
+            if (maj != lastMaj) {
+                lastMaj = maj;
+#define X() MACRO_BEGIN \
+    if (minorBits) { \
+        WriteByte(MSG_ENTITY, minorBits); \
+        for (int j = 0; j < Inventory_groups_minor; ++j) { \
+            if (!(minorBits & BIT(j))) { \
+                continue; \
+            } \
+            const GameItem it = Items_from(Inventory_groups_minor * maj + j); \
+            WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); \
+        } \
+    } \
+MACRO_END
+                X();
+                minorBits = 0;
+            }
+            minorBits = BITSET(minorBits, BIT(G_MINOR(it.m_id)), true);
+        }
     });
+    X();
+#undef X
 }
 #endif
 
+#undef G_MAJOR
+#undef G_MINOR
+
 #ifdef SVQC
 bool Inventory_Send(Inventory this, Client to, int sf)
 {
index bac1270..f3aa0ce 100644 (file)
@@ -34,7 +34,6 @@ const int IT_SUPERWEAPON                              = BIT(21); // suit
 const int IT_STRENGTH                                  = BIT(22);
 
 // item masks
-const int IT_AMMO                              = IT_FUEL | IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_PLASMA;
 const int IT_UNLIMITED_AMMO                    = IT_UNLIMITED_WEAPON_AMMO | IT_UNLIMITED_SUPERWEAPONS;
 const int IT_PICKUPMASK                        = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FUEL_REGEN; // strength and invincible are handled separately
 
index 7946fb7..4b6b9fd 100644 (file)
@@ -15,7 +15,7 @@ ENDCLASS(Armor)
 
 #ifdef GAMEQC
 MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3"));
-SOUND(ArmorSmall, "misc/armor1");
+SOUND(ArmorSmall, Item_Sound("armor1"));
 #endif
 
 REGISTER_ITEM(ArmorSmall, Armor) {
@@ -35,7 +35,7 @@ REGISTER_ITEM(ArmorSmall, Armor) {
 
 #ifdef GAMEQC
 MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3"));
-SOUND(ArmorMedium, "misc/armor10");
+SOUND(ArmorMedium, Item_Sound("armor10"));
 #endif
 
 REGISTER_ITEM(ArmorMedium, Armor) {
@@ -54,21 +54,21 @@ REGISTER_ITEM(ArmorMedium, Armor) {
 }
 
 #ifdef GAMEQC
-MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3"));
-SOUND(ArmorLarge, "misc/armor17_5");
+MODEL(ArmorBig_ITEM, Item_Model("item_armor_big.md3"));
+SOUND(ArmorBig, Item_Sound("armor17_5"));
 #endif
 
-REGISTER_ITEM(ArmorLarge, Armor) {
+REGISTER_ITEM(ArmorBig, Armor) {
 #ifdef GAMEQC
-    this.m_model                =   MDL_ArmorLarge_ITEM;
-    this.m_sound                =   SND_ArmorLarge;
+    this.m_model                =   MDL_ArmorBig_ITEM;
+    this.m_sound                =   SND_ArmorBig;
 #endif
     this.m_name                 =   "50 Armor";
     this.m_icon                 =   "armor";
     this.m_color                =   '0 1 0';
-    this.m_waypoint             =   _("Large armor");
+    this.m_waypoint             =   _("Big armor");
 #ifdef SVQC
-    this.m_botvalue             =   20000; // FIXME: higher than BOT_PICKUP_RATING_HIGH?
+    this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
     this.m_itemid               =   IT_ARMOR;
     this.m_respawntime          =   GET(g_pickup_respawntime_long);
     this.m_respawntimejitter    =   GET(g_pickup_respawntimejitter_long);
@@ -77,7 +77,7 @@ REGISTER_ITEM(ArmorLarge, Armor) {
 
 #ifdef GAMEQC
 MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3"));
-SOUND(ArmorMega, "misc/armor25");
+SOUND(ArmorMega, Item_Sound("armor25"));
 #endif
 
 REGISTER_ITEM(ArmorMega, Armor) {
@@ -91,6 +91,7 @@ REGISTER_ITEM(ArmorMega, Armor) {
     this.m_waypoint             =   _("Mega armor");
     this.m_waypointblink        =   2;
 #ifdef SVQC
+    this.m_maxs                 =   '16 16 70';
     this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
     this.m_itemid               =   IT_ARMOR;
     this.m_respawntime          =   GET(g_pickup_respawntime_long);
index 1597ba6..f791598 100644 (file)
@@ -15,7 +15,7 @@ ENDCLASS(Health)
 
 #ifdef GAMEQC
 MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3"));
-SOUND(HealthSmall, "misc/minihealth");
+SOUND(HealthSmall, Item_Sound("minihealth"));
 #endif
 
 REGISTER_ITEM(HealthSmall, Health) {
@@ -35,7 +35,7 @@ REGISTER_ITEM(HealthSmall, Health) {
 
 #ifdef GAMEQC
 MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3"));
-SOUND(HealthMedium, "misc/mediumhealth");
+SOUND(HealthMedium, Item_Sound("mediumhealth"));
 #endif
 
 REGISTER_ITEM(HealthMedium, Health) {
@@ -54,19 +54,19 @@ REGISTER_ITEM(HealthMedium, Health) {
 }
 
 #ifdef GAMEQC
-MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3"));
-SOUND(HealthLarge, "misc/mediumhealth");
+MODEL(HealthBig_ITEM, Item_Model("g_h50.md3"));
+SOUND(HealthBig, Item_Sound("mediumhealth"));
 #endif
 
-REGISTER_ITEM(HealthLarge, Health) {
+REGISTER_ITEM(HealthBig, Health) {
 #ifdef GAMEQC
-    this.m_model                =   MDL_HealthLarge_ITEM;
-    this.m_sound                =   SND_HealthLarge;
+    this.m_model                =   MDL_HealthBig_ITEM;
+    this.m_sound                =   SND_HealthBig;
 #endif
     this.m_name                 =   "50 Health";
     this.m_icon                 =   "health";
     this.m_color                =   '1 0 0';
-    this.m_waypoint             =   _("Large health");
+    this.m_waypoint             =   _("Big health");
 #ifdef SVQC
     this.m_botvalue             =   BOT_PICKUP_RATING_MID;
     this.m_itemid               =   IT_25HP;
@@ -77,7 +77,7 @@ REGISTER_ITEM(HealthLarge, Health) {
 
 #ifdef GAMEQC
 MODEL(HealthMega_ITEM, Item_Model("g_h100.md3"));
-SOUND(HealthMega, "misc/megahealth");
+SOUND(HealthMega, Item_Sound("megahealth"));
 #endif
 
 REGISTER_ITEM(HealthMega, Health) {
@@ -91,6 +91,7 @@ REGISTER_ITEM(HealthMega, Health) {
     this.m_waypoint             =   _("Mega health");
     this.m_waypointblink        =   2;
 #ifdef SVQC
+    this.m_maxs                 =   '16 16 70';
     this.m_botvalue             =   BOT_PICKUP_RATING_HIGH;
     this.m_itemid               =   IT_HEALTH;
     this.m_respawntime          =   GET(g_pickup_respawntime_long);
index 26d649d..80b1e6a 100644 (file)
@@ -9,7 +9,7 @@
 CLASS(Powerup, Pickup)
 #ifdef SVQC
     ATTRIB(Powerup, m_mins, vector, '-16 -16 0');
-    ATTRIB(Powerup, m_maxs, vector, '16 16 48');
+    ATTRIB(Powerup, m_maxs, vector, '16 16 80');
     ATTRIB(Powerup, m_botvalue, int, 100000);
     ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
     ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
@@ -19,7 +19,7 @@ ENDCLASS(Powerup)
 
 #ifdef GAMEQC
 MODEL(Strength_ITEM, Item_Model("g_strength.md3"));
-SOUND(Strength, "misc/powerup");
+SOUND(Strength, Item_Sound("powerup"));
 #endif
 
 REGISTER_ITEM(Strength, Powerup) {
@@ -39,7 +39,7 @@ REGISTER_ITEM(Strength, Powerup) {
 
 #ifdef GAMEQC
 MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
-SOUND(Shield, "misc/powerup_shield");
+SOUND(Shield, Item_Sound("powerup_shield"));
 #endif
 
 REGISTER_ITEM(Shield, Powerup) {
index 71bca32..748f9a9 100644 (file)
@@ -451,7 +451,7 @@ METHOD(Mage, mr_anim, bool(Mage this, entity actor))
 #endif
 #ifdef SVQC
 .float speed;
-spawnfunc(item_health_large);
+spawnfunc(item_health_big);
 METHOD(Mage, mr_setup, bool(Mage this, entity actor))
 {
     TC(Mage, this);
@@ -461,7 +461,7 @@ METHOD(Mage, mr_setup, bool(Mage this, entity actor))
     if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_mage_speed_stop); }
     if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_mage_damageforcescale); }
 
-    actor.monster_loot = spawnfunc_item_health_large;
+    actor.monster_loot = spawnfunc_item_health_big;
     actor.monster_attackfunc = M_Mage_Attack;
 
     return true;
index b8c3bab..f0f789a 100644 (file)
@@ -34,7 +34,7 @@ void monster_dropitem(entity this, entity attacker)
        if(!this.candrop || !this.monster_loot)
                return;
 
-       vector org = this.origin + ((this.mins + this.maxs) * 0.5);
+       vector org = CENTER_OR_VIEWOFS(this);
        entity e = new(droppedweapon); // use weapon handling to remove it on touch
        e.spawnfunc_checked = true;
 
@@ -80,7 +80,7 @@ bool Monster_ValidTarget(entity this, entity targ)
 
        if((targ == this)
        || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
-       || (IS_VEHICLE(targ) && !((get_monsterinfo(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
+       || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
        || (time < game_starttime) // monsters do nothing before match has started
        || (targ.takedamage == DAMAGE_NO)
        || (targ.items & IT_INVISIBILITY)
@@ -107,10 +107,8 @@ bool Monster_ValidTarget(entity this, entity targ)
        if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
        if(this.enemy != targ)
        {
-               float dot;
-
                makevectors (this.angles);
-               dot = normalize (targ.origin - this.origin) * v_forward;
+               float dot = normalize (targ.origin - this.origin) * v_forward;
 
                if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
        }
@@ -118,25 +116,25 @@ bool Monster_ValidTarget(entity this, entity targ)
        return true; // this target is valid!
 }
 
-entity Monster_FindTarget(entity mon)
+entity Monster_FindTarget(entity this)
 {
-       if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator
+       if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return this.enemy; } // Handled by a mutator
 
        entity closest_target = NULL;
+       vector my_center = CENTER_OR_VIEWOFS(this);
 
        // find the closest acceptable target to pass to
-       FOREACH_ENTITY_RADIUS(mon.origin, mon.target_range, it.monster_attack,
+       FOREACH_ENTITY_RADIUS(this.origin, this.target_range, it.monster_attack,
        {
-               if(Monster_ValidTarget(mon, it))
+               if(Monster_ValidTarget(this, it))
                {
                        // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
-                       vector head_center = CENTER_OR_VIEWOFS(it);
-                       vector ent_center = CENTER_OR_VIEWOFS(mon);
+                       vector targ_center = CENTER_OR_VIEWOFS(it);
 
                        if(closest_target)
                        {
                                vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
-                               if(vlen2(ent_center - head_center) < vlen2(ent_center - closest_target_center))
+                               if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
                                        { closest_target = it; }
                        }
                        else { closest_target = it; }
@@ -146,43 +144,43 @@ entity Monster_FindTarget(entity mon)
        return closest_target;
 }
 
-void monster_setupcolors(entity mon)
+void monster_setupcolors(entity this)
 {
-       if(IS_PLAYER(mon.realowner))
-               mon.colormap = mon.realowner.colormap;
-       else if(teamplay && mon.team)
-               mon.colormap = 1024 + (mon.team - 1) * 17;
+       if(IS_PLAYER(this.realowner))
+               this.colormap = this.realowner.colormap;
+       else if(teamplay && this.team)
+               this.colormap = 1024 + (this.team - 1) * 17;
        else
        {
-               if(mon.monster_skill <= MONSTER_SKILL_EASY)
-                       mon.colormap = 1029;
-               else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM)
-                       mon.colormap = 1027;
-               else if(mon.monster_skill <= MONSTER_SKILL_HARD)
-                       mon.colormap = 1038;
-               else if(mon.monster_skill <= MONSTER_SKILL_INSANE)
-                       mon.colormap = 1028;
-               else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE)
-                       mon.colormap = 1032;
+               if(this.monster_skill <= MONSTER_SKILL_EASY)
+                       this.colormap = 1029;
+               else if(this.monster_skill <= MONSTER_SKILL_MEDIUM)
+                       this.colormap = 1027;
+               else if(this.monster_skill <= MONSTER_SKILL_HARD)
+                       this.colormap = 1038;
+               else if(this.monster_skill <= MONSTER_SKILL_INSANE)
+                       this.colormap = 1028;
+               else if(this.monster_skill <= MONSTER_SKILL_NIGHTMARE)
+                       this.colormap = 1032;
                else
-                       mon.colormap = 1024;
+                       this.colormap = 1024;
        }
 }
 
-void monster_changeteam(entity ent, float newteam)
+void monster_changeteam(entity this, int newteam)
 {
        if(!teamplay) { return; }
 
-       ent.team = newteam;
-       ent.monster_attack = true; // new team, activate attacking
-       monster_setupcolors(ent);
+       this.team = newteam;
+       this.monster_attack = true; // new team, activate attacking
+       monster_setupcolors(this);
 
-       if(ent.sprite)
+       if(this.sprite)
        {
-               WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
+               WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
 
-               ent.sprite.team = newteam;
-               ent.sprite.SendFlags |= 1;
+               this.sprite.team = newteam;
+               this.sprite.SendFlags |= 1;
        }
 }
 
@@ -295,10 +293,9 @@ void Monster_Sounds_Clear(entity this)
 
 bool Monster_Sounds_Load(entity this, string f, int first)
 {
-       float fh;
        string s;
        var .string field;
-       fh = fopen(f, FILE_READ);
+       float fh = fopen(f, FILE_READ);
        if(fh < 0)
        {
                LOG_TRACE("Monster sound file not found: ", f);
@@ -330,7 +327,7 @@ void Monster_Sounds_Update(entity this)
                Monster_Sounds_Load(this, get_monster_model_datafilename(this.model, 0, "sounds"), 0);
 }
 
-void Monster_Sound(entity this, .string samplefield, float sound_delay, float delaytoo, float chan)
+void Monster_Sound(entity this, .string samplefield, float sound_delay, bool delaytoo, float chan)
 {
        if(!autocvar_g_monsters_sounds) { return; }
 
@@ -384,7 +381,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel)
        this.velocity = vel;
        tracetoss(this, this);
        this.velocity = old;
-       if (trace_ent != this.enemy)
+       if(trace_ent != this.enemy)
                return false;
 
        return true;
@@ -397,12 +394,12 @@ bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity touch
 
        setanim(this, anm, false, true, false);
 
-       if(this.animstate_endtime > time && (this.flags & FL_MONSTER))
+       if(this.animstate_endtime > time && IS_MONSTER(this))
                this.attack_finished_single[0] = this.anim_finished = this.animstate_endtime;
        else
                this.attack_finished_single[0] = this.anim_finished = time + animtime;
 
-       if(this.flags & FL_MONSTER)
+       if(IS_MONSTER(this))
                this.state = MONSTER_ATTACK_RANGED;
        settouch(this, touchfunc);
        this.origin_z += 1;
@@ -414,14 +411,14 @@ bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity touch
 
 void Monster_Attack_Check(entity this, entity targ)
 {
-       if((this == NULL || targ == NULL)
+       if((!this || !targ)
        || (!this.monster_attackfunc)
        || (time < this.attack_finished_single[0])
        ) { return; }
 
        if(vdist(targ.origin - this.origin, <=, this.attack_range))
        {
-               bool attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ);
+               int attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ);
                if(attack_success == 1)
                        Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -430,7 +427,7 @@ void Monster_Attack_Check(entity this, entity targ)
 
        if(vdist(targ.origin - this.origin, >, this.attack_range))
        {
-               float attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ);
+               int attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ);
                if(attack_success == 1)
                        Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -458,7 +455,7 @@ void Monster_UpdateModel(entity this)
        this.anim_die2   = animfixfps(this, '9 1 0.01', '0 0 0');*/
 
        // then get the real values
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        mon.mr_anim(mon, this);
 }
 
@@ -554,7 +551,8 @@ vector Monster_Move_Target(entity this, entity targ)
                targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us)
                WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
 
-               if((this.enemy == NULL)
+               // cases where the enemy may have changed their state (don't need to check everything here)
+               if((!this.enemy)
                        || (IS_DEAD(this.enemy) || this.enemy.health < 1)
                        || (STAT(FROZEN, this.enemy))
                        || (this.enemy.flags & FL_NOTARGET)
@@ -695,8 +693,6 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        // update goal entity if lost
        if(this.target2 && this.goalentity.targetname != this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
 
-       entity targ;
-
        if(STAT(FROZEN, this) == 2)
        {
                this.revive_progress = bound(0, this.revive_progress + this.ticrate * this.revive_speed, 1);
@@ -781,7 +777,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                }
        }
 
-       targ = this.goalentity;
+       entity targ = this.goalentity;
 
        if (MUTATOR_CALLHOOK(MonsterMove, this, runspeed, walkspeed, targ)
                || gameover
@@ -802,8 +798,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        runspeed = bound(0, M_ARGV(1, float) * MONSTER_SKILLMOD(this), runspeed * 2.5); // limit maxspeed to prevent craziness
        walkspeed = bound(0, M_ARGV(2, float) * MONSTER_SKILLMOD(this), walkspeed * 2.5); // limit maxspeed to prevent craziness
 
-       if(teamplay)
-       if(autocvar_g_monsters_teams)
+       if(teamplay && autocvar_g_monsters_teams)
        if(DIFF_TEAM(this.monster_follow, this))
                this.monster_follow = NULL;
 
@@ -855,13 +850,12 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
 
        if(vdist(this.origin - this.moveto, >, 100))
        {
-               float do_run = (this.enemy || this.monster_moveto);
+               bool do_run = (this.enemy || this.monster_moveto);
                if(IS_ONGROUND(this) || ((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
                        Monster_CalculateVelocity(this, this.moveto, this.origin, true, ((do_run) ? runspeed : walkspeed));
 
-               if(time > this.pain_finished) // TODO: use anim_finished instead!
+               if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!?
                if(!this.state)
-               if(time > this.anim_finished)
                if(vdist(this.velocity, >, 10))
                        setanim(this, ((do_run) ? this.anim_run : this.anim_walk), true, false, false);
                else
@@ -876,8 +870,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                        this.target2 = e.target;
 
                movelib_brake_simple(this, stpspeed);
-               if(time > this.anim_finished)
-               if(time > this.pain_finished)
+               if(time > this.anim_finished && time > this.pain_finished)
                if(!this.state)
                if(vdist(this.velocity, <=, 30))
                        setanim(this, this.anim_idle, true, false, false);
@@ -1032,7 +1025,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
 
        CSQCModel_UnlinkEntity(this);
 
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        mon.mr_death(mon, this);
 
        if(this.candrop && this.weapon)
@@ -1056,14 +1049,11 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL)
                return;
 
-       vector v;
-       float take, save;
-
-       v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage);
-       take = v_x;
-       save = v_y;
+       vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage);
+       float take = v.x;
+       //float save = v.y;
 
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        take = mon.mr_pain(mon, this, take, attacker, deathtype);
 
        if(take)
@@ -1160,8 +1150,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
 
        movelib_move_simple_gravity(this, v_forward, mspeed, 1);
 
-       if(time > this.pain_finished)
-       if(time > this.attack_finished_single[0])
+       if(time > this.pain_finished && time > this.attack_finished_single[0])
        if(vdist(this.velocity, >, 10))
                setanim(this, this.anim_walk, true, false, false);
        else
@@ -1210,14 +1199,13 @@ void Monster_Think(entity this)
        setthink(this, Monster_Think);
        this.nextthink = time + this.ticrate;
 
-       if(this.monster_lifetime)
-       if(time >= this.monster_lifetime)
+       if(this.monster_lifetime && time >= this.monster_lifetime)
        {
                Damage(this, this, this, this.health + this.max_health, DEATH_KILL.m_id, this.origin, this.origin);
                return;
        }
 
-       Monster mon = get_monsterinfo(this.monsterid);
+       Monster mon = Monsters_from(this.monsterid);
        if(mon.mr_think(mon, this))
                Monster_Move(this, this.speed2, this.speed, this.stopspeed);
 
index fdb0dc3..c23dd24 100644 (file)
@@ -8,7 +8,7 @@ REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook"));
 bool autocvar_g_breakablehook; // allow toggling mid match?
 bool autocvar_g_breakablehook_owner;
 
-MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(breakablehook, Damage_Calculate)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
index f6c25f7..915f953 100644 (file)
@@ -116,3 +116,11 @@ REGISTER_BUFF(LUCK) {
     this.m_color = '1 0.23 0.44';
 }
 BUFF_SPAWNFUNCS(luck, BUFF_LUCK)
+
+REGISTER_BUFF(FLIGHT) {
+    this.m_prettyName = _("Flight");
+    this.m_name = "flight";
+    this.m_skin = 11;
+    this.m_color = '0.23 0.44 1';
+}
+BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT)
index 32c61eb..900d16a 100644 (file)
@@ -6,11 +6,14 @@
 .float buff_time = _STAT(BUFF_TIME);
 void buffs_DelayedInit(entity this);
 
-REGISTER_MUTATOR(buffs, cvar("g_buffs"))
+AUTOCVAR(g_buffs, int, -1, _("Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them"));
+
+REGISTER_MUTATOR(buffs, autocvar_g_buffs)
 {
        MUTATOR_ONADD
        {
-               InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
+               if(autocvar_g_buffs > 0)
+                       InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
        }
 }
 
@@ -231,6 +234,7 @@ void buff_Think(entity this)
                this.skin = buff.m_skin;
 
                setmodel(this, MDL_BUFF);
+               setsize(this, BUFF_MIN, BUFF_MAX);
 
                if(this.buff_waypoint)
                {
@@ -446,7 +450,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
        }
 }
 
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
@@ -591,6 +595,10 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
 
        if(player.buffs & BUFF_JUMP.m_itemid)
                M_ARGV(1, float) = autocvar_g_buffs_jump_height;
+
+       if(player.buffs & BUFF_FLIGHT.m_itemid)
+       if(!IS_JUMP_HELD(player) && PHYS_INPUT_BUTTON_CROUCH(player))
+               player.gravity *= -1;
 }
 
 MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
@@ -743,6 +751,9 @@ MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
 
 MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
 {
+       if(autocvar_g_buffs < 0)
+               return; // no auto replacing of entities in this mode
+
        entity ent = M_ARGV(0, entity);
 
        if(autocvar_g_buffs_replace_powerups)
@@ -898,6 +909,16 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
                BUFF_ONREM(BUFF_INVISIBLE)
                        player.alpha = player.buff_invisible_prev_alpha;
 
+               BUFF_ONADD(BUFF_FLIGHT)
+               {
+                       player.buff_flight_oldgravity = player.gravity;
+                       if(!player.gravity)
+                               player.gravity = 1;
+               }
+
+               BUFF_ONREM(BUFF_FLIGHT)
+                       player.gravity = ((player.trigger_gravity_check) ? player.trigger_gravity_check.enemy.gravity : player.buff_flight_oldgravity);
+
                player.oldbuffs = player.buffs;
                if(player.buffs)
                {
@@ -981,12 +1002,14 @@ REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace");
 
 MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
 {
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
+       if(autocvar_g_buffs > 0) // only report as a mutator if they're enabled
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
 }
 
 MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
 {
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
+       if(autocvar_g_buffs > 0)
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
 }
 
 void buffs_DelayedInit(entity this)
index b9fc1e4..8383a72 100644 (file)
@@ -59,6 +59,8 @@ float autocvar_g_buffs_luck_damagemultiplier = 3;
 // disability
 .float buff_disability_time;
 .float buff_disability_effect_time;
+// flight
+.float buff_flight_oldgravity;
 // common buff variables
 .float buff_effect_delay;
 
@@ -70,8 +72,8 @@ float autocvar_g_buffs_luck_damagemultiplier = 3;
 .int oldbuffs; // for updating effects
 .entity buff_model; // controls effects (TODO: make csqc)
 
-const vector BUFF_MIN = ('-16 -16 -20');
-const vector BUFF_MAX = ('16 16 20');
+const vector BUFF_MIN = ('-16 -16 0');
+const vector BUFF_MAX = ('16 16 60');
 
 // client side options
 .float cvar_cl_buffs_autoreplace;
index c4d2e03..36da631 100644 (file)
@@ -16,7 +16,7 @@ MUTATOR_HOOKFUNCTION(campcheck, PlayerDies)
        Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK);
 }
 
-MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(campcheck, Damage_Calculate)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
index 7736fa7..1efa4cb 100644 (file)
@@ -11,7 +11,7 @@ GETTER(float, instagib_respawntimejitter_ammo)
 
 #ifdef GAMEQC
 MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3"));
-SOUND(VaporizerCells, "misc/itempickup");
+SOUND(VaporizerCells, Item_Sound("itempickup"));
 #endif
 
 REGISTER_ITEM(VaporizerCells, Ammo) {
@@ -31,7 +31,7 @@ REGISTER_ITEM(VaporizerCells, Ammo) {
 
 #ifdef GAMEQC
 MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3"));
-SOUND(ExtraLife, "misc/megahealth");
+SOUND(ExtraLife, Item_Sound("megahealth"));
 #endif
 
 REGISTER_ITEM(ExtraLife, Powerup) {
@@ -49,7 +49,7 @@ REGISTER_ITEM(ExtraLife, Powerup) {
 
 #ifdef GAMEQC
 MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
-SOUND(Invisibility, "misc/powerup");
+SOUND(Invisibility, Item_Sound("powerup"));
 #endif
 
 REGISTER_ITEM(Invisibility, Powerup) {
@@ -67,7 +67,7 @@ REGISTER_ITEM(Invisibility, Powerup) {
 
 #ifdef GAMEQC
 MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
-SOUND(Speed, "misc/powerup_shield");
+SOUND(Speed, Item_Sound("powerup_shield"));
 #endif
 
 REGISTER_ITEM(Speed, Powerup) {
index 4c6dc15..88bb8c0 100644 (file)
@@ -257,7 +257,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
index b76d22e..3db867a 100644 (file)
@@ -49,18 +49,18 @@ float autocvar_hud_panel_itemstime_progressbar_maxtime = 30;
 string autocvar_hud_panel_itemstime_progressbar_name = "progressbar";
 float autocvar_hud_panel_itemstime_progressbar_reduced;
 bool autocvar_hud_panel_itemstime_hidespawned = 1;
-bool autocvar_hud_panel_itemstime_hidelarge = false;
+bool autocvar_hud_panel_itemstime_hidebig = false;
 int autocvar_hud_panel_itemstime_text = 1;
-#define hud_panel_itemstime_hidelarge autocvar_hud_panel_itemstime_hidelarge
+#define hud_panel_itemstime_hidebig autocvar_hud_panel_itemstime_hidebig
 #else
-#define hud_panel_itemstime_hidelarge false
+#define hud_panel_itemstime_hidebig false
 #endif
 
 bool Item_ItemsTime_SpectatorOnly(GameItem it)
 {
     return (false
-    || it == ITEM_ArmorMega     || (it == ITEM_ArmorLarge && !hud_panel_itemstime_hidelarge)
-    || it == ITEM_HealthMega    || (it == ITEM_HealthLarge && !hud_panel_itemstime_hidelarge)
+    || it == ITEM_ArmorMega     || (it == ITEM_ArmorBig && !hud_panel_itemstime_hidebig)
+    || it == ITEM_HealthMega    || (it == ITEM_HealthBig && !hud_panel_itemstime_hidebig)
     );
 }
 
index 40747ff..92bbacc 100644 (file)
@@ -6,7 +6,7 @@ REGISTER_MUTATOR(midair, cvar("g_midair"));
 
 .float midair_shieldtime;
 
-MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(midair, Damage_Calculate)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
index 6ec9c23..27c31d2 100644 (file)
@@ -153,12 +153,14 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan
 REGISTER_MUTATOR(nades, cvar("g_nades"));
 
 .float nade_time_primed;
+.float nade_lifetime;
 
 .entity nade_spawnloc;
 
+
 void nade_timer_think(entity this)
 {
-       this.skin = 8 - (this.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
+       this.skin = 8 - (this.owner.wait - time) / (this.owner.nade_lifetime / 10);
        this.nextthink = time;
        if(!this.owner || wasfreed(this.owner))
                delete(this);
@@ -862,7 +864,7 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i
        if(this.health == this.max_health)
        {
                sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
-               this.nextthink = max(time + autocvar_g_nades_nade_lifetime, time);
+               this.nextthink = max(time + this.nade_lifetime, time);
                setthink(this, nade_beep);
        }
 
@@ -1048,6 +1050,7 @@ void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, strin
        setthink(n, nade_beep);
        n.nextthink = max(n.wait - 3, time);
        n.projectiledeathtype = DEATH_NADE.m_id;
+       n.nade_lifetime = ntime;
 
        setmodel(fn, MDL_NADE_VIEW);
        .entity weaponentity = weaponentities[0]; // TODO: unhardcode
@@ -1218,7 +1221,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
        entity held_nade = player.nade;
        if (held_nade)
        {
-               player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1);
+               player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / held_nade.nade_lifetime, 1);
                // LOG_TRACEF("%d %d", player.nade_timer, time - held_nade.nade_time_primed);
                makevectors(player.angles);
                held_nade.velocity = player.velocity;
@@ -1397,7 +1400,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST)
        nades_RemoveBonus(frag_target);
 }
 
-MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(nades, Damage_Calculate)
 {
        entity frag_inflictor = M_ARGV(0, entity);
        entity frag_attacker = M_ARGV(1, entity);
index 143b3c6..76e735e 100644 (file)
@@ -256,7 +256,7 @@ MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
 {
        entity player = M_ARGV(0, entity);
 
-       if(!intermission_running)
+       if(!gameover)
        if(!IS_DEAD(player))
        if(IS_PLAYER(player))
                NIX_GiveCurrentWeapon(player);
index 21a191a..501b3af 100644 (file)
@@ -11,7 +11,7 @@ float autocvar_g_overkill_ammo_charge_limit;
 bool autocvar_g_overkill_filter_healthmega;
 bool autocvar_g_overkill_filter_armormedium;
 bool autocvar_g_overkill_filter_armorbig;
-bool autocvar_g_overkill_filter_armorlarge;
+bool autocvar_g_overkill_filter_armormega;
 
 .float ok_lastwep;
 .float ok_item;
@@ -92,18 +92,17 @@ float ok_CheckWeaponCharge(entity ent, int wep)
        return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
 }
 
-MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
+MUTATOR_HOOKFUNCTION(ok, Damage_Calculate, CBC_ORDER_LAST)
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
        float frag_deathtype = M_ARGV(3, float);
 
-       if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
+       if(IS_PLAYER(frag_attacker) && (IS_PLAYER(frag_target) || IS_VEHICLE(frag_target) || IS_TURRET(frag_target)))
        if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
        {
                if(frag_attacker != frag_target)
-               if(frag_target.health > 0)
-               if(STAT(FROZEN, frag_target) == 0)
+               if(!STAT(FROZEN, frag_target))
                if(!IS_DEAD(frag_target))
                {
                        Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
@@ -164,7 +163,7 @@ MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
 
 MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
 {
-       if(intermission_running || gameover)
+       if(gameover)
                return;
 
        entity player = M_ARGV(0, entity);
@@ -281,8 +280,7 @@ MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
                        wep.nextthink = time + 0.1;
                        return true;
                }
-
-               if(ent.classname == "item_invincible")
+               else if(ent.classname == "item_invincible")
                {
                        entity wep = new(weapon_rpc);
                        setorigin(wep, ent.origin);
@@ -312,10 +310,8 @@ MUTATOR_HOOKFUNCTION(ok, FilterItem)
        {
                case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega;
                case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium;
-               // WARNING: next two statements look wrong because of inconsistency between cvar names and code
-               // armor cvars need renaming to be consistent with their health counterparts
-               case ITEM_ArmorLarge: return autocvar_g_overkill_filter_armorbig;
-               case ITEM_ArmorMega: return autocvar_g_overkill_filter_armorlarge;
+               case ITEM_ArmorBig: return autocvar_g_overkill_filter_armorbig;
+               case ITEM_ArmorMega: return autocvar_g_overkill_filter_armormega;
        }
 
        return true;
index 04d8099..7edef98 100644 (file)
@@ -5,7 +5,7 @@
 
 REGISTER_MUTATOR(rm, cvar("g_instagib"));
 
-MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(rm, Damage_Calculate)
 {
        // we do it this way, so rm can be toggled during the match
        if(!autocvar_g_rm) { return; }
index e4b7843..a0017f9 100644 (file)
@@ -87,6 +87,8 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
                int tested = 0;
                FOREACH_CLIENT_RANDOM(IS_PLAYER(it), LAMBDA(
                        if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_max && tested >= autocvar_g_spawn_near_teammate_ignore_spawnpoint_max) break;
+
+                       if (PHYS_INPUT_BUTTON_CHAT(it)) continue;
                        if (!SAME_TEAM(player, it)) continue;
                        if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && it.health < autocvar_g_balance_health_regenstable) continue;
                        if (IS_DEAD(it)) continue;
@@ -114,22 +116,22 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
                                switch(i)
                                {
                                        case 0:
-                                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 64 + v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 64 + v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it);
                                                break;
                                        case 1:
-                                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 64 - v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 64 - v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it);
                                                break;
                                        case 2:
-                                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin + v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it);
                                                break;
                                        case 3:
-                                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it);
                                                break;
                                        case 4:
-                                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 128 + v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 128 + v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it);
                                                break;
                                        case 5:
-                                               tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 128 - v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it);
+                                               tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 128 - v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it);
                                                break;
                                }
 
@@ -139,17 +141,17 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
 
                                // 400 is about the height of a typical laser jump (in overkill)
                                // not traceline because we need space for the whole player, not just his origin
-                               tracebox(horizontal_trace_endpos, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), horizontal_trace_endpos - '0 0 400', MOVE_NORMAL, it);
+                               tracebox(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), horizontal_trace_endpos - '0 0 400', MOVE_NORMAL, it);
                                vector vectical_trace_endpos = trace_endpos;
                                //te_lightning1(NULL, horizontal_trace_endpos, vectical_trace_endpos);
                                if (trace_startsolid) goto skip; // inside another player
                                if (trace_fraction == 1.0) goto skip; // above void or too high
                                if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) goto skip;
                                if (pointcontents(vectical_trace_endpos) != CONTENT_EMPTY) goto skip; // no lava or slime (or water which i assume would be annoying anyway)
-                               if (tracebox_hits_trigger_hurt(horizontal_trace_endpos, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), vectical_trace_endpos)) goto skip;
+                               if (tracebox_hits_trigger_hurt(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), vectical_trace_endpos)) goto skip;
 
                                // make sure the spawned player will have floor ahead (or at least a wall - he shouldn't fall as soon as he starts moving)
-                               vector floor_test_start = vectical_trace_endpos + v_up * STAT(PL_MAX, NULL).z + v_forward * STAT(PL_MAX, NULL).x; // top front of player's bbox - highest point we know is not inside solid
+                               vector floor_test_start = vectical_trace_endpos + v_up * STAT(PL_MAX, player).z + v_forward * STAT(PL_MAX, player).x; // top front of player's bbox - highest point we know is not inside solid
                                traceline(floor_test_start, floor_test_start + v_forward * 100 - v_up * 128, MOVE_NOMONSTERS, it);
                                //te_beam(NULL, floor_test_start, trace_endpos);
                                if (trace_fraction == 1.0) goto skip;
index 0fb3d61..73f22b8 100644 (file)
@@ -14,7 +14,6 @@ REGISTER_WAYPOINT(RaceFinish, _("Finish"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(RaceStart, _("Start"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(RaceStartFinish, _("Start"), '1 0.5 0', 1);
 
-REGISTER_WAYPOINT(Assault, _("<placeholder>"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(AssaultDefend, _("Defend"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(AssaultDestroy, _("Destroy"), '1 0.5 0', 1);
 REGISTER_WAYPOINT(AssaultPush, _("Push"), '1 0.5 0', 1);
index e7a3356..bdedf29 100644 (file)
@@ -282,10 +282,10 @@ void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, ve
 
     // rotate them, and make them absolute
     rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
-    v1 = rotate(v1, rot) + org;
-    v2 = rotate(v2, rot) + org;
-    v3 = rotate(v3, rot) + org;
-    v4 = rotate(v4, rot) + org;
+    v1 = Rotate(v1, rot) + org;
+    v2 = Rotate(v2, rot) + org;
+    v3 = Rotate(v3, rot) + org;
+    v4 = Rotate(v4, rot) + org;
 
     // draw them
     R_BeginPolygon(pic, f);
@@ -319,9 +319,9 @@ void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, fl
     up = '0 1 0';
 
     rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
-    o = rotate(o, rot) + org;
-    ri = rotate(ri, rot);
-    up = rotate(up, rot);
+    o = Rotate(o, rot) + org;
+    ri = Rotate(ri, rot);
+    up = Rotate(up, rot);
 
     owidth = width + 2 * border;
     o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
@@ -348,19 +348,19 @@ vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
 
     R_BeginPolygon("", DRAWFLAG_NORMAL);
     R_PolygonVertex(o,                                  '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(arrowY  - borderX, ang), '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
-    R_PolygonVertex(o + rotate(arrowY  + borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(arrowY  - borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
+    R_PolygonVertex(o + Rotate(arrowY  + borderX, ang), '0 0 0', '0 0 0', a);
     R_EndPolygon();
 
     R_BeginPolygon("", DRAWFLAG_ADDITIVE);
-    R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
-    R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
-    R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
+    R_PolygonVertex(o + Rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
+    R_PolygonVertex(o + Rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
+    R_PolygonVertex(o + Rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
     R_EndPolygon();
 
-    return o + rotate(eY * (borderDiag+size+margin), ang);
+    return o + Rotate(eY * (borderDiag+size+margin), ang);
 }
 
 // returns location of sprite healthbar
index a6431cf..92fd5df 100644 (file)
 
     MSG_CENTER_NOTIF(ASSAULT_ATTACKING,                 1,      0, 0, "",               CPID_ASSAULT_ROLE,      "0 0",  _("^BGYou are attacking!"), "")
     MSG_CENTER_NOTIF(ASSAULT_DEFENDING,                 1,      0, 0, "",               CPID_ASSAULT_ROLE,      "0 0",  _("^BGYou are defending!"), "")
+    MSG_CENTER_NOTIF(ASSAULT_OBJ_DESTROYED,             1,      0, 1, "f1time",         CPID_ASSAULT_ROLE,      "0 0",  _("^BGObjective destroyed in ^F4%s^BG!"), "")
 
     MSG_CENTER_NOTIF(COUNTDOWN_BEGIN,                   1,      0, 0, "",               CPID_ROUND,             "2 0",  _("^F4Begin!"), "")
     MSG_CENTER_NOTIF(COUNTDOWN_GAMESTART,               1,      0, 1, "",               CPID_ROUND,             "1 f1", _("^F4Game starts in ^COUNT"), "")
index b5635e0..58828c4 100644 (file)
@@ -115,7 +115,7 @@ void PM_ClientMovement_UpdateStatus(entity this)
                // wants to stand, if currently crouching we need to check for a low ceiling first
                if (IS_DUCKED(this))
                {
-                       tracebox(this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), this.origin, MOVE_NORMAL, this);
+                       tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, MOVE_NORMAL, this);
                        if (!trace_startsolid) UNSET_DUCKED(this);
                }
        }
@@ -481,15 +481,15 @@ float racecar_angle(float forward, float down)
                return ret * angle_mult;
 }
 
+#ifdef SVQC
 string specialcommand = "xwxwxsxsxaxdxaxdx1x ";
 .float specialcommand_pos;
 void SpecialCommand(entity this)
 {
-#ifdef SVQC
        if (!CheatImpulse(this, CHIMPULSE_GIVE_ALL.impulse))
                LOG_INFO("A hollow voice says \"Plugh\".\n");
-#endif
 }
+#endif
 
 bool PM_check_specialcommand(entity this, int buttons)
 {
@@ -803,5 +803,10 @@ void CSQC_ClientMovement_PlayerMove_Frame(entity this)
 
 #ifdef SVQC
        this.pm_frametime = frametime;
+#elif defined(CSQC)
+       if((ITEMS_STAT(this) & IT_USING_JETPACK) && !IS_DEAD(this) && !intermission)
+               this.csqcmodel_modelflags |= MF_ROCKET;
+       else
+               this.csqcmodel_modelflags &= ~MF_ROCKET;
 #endif
 }
index 69a95be..4394e17 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "../teams.qh"
 string W_Sound(string w_snd);
+string Item_Sound(string it_snd);
 
 SOUND(ARC_FIRE, W_Sound("arc_fire"));
 SOUND(ARC_LOOP, W_Sound("arc_loop"));
@@ -236,20 +237,20 @@ SOUND(NADE_BEEP, "overkill/grenadebip");
 
 SOUND(BUFF_LOST, "relics/relic_effect");
 
-SOUND(POWEROFF, "misc/poweroff");
-SOUND(POWERUP, "misc/powerup");
-SOUND(SHIELD_RESPAWN, "misc/shield_respawn");
-SOUND(STRENGTH_RESPAWN, "misc/strength_respawn");
+SOUND(POWEROFF, Item_Sound("poweroff"));
+SOUND(POWERUP, Item_Sound("powerup"));
+SOUND(SHIELD_RESPAWN, Item_Sound("shield_respawn"));
+SOUND(STRENGTH_RESPAWN, Item_Sound("strength_respawn"));
 
-SOUND(ARMOR25, "misc/armor25");
+SOUND(ARMOR25, Item_Sound("armor25"));
 SOUND(ARMORIMPACT, "misc/armorimpact");
 SOUND(BODYIMPACT1, "misc/bodyimpact1");
 SOUND(BODYIMPACT2, "misc/bodyimpact2");
 
-SOUND(ITEMPICKUP, "misc/itempickup");
-SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown");
-SOUND(ITEMRESPAWN, "misc/itemrespawn");
-SOUND(MEGAHEALTH, "misc/megahealth");
+SOUND(ITEMPICKUP, Item_Sound("itempickup"));
+SOUND(ITEMRESPAWNCOUNTDOWN, Item_Sound("itemrespawncountdown"));
+SOUND(ITEMRESPAWN, Item_Sound("itemrespawn"));
+SOUND(MEGAHEALTH, Item_Sound("megahealth"));
 
 SOUND(LAVA, "player/lava");
 SOUND(SLIME, "player/slime");
index 82398aa..7649954 100644 (file)
@@ -44,14 +44,14 @@ const int MAX_CL_STATS = 256;
 REGISTER_STAT(WEAPONS, vectori)
 REGISTER_STAT(WEAPONSINMAP, vectori)
 
-REGISTER_STAT(PL_VIEW_OFS, vector, autocvar_sv_player_viewoffset)
-REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector, autocvar_sv_player_crouch_viewoffset)
+REGISTER_STAT(PL_VIEW_OFS, vector)
+REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector)
 
-REGISTER_STAT(PL_MIN, vector, autocvar_sv_player_mins)
-REGISTER_STAT(PL_CROUCH_MIN, vector, autocvar_sv_player_crouch_mins)
+REGISTER_STAT(PL_MIN, vector)
+REGISTER_STAT(PL_CROUCH_MIN, vector)
 
-REGISTER_STAT(PL_MAX, vector, autocvar_sv_player_maxs)
-REGISTER_STAT(PL_CROUCH_MAX, vector, autocvar_sv_player_crouch_maxs)
+REGISTER_STAT(PL_MAX, vector)
+REGISTER_STAT(PL_CROUCH_MAX, vector)
 
 REGISTER_STAT(KH_KEYS, int)
 
@@ -63,9 +63,10 @@ REGISTER_STAT(WEAPON_NEXTTHINK, float)
 #ifdef SVQC
 SPECTATE_COPYFIELD(_STAT(WEAPON_NEXTTHINK))
 float W_WeaponRateFactor(entity this);
+float gameover;
 #endif
 REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
-
+REGISTER_STAT(GAMEOVER, int, gameover)
 REGISTER_STAT(GAMESTARTTIME, float)
 REGISTER_STAT(STRENGTH_FINISHED, float)
 REGISTER_STAT(INVINCIBLE_FINISHED, float)
index 2f1a4c3..9d89679 100644 (file)
@@ -393,7 +393,7 @@ bool have_pickup_item(entity this)
                if(autocvar_g_pickup_items == 0)
                        return false;
                if(g_weaponarena)
-                       if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena
+                       if(this.weapons || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena
                                return false;
        }
        return true;
@@ -924,17 +924,20 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup
 float weapon_pickupevalfunc(entity player, entity item)
 {
        float c;
+       int rating = item.bot_pickupbasevalue;
 
        // See if I have it already
-       if(item.weapons & ~player.weapons)
+       if(player.weapons & item.weapons)
        {
                // If I can pick it up
                if(!item.spawnshieldtime)
                        c = 0;
                else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets)
                {
+                       if (rating > 0)
+                               rating = BOT_PICKUP_RATING_LOW * 0.5 * (1 + rating / BOT_PICKUP_RATING_HIGH);
                        // Skilled bots will grab more
-                       c = bound(0, skill / 10, 1) * 0.5;
+                       c = 1 + bound(0, skill / 10, 1) * 0.5;
                }
                else
                        c = 0;
@@ -942,58 +945,74 @@ float weapon_pickupevalfunc(entity player, entity item)
        else
                c = 1;
 
+       if (c <= 0)
+               return 0;
+
        // If custom weapon priorities for bots is enabled rate most wanted weapons higher
-       if( bot_custom_weapon && c )
-       {
-               // Find the highest position on any range
-               int position = -1;
-               for (int j = 0; j < WEP_LAST ; ++j){
-                       if(
-                                       bot_weapons_far[j] == item.weapon ||
-                                       bot_weapons_mid[j] == item.weapon ||
-                                       bot_weapons_close[j] == item.weapon
-                         )
+       if(bot_custom_weapon)
+       {
+               int best_ratio = 0;
+               int missing = 0;
+
+               // evaluate weapon usefulness in all ranges
+               for(int list = 0; list < 3; list++)
+               {
+                       int position = -1;
+                       int wep_count = 0;
+                       int wpn = item.weapon;
+                       for (int j = 0; j < WEP_LAST; ++j)
                        {
-                               position = j;
-                               break;
+                               int list_wpn = 0;
+                               if (list == 0) list_wpn = bot_weapons_far[j];
+                               else if (list == 1) list_wpn = bot_weapons_mid[j];
+                               else list_wpn = bot_weapons_close[j];
+
+                               if (weaponsInMap & Weapons_from(list_wpn).m_wepset) // only if available
+                               {
+                                       if (list_wpn > 0)
+                                               wep_count++;
+                                       if (position == -1 && list_wpn == wpn)
+                                               position = wep_count;
+                               }
+                       }
+                       if (position == -1)
+                       {
+                               missing++;
+                               position = wep_count; // if missing assume last
+                       }
+                       if (wep_count)
+                       {
+                               if (!best_ratio || position / wep_count < best_ratio)
+                                       best_ratio = position / wep_count;
                        }
                }
 
-               // Rate it
-               if (position >= 0 )
-               {
-                       position = WEP_LAST - position;
-                       // item.bot_pickupbasevalue is overwritten here
-                       return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c;
-               }
+               if (missing < 3 && best_ratio)
+                       c = c - best_ratio * 0.3;
        }
 
-       return item.bot_pickupbasevalue * c;
+       return rating * c;
 }
 
 float commodity_pickupevalfunc(entity player, entity item)
 {
-       float c;
-       float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
-       c = 0;
+       bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
+       float c = 0;
 
        // Detect needed ammo
        FOREACH(Weapons, it != WEP_Null, {
                if(!(player.weapons & (it.m_wepset)))
                        continue;
 
-               if(it.items & ITEM_Shells.m_itemid)
-                       need_shells = true;
-               else if(it.items & ITEM_Bullets.m_itemid)
-                       need_nails = true;
-               else if(it.items & ITEM_Rockets.m_itemid)
-                       need_rockets = true;
-               else if(it.items & ITEM_Cells.m_itemid)
-                       need_cells = true;
-               else if(it.items & ITEM_Plasma.m_itemid)
-                       need_plasma = true;
-               else if(it.items & ITEM_JetpackFuel.m_itemid)
-                       need_fuel = true;
+               switch(it.ammo_field)
+               {
+                       case ammo_shells:  need_shells  = true; break;
+                       case ammo_nails:   need_nails   = true; break;
+                       case ammo_rockets: need_rockets = true; break;
+                       case ammo_cells:   need_cells   = true; break;
+                       case ammo_plasma:  need_plasma  = true; break;
+                       case ammo_fuel:    need_fuel    = true; break;
+               }
        });
 
        // TODO: figure out if the player even has the weapon this ammo is for?
@@ -1346,17 +1365,17 @@ spawnfunc(item_armor_big)
                this.max_armorvalue = g_pickup_armorbig_max;
        if(!this.pickup_anyway)
                this.pickup_anyway = g_pickup_armorbig_anyway;
-       StartItem(this, ITEM_ArmorLarge);
+       StartItem(this, ITEM_ArmorBig);
 }
 
-spawnfunc(item_armor_large)
+spawnfunc(item_armor_mega)
 {
        if(!this.armorvalue)
-               this.armorvalue = g_pickup_armorlarge;
+               this.armorvalue = g_pickup_armormega;
        if(!this.max_armorvalue)
-               this.max_armorvalue = g_pickup_armorlarge_max;
+               this.max_armorvalue = g_pickup_armormega_max;
        if(!this.pickup_anyway)
-               this.pickup_anyway = g_pickup_armorlarge_anyway;
+               this.pickup_anyway = g_pickup_armormega_anyway;
        StartItem(this, ITEM_ArmorMega);
 }
 
@@ -1382,15 +1401,15 @@ spawnfunc(item_health_medium)
     StartItem(this, ITEM_HealthMedium);
 }
 
-spawnfunc(item_health_large)
+spawnfunc(item_health_big)
 {
        if(!this.max_health)
-               this.max_health = g_pickup_healthlarge_max;
+               this.max_health = g_pickup_healthbig_max;
        if(!this.health)
-               this.health = g_pickup_healthlarge;
+               this.health = g_pickup_healthbig;
        if(!this.pickup_anyway)
-               this.pickup_anyway = g_pickup_healthlarge_anyway;
-       StartItem(this, ITEM_HealthLarge);
+               this.pickup_anyway = g_pickup_healthbig_anyway;
+       StartItem(this, ITEM_HealthBig);
 }
 
 spawnfunc(item_health_mega)
@@ -1406,9 +1425,11 @@ spawnfunc(item_health_mega)
 
 // support old misnamed entities
 spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-spawnfunc(item_armor25) { spawnfunc_item_armor_large(this); }
+spawnfunc(item_armor25) { spawnfunc_item_armor_mega(this); }
+spawnfunc(item_armor_large) { spawnfunc_item_armor_mega(this); }
 spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
 spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
+spawnfunc(item_health_large) { spawnfunc_item_health_big(this); }
 spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
 
 spawnfunc(item_strength)
index 498f0ff..5a27956 100644 (file)
@@ -130,23 +130,14 @@ vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
        return sdir * vs + '0 0 1' * vz;
 }
 
-void trigger_push_touch(entity this, entity toucher)
+bool jumppad_push(entity this, entity targ)
 {
-       if (this.active == ACTIVE_NOT)
-               return;
-
-       if (!isPushable(toucher))
-               return;
-
-       if(this.team)
-               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
-                       return;
-
-       EXACTTRIGGER_TOUCH(this, toucher);
+       if (!isPushable(targ))
+               return false;
 
        if(this.enemy)
        {
-               toucher.velocity = trigger_push_calculatevelocity(toucher.origin, this.enemy, this.height);
+               targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height);
        }
        else if(this.target && this.target != "")
        {
@@ -159,105 +150,117 @@ void trigger_push_touch(entity this, entity toucher)
                        else
                                RandomSelection_AddEnt(e, 1, 1);
                }
-               toucher.velocity = trigger_push_calculatevelocity(toucher.origin, RandomSelection_chosen_ent, this.height);
+               targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height);
        }
        else
        {
-               toucher.velocity = this.movedir;
+               targ.velocity = this.movedir;
        }
 
-       UNSET_ONGROUND(toucher);
+       UNSET_ONGROUND(targ);
 
 #ifdef CSQC
-       if (toucher.flags & FL_PROJECTILE)
+       if (targ.flags & FL_PROJECTILE)
        {
-               toucher.angles = vectoangles (toucher.velocity);
-               switch(toucher.move_movetype)
+               targ.angles = vectoangles (targ.velocity);
+               switch(targ.move_movetype)
                {
                        case MOVETYPE_FLY:
-                               set_movetype(toucher, MOVETYPE_TOSS);
-                               toucher.gravity = 1;
+                               set_movetype(targ, MOVETYPE_TOSS);
+                               targ.gravity = 1;
                                break;
                        case MOVETYPE_BOUNCEMISSILE:
-                               set_movetype(toucher, MOVETYPE_BOUNCE);
-                               toucher.gravity = 1;
+                               set_movetype(targ, MOVETYPE_BOUNCE);
+                               targ.gravity = 1;
                                break;
                }
        }
 #endif
 
 #ifdef SVQC
-       if (IS_PLAYER(toucher))
+       if (IS_PLAYER(targ))
        {
                // reset tracking of oldvelocity for impact damage (sudden velocity changes)
-               toucher.oldvelocity = toucher.velocity;
+               targ.oldvelocity = targ.velocity;
 
                if(this.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
                {
                        // flash when activated
-                       Send_Effect(EFFECT_JUMPPAD, toucher.origin, toucher.velocity, 1);
-                       _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
+                       Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1);
+                       _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
                        this.pushltime = time + 0.2;
                }
-               if(IS_REAL_CLIENT(toucher) || IS_BOT_CLIENT(toucher))
+               if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ))
                {
                        bool found = false;
-                       for(int i = 0; i < toucher.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
-                               if(toucher.(jumppadsused[i]) == this)
+                       for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
+                               if(targ.(jumppadsused[i]) == this)
                                        found = true;
                        if(!found)
                        {
-                               toucher.(jumppadsused[toucher.jumppadcount % NUM_JUMPPADSUSED]) = this;
-                               toucher.jumppadcount = toucher.jumppadcount + 1;
+                               targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this;
+                               targ.jumppadcount = targ.jumppadcount + 1;
                        }
 
-                       if(IS_REAL_CLIENT(toucher))
+                       if(IS_REAL_CLIENT(targ))
                        {
                                if(this.message)
-                                       centerprint(toucher, this.message);
+                                       centerprint(targ, this.message);
                        }
                        else
-                               toucher.lastteleporttime = time;
+                               targ.lastteleporttime = time;
 
-                       if (!IS_DEAD(toucher))
-                               animdecide_setaction(toucher, ANIMACTION_JUMP, true);
+                       if (!IS_DEAD(targ))
+                               animdecide_setaction(targ, ANIMACTION_JUMP, true);
                }
                else
-                       toucher.jumppadcount = true;
+                       targ.jumppadcount = true;
 
                // reset tracking of who pushed you into a hazard (for kill credit)
-               toucher.pushltime = 0;
-               toucher.istypefrag = 0;
+               targ.pushltime = 0;
+               targ.istypefrag = 0;
        }
 
        if(this.enemy.target)
-               SUB_UseTargets(this.enemy, toucher, toucher); // TODO: do we need toucher as trigger too?
+               SUB_UseTargets(this.enemy, targ, targ); // TODO: do we need targ as trigger too?
 
-       if (toucher.flags & FL_PROJECTILE)
+       if (targ.flags & FL_PROJECTILE)
        {
-               toucher.angles = vectoangles (toucher.velocity);
-               toucher.com_phys_gravity_factor = 1;
-               switch(toucher.move_movetype)
+               targ.angles = vectoangles (targ.velocity);
+               targ.com_phys_gravity_factor = 1;
+               switch(targ.move_movetype)
                {
                        case MOVETYPE_FLY:
-                               set_movetype(toucher, MOVETYPE_TOSS);
-                               toucher.gravity = 1;
+                               set_movetype(targ, MOVETYPE_TOSS);
+                               targ.gravity = 1;
                                break;
                        case MOVETYPE_BOUNCEMISSILE:
-                               set_movetype(toucher, MOVETYPE_BOUNCE);
-                               toucher.gravity = 1;
+                               set_movetype(targ, MOVETYPE_BOUNCE);
+                               targ.gravity = 1;
                                break;
                }
-               UpdateCSQCProjectile(toucher);
+               UpdateCSQCProjectile(targ);
        }
+#endif
 
-       /*if (toucher.flags & FL_ITEM)
-       {
-               ItemUpdate(toucher);
-               toucher.SendFlags |= ISF_DROP;
-       }*/
+       return true;
+}
+
+void trigger_push_touch(entity this, entity toucher)
+{
+       if (this.active == ACTIVE_NOT)
+               return;
 
-       if (this.spawnflags & PUSH_ONCE)
+       if(this.team)
+               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
+                       return;
+
+       EXACTTRIGGER_TOUCH(this, toucher);
+
+       noref bool success = jumppad_push(this, toucher);
+
+#ifdef SVQC
+       if (success && (this.spawnflags & PUSH_ONCE))
        {
                settouch(this, func_null);
                setthink(this, SUB_Remove);
@@ -272,23 +275,20 @@ void trigger_push_updatelink(entity this);
 #endif
 void trigger_push_findtarget(entity this)
 {
-       entity t;
-       vector org;
-
        // first calculate a typical start point for the jump
-       org = (this.absmin + this.absmax) * 0.5;
-       org_z = this.absmax.z - STAT(PL_MIN, NULL).z;
+       vector org = (this.absmin + this.absmax) * 0.5;
+       org.z = this.absmax.z - PL_MIN_CONST.z;
 
        if (this.target)
        {
-               float n = 0;
-               for(t = NULL; (t = find(t, targetname, this.target)); )
+               int n = 0;
+               for(entity t = NULL; (t = find(t, targetname, this.target)); )
                {
                        ++n;
 #ifdef SVQC
                        entity e = spawn();
                        setorigin(e, org);
-                       setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+                       setsize(e, PL_MIN_CONST, PL_MAX_CONST);
                        e.velocity = trigger_push_calculatevelocity(org, t, this.height);
                        tracetoss(e, e);
                        if(e.move_movetype == MOVETYPE_NONE)
@@ -321,14 +321,13 @@ void trigger_push_findtarget(entity this)
        {
                entity e = spawn();
                setorigin(e, org);
-               setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+               setsize(e, PL_MIN_CONST, PL_MAX_CONST);
                e.velocity = this.movedir;
                tracetoss(e, e);
                waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
                delete(e);
        }
 
-       trigger_push_link(this);
        defer(this, 0.1, trigger_push_updatelink);
 #endif
 }
@@ -393,6 +392,8 @@ spawnfunc(trigger_push)
                this.noise = "misc/jumppad.wav";
        precache_sound (this.noise);
 
+       trigger_push_link(this); // link it now
+
        // this must be called to spawn the teleport waypoints for bots
        InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET);
 }
@@ -415,6 +416,11 @@ bool target_push_send(entity this, entity to, float sf)
        return true;
 }
 
+void target_push_use(entity this, entity actor, entity trigger)
+{
+       jumppad_push(this, actor);
+}
+
 void target_push_link(entity this)
 {
        BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
@@ -429,7 +435,18 @@ void target_push_init(entity this)
        target_push_link(this);
 }
 
-spawnfunc(target_push) { target_push_init(this); }
+void target_push_init2(entity this)
+{
+       if(this.target && this.target != "") // we have an old style pusher!
+       {
+               InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET);
+               this.use = target_push_use;
+       }
+
+       target_push_init(this); // normal push target behaviour can be combined with a legacy pusher?
+}
+
+spawnfunc(target_push) { target_push_init2(this); }
 spawnfunc(info_notnull) { target_push_init(this); }
 spawnfunc(target_position) { target_push_init(this); }
 
index ddad35d..5a2f05a 100644 (file)
@@ -44,7 +44,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity
        }
 }
 
-bool hk_is_valid_target(entity this, entity e_target);
+bool hk_is_valid_target(entity this, entity proj, entity targ);
 void turret_hk_missile_think(entity this)
 {
     vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
@@ -53,10 +53,7 @@ void turret_hk_missile_think(entity this)
     float lt_for;   // Length of Trace FORwrad
     float lt_seek;  // Length of Trace SEEK (left, right, up down)
     float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
-    vector pre_pos;
     float myspeed;
-    entity e;
-    float ad,edist;
 
     this.nextthink = time + this.ticrate;
 
@@ -69,19 +66,17 @@ void turret_hk_missile_think(entity this)
     // Pick the closest valid target.
     if (!this.enemy)
     {
-        e = findradius(this.origin, 5000);
-        while (e)
+        // in this case, the lighter check is to validate it first, and check distance if it is valid
+        IL_EACH(g_damagedbycontents, hk_is_valid_target(this.owner, this, it),
         {
-            if (hk_is_valid_target(this, e))
-            {
-                if (!this.enemy)
-                    this.enemy = e;
-                else
-                    if (vlen2(this.origin - e.origin) < vlen2(this.origin - this.enemy.origin))
-                        this.enemy = e;
-            }
-            e = e.chain;
-        }
+            if(vdist(it.origin, >, 5000))
+                continue;
+
+            if(!this.enemy)
+                this.enemy = it;
+            else if(vlen2(this.origin - it.origin) < vlen2(this.origin - this.enemy.origin))
+                this.enemy = it;
+        });
     }
 
     this.angles = vectoangles(this.velocity);
@@ -91,16 +86,15 @@ void turret_hk_missile_think(entity this)
 
     if (this.enemy)
     {
-        edist = vlen(this.origin - this.enemy.origin);
         // Close enougth to do decent damage?
-        if ( edist <= (this.owner.shot_radius * 0.25) )
+        if(vdist(this.origin - this.enemy.origin, <=, (this.owner.shot_radius * 0.25)))
         {
             turret_projectile_explode(this);
             return;
         }
 
         // Get data on enemy position
-        pre_pos = this.enemy.origin +
+        vector pre_pos = this.enemy.origin +
                   this.enemy.velocity *
                   min((vlen(this.enemy.origin - this.origin) / vlen(this.velocity)),0.5);
 
@@ -111,12 +105,11 @@ void turret_hk_missile_think(entity this)
     }
     else
     {
-    edist = 0;
-    ve = '0 0 0';
+        ve = '0 0 0';
         fe = 0;
     }
 
-    if ((fe != 1) || (this.enemy == NULL) || (edist > 1000))
+    if ((fe != 1) || (this.enemy == NULL) || vdist(this.origin - this.enemy.origin, >, 1000))
     {
         myspeed = vlen(this.velocity);
 
@@ -129,7 +122,7 @@ void turret_hk_missile_think(entity this)
         ff = trace_fraction;
 
         // Find angular offset
-        ad = vlen(vectoangles(normalize(this.enemy.origin - this.origin)) - this.angles);
+        float ad = vlen(vectoangles(normalize(this.enemy.origin - this.origin)) - this.angles);
 
         // To close to something, Slow down!
         if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
@@ -224,7 +217,7 @@ void turret_hk_missile_think(entity this)
 
 #ifdef TURRET_DEBUG_HK
     //if(this.atime < time) {
-    if ((fe <= 0.99)||(edist > 1000))
+    if ((fe <= 0.99)||vdist(this.origin - this.enemy.origin, >, 1000))
     {
         te_lightning2(NULL,this.origin, this.origin + vr * lt_seek);
         te_lightning2(NULL,this.origin, this.origin + vl * lt_seek);
@@ -246,35 +239,39 @@ void turret_hk_missile_think(entity this)
     UpdateCSQCProjectile(this);
 }
 
-bool hk_is_valid_target(entity this, entity e_target)
+bool hk_is_valid_target(entity this, entity proj, entity targ)
 {
-    if (e_target == NULL)
+    if (!targ)
+        return false;
+
+    // we know for sure pure entities are bad targets
+    if(is_pure(targ))
         return false;
 
     // If only this was used more..
-    if (e_target.flags & FL_NOTARGET)
+    if (targ.flags & FL_NOTARGET)
         return false;
 
     // Cant touch this
-    if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
+    if ((targ.takedamage == DAMAGE_NO) || (targ.health < 0))
         return false;
 
     // player
-    if (IS_CLIENT(e_target))
+    if (IS_PLAYER(targ))
     {
-        if (this.owner.target_select_playerbias < 0)
+        if (this.target_select_playerbias < 0)
             return false;
 
-        if (IS_DEAD(e_target))
+        if (IS_DEAD(targ))
             return false;
     }
 
     // Missile
-    if ((e_target.flags & FL_PROJECTILE) && (this.owner.target_select_missilebias < 0))
+    if ((targ.flags & FL_PROJECTILE) && (this.target_select_missilebias < 0))
         return false;
 
     // Team check
-    if ((e_target.team == this.owner.team) || (this.owner.team == e_target.owner.team))
+    if ((targ.team == this.team) || (this.team == targ.owner.team))
         return false;
 
     return true;
index b165bb3..9c8d05c 100644 (file)
@@ -739,7 +739,8 @@ void vehicles_impact(entity this, float _minspeed, float _speedfac, float _maxpa
 // vehicle enter/exit handling
 vector vehicles_findgoodexit(entity this, vector prefer_spot)
 {
-       tracebox(this.origin + '0 0 32', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), prefer_spot, MOVE_NORMAL, this.owner);
+       // TODO: we actually want the player's size here
+       tracebox(this.origin + '0 0 32', PL_MIN_CONST, PL_MAX_CONST, prefer_spot, MOVE_NORMAL, this.owner);
        if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                return prefer_spot;
 
@@ -751,7 +752,7 @@ vector vehicles_findgoodexit(entity this, vector prefer_spot)
                v = randomvec();
                v_z = 0;
                v = v2 + normalize(v) * mysize;
-               tracebox(v2, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, MOVE_NORMAL, this.owner);
+               tracebox(v2, PL_MIN_CONST, PL_MAX_CONST, v, MOVE_NORMAL, this.owner);
                if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                        return v;
        }
@@ -793,7 +794,7 @@ void vehicles_exit(entity vehic, bool eject)
                        WriteAngle(MSG_ONE, 0);
                }
 
-               setsize(player, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+               setsize(player, STAT(PL_MIN,player), STAT(PL_MAX, player));
 
                player.takedamage               = DAMAGE_AIM;
                player.solid                    = SOLID_SLIDEBOX;
@@ -803,7 +804,7 @@ void vehicles_exit(entity vehic, bool eject)
                player.alpha                    = 1;
                player.PlayerPhysplug   = func_null;
                player.vehicle                  = NULL;
-               player.view_ofs         = STAT(PL_VIEW_OFS, NULL);
+               player.view_ofs         = STAT(PL_VIEW_OFS, player);
                player.event_damage     = PlayerDamage;
                player.hud                              = HUD_NORMAL;
                PS(player).m_switchweapon = vehic.m_switchweapon;
@@ -959,8 +960,8 @@ void vehicles_enter(entity pl, entity veh)
        veh.vehicle_hudmodel.viewmodelforclient = pl;
 
        pl.crouch = false;
-       pl.view_ofs = STAT(PL_VIEW_OFS, NULL);
-       setsize (pl, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+       pl.view_ofs = STAT(PL_VIEW_OFS, pl);
+       setsize (pl, STAT(PL_MIN, pl), STAT(PL_MAX, pl));
 
        veh.event_damage        = vehicles_damage;
        veh.nextthink           = 0;
index 3f5f404..573e46a 100644 (file)
@@ -184,11 +184,11 @@ vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity p
        //vector exitspot;
        float mysize;
 
-       tracebox(gunner.origin + '0 0 32', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), prefer_spot, MOVE_NORMAL, player);
+       tracebox(gunner.origin + '0 0 32', STAT(PL_MIN, player), STAT(PL_MAX, player), prefer_spot, MOVE_NORMAL, player);
        if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                return prefer_spot;
 
-       mysize = 1.5 * vlen(STAT(PL_MAX, NULL) - STAT(PL_MIN, NULL)); // can't use gunner's size, as they don't have a size
+       mysize = 1.5 * vlen(STAT(PL_MAX, player) - STAT(PL_MIN, player)); // can't use gunner's size, as they don't have a size
        float i;
        vector v, v2;
        v2 = 0.5 * (gunner.absmin + gunner.absmax);
@@ -197,7 +197,7 @@ vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity p
                v = randomvec();
                v_z = 0;
                v = v2 + normalize(v) * mysize;
-               tracebox(v2, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, MOVE_NORMAL, player);
+               tracebox(v2, STAT(PL_MIN, player), STAT(PL_MAX, player), v, MOVE_NORMAL, player);
                if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
                        return v;
        }
@@ -224,7 +224,7 @@ void bumblebee_gunner_exit(entity this, int _exitflag)
        }
 
        CSQCVehicleSetup(player, HUD_NORMAL);
-       setsize(player, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+       setsize(player, STAT(PL_MIN, player), STAT(PL_MAX, player));
 
        player.takedamage     = DAMAGE_AIM;
        player.solid          = SOLID_SLIDEBOX;
@@ -232,7 +232,7 @@ void bumblebee_gunner_exit(entity this, int _exitflag)
        player.effects       &= ~EF_NODRAW;
        player.alpha          = 1;
        player.PlayerPhysplug = func_null;
-       player.view_ofs       = STAT(PL_VIEW_OFS, NULL);
+       player.view_ofs       = STAT(PL_VIEW_OFS, player);
        player.event_damage   = PlayerDamage;
        player.hud            = HUD_NORMAL;
        player.teleportable       = TELEPORT_NORMAL;
@@ -391,7 +391,7 @@ bool bumblebee_pilot_frame(entity this, float dt)
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
                vehic.solid = SOLID_NOT;
                vehic.takedamage = DAMAGE_NO;
index 0cdfc8c..80c28a8 100644 (file)
@@ -151,7 +151,7 @@ bool racer_frame(entity this, float dt)
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
                vehic.solid = SOLID_NOT;
                vehic.takedamage = DAMAGE_NO;
index 1068e74..7c433f9 100644 (file)
@@ -133,7 +133,7 @@ bool raptor_frame(entity this, float dt)
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
                vehic.solid = SOLID_NOT;
                vehic.takedamage = DAMAGE_NO;
index d99335a..6ad4694 100644 (file)
@@ -48,7 +48,7 @@ bool spiderbot_frame(entity this, float dt)
        entity vehic = this.vehicle;
        return = true;
 
-       if(intermission_running)
+       if(gameover)
        {
                vehic.solid = SOLID_NOT;
                vehic.takedamage = DAMAGE_NO;
index 3e4654f..b998bc9 100644 (file)
@@ -133,7 +133,8 @@ void W_Porto_Touch(entity this, entity toucher)
        norm = trace_plane_normal;
        if(trace_ent.iscreature)
        {
-               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * STAT(PL_MIN, NULL).z, MOVE_WORLDONLY, this);
+               // TODO: why not use entity size?
+               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_CONST.z, MOVE_WORLDONLY, this);
                if(trace_fraction >= 1)
                        return;
                if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
index 9ae0c60..d45cf31 100644 (file)
@@ -283,7 +283,7 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit
 }
 METHOD(Shotgun, wr_setup, void(entity thiswep, entity actor))
 {
-    actor.ammo_field = ammo_none;
+    actor.ammo_field = ammo_shells;
 }
 METHOD(Shotgun, wr_checkammo1, bool(entity thiswep, entity actor))
 {
index e11e4d8..91042b6 100644 (file)
@@ -215,24 +215,24 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor, .entity weaponentity)
        W_DecreaseAmmo(thiswep, actor, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)));
 }
 
-void W_RocketMinsta_Laser_Explode (entity this)
+void W_RocketMinsta_Laser_Explode (entity this, entity directhitentity)
 {
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(this.realowner, other))
-                               if(!IS_DEAD(other))
-                                       if(IsFlying(other))
+       if(directhitentity.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(directhitentity))
+                       if(DIFF_TEAM(this.realowner, directhitentity))
+                               if(!IS_DEAD(directhitentity))
+                                       if(IsFlying(directhitentity))
                                                Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
 
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
-       RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, other);
+       RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, directhitentity);
        delete(this);
 }
 
 void W_RocketMinsta_Laser_Explode_use(entity this, entity actor, entity trigger)
 {
-       W_RocketMinsta_Laser_Explode(this);
+       W_RocketMinsta_Laser_Explode(this, trigger); // we probably don't want trigger used here, but this matches closest to old behaviour
 }
 
 void W_RocketMinsta_Laser_Touch(entity this, entity toucher)
index a617c73..2d48e57 100644 (file)
@@ -28,8 +28,8 @@
  */
 #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)))
+       noref float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
+       noref float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
 
 #define SYSTEM_UPDATE(sys) \
        MACRO_BEGIN \
index 66613b5..9e26e70 100644 (file)
@@ -109,15 +109,15 @@ void CSQCPlayer_SetMinsMaxs(entity this)
 {
        if (IS_DUCKED(this) || !this.isplayermodel)
        {
-               this.mins = STAT(PL_CROUCH_MIN, NULL);
-               this.maxs = STAT(PL_CROUCH_MAX, NULL);
-               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, NULL);
+               this.mins = STAT(PL_CROUCH_MIN, this);
+               this.maxs = STAT(PL_CROUCH_MAX, this);
+               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
        }
        else
        {
-               this.mins = STAT(PL_MIN, NULL);
-               this.maxs = STAT(PL_MAX, NULL);
-               this.view_ofs = STAT(PL_VIEW_OFS, NULL);
+               this.mins = STAT(PL_MIN, this);
+               this.maxs = STAT(PL_MAX, this);
+               this.view_ofs = STAT(PL_VIEW_OFS, this);
        }
 }
 
@@ -226,8 +226,8 @@ void CSQCPlayer_SetCamera()
 {
        const vector v0 = ((intermission && !autocvar_cl_movement_intermissionrunning) ? '0 0 0' : pmove_vel); // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
        const float vh = STAT(VIEWHEIGHT);
-       const vector pl_viewofs = STAT(PL_VIEW_OFS, NULL);
-       const vector pl_viewofs_crouch = STAT(PL_CROUCH_VIEW_OFS, NULL);
+       const vector pl_viewofs = STAT(PL_VIEW_OFS);
+       const vector pl_viewofs_crouch = STAT(PL_CROUCH_VIEW_OFS);
        const entity e = csqcplayer;
        if (e)
        {
index bb363c1..0a887cf 100644 (file)
@@ -101,7 +101,7 @@ noref vector _vec2;
 noref vector _vec3;
 #define vec3(_x, _y, _z) (_vec3.x = (_x), _vec3.y = (_y), _vec3.z = (_z), _vec3)
 
-vector rotate(vector v, float a)
+vector Rotate(vector v, float a)
 {
        float a_sin = sin(a), a_cos = cos(a);
        vector r = '0 0 0';
index d30ab3d..a28b086 100644 (file)
                PERSON(Zac "Mario" Jardine) \
        NL() \
        TITLE(_("Extended Team")) \
+               PERSON(AllieWay) \
                PERSON(Antonio "terencehill" Piu) \
                PERSON(Archer) \
                PERSON(BuddyFriendGuy) \
                PERSON(Debugger) \
+               PERSON(Diomedes) \
+               PERSON(Freddy) \
                PERSON(GATTS) \
                PERSON(Halogene) \
                PERSON(IDWMaster) \
                PERSON(Jan "zykure" Behrens) \
                PERSON(JH0nny) \
-               PERSON(Luigi) \
+               PERSON(Jubilant) \
                PERSON(Łukasz "kuniu the frogg" Polek) \
+               PERSON(martin-t) \
                PERSON(Matthias "matthiaskrgr" Krüger) \
                PERSON(Mattia "Melanosuchus" Basaglia) \
                PERSON(MrBougo) \
        FUNCTION(_("Level Design")) \
                PERSON(Amadeusz "amade/proraide" Sławiński) \
                PERSON(Ben "MooKow" Banker) \
-               PERSON(Calinou) \
                PERSON(Cortez) \
                PERSON(Cuinn "Cuinnton" Herrick) \
                PERSON(Debugger) \
+               PERSON(Hugo "Calinou" Locurcio) \
                PERSON(Jakob "tZork" Markström Gröhn) \
                PERSON(Konrad "Justin" Slawinski) \
                PERSON(Maddin) \
                        PERSON(Mihail "meequz" Varantsou) \
                NL() \
                FUNCTION(_("Bulgarian")) \
+                       PERSON(Alexander "alex4o" Bonin) \
+                       PERSON(ifohancroft) \
                        PERSON(lokster) \
                        PERSON(set_killer) \
+                       PERSON(ubone) \
                NL() \
                FUNCTION(_("Chinese (China)")) \
-                       PERSON(Antonidas) \
                        PERSON(kalawore) \
                        PERSON(sapphireliu) \
                NL() \
+               FUNCTION(_("Chinese (Taiwan)")) \
+                       PERSON(Alisha) \
+                       PERSON(Armcoon) \
+                       PERSON(Jeff "s8321414" Huang) \
+               NL() \
+               FUNCTION(_("Cornish")) \
+                       PERSON(Nicky "nrowe" Rowe) \
+               NL() \
                FUNCTION(_("Czech")) \
                        PERSON(shogun assassin/woky) \
                        PERSON(Superovoce) \
                NL() \
                FUNCTION(_("Dutch")) \
                        PERSON(Alexander "freefang" van Dam) \
+                       PERSON(Jonathan "Jonakeys" van der Steege) \
                        PERSON(PinkRobot) \
                        PERSON(vegiburger) \
                NL() \
                FUNCTION(_("English (Australia)")) \
                        PERSON(Laurene "sunflowers" Albrand) \
+                       PERSON(Stuart "Cefiar" Young) \
                        PERSON(Zac "Mario" Jardine) \
                NL() \
                FUNCTION(_("Finnish")) \
+                       PERSON(Jonas "PowaTree" Sahlberg) \
                        PERSON(Henry "Exitium" Sanmark) \
                        PERSON(Rasmus "FruitieX" Eskola) \
                NL() \
                FUNCTION(_("French")) \
-                       PERSON(Calinou) \
+                       PERSON(Hugo "Calinou" Locurcio) \
+                       PERSON(Kim "coughingmouse" Lee) \
                        PERSON(Maxime "Taximus" Paradis) \
                        PERSON(RedGuff) \
+                       PERSON(Thomas "illwieckz" Debesse) \
                        PERSON(Yannick "SpiKe" Le Guen) \
                NL() \
                FUNCTION(_("German")) \
                        PERSON(Erik "Ablu" Schilling) \
                        PERSON(Jope "Sless" Withers) \
                        PERSON(Marvin "Mirio" Beck) \
+                       PERSON(Paul "Snapper") \
                        PERSON(Rudolf "divVerent" Polzer) \
+                       PERSON(Wuzzy) \
                        PERSON(Yepoleb) \
                NL() \
                FUNCTION(_("Greek")) \
                        PERSON(stdi) \
                        PERSON(XCostaX) \
                NL() \
+               FUNCTION(_("Kazakh")) \
+                       PERSON("Артем \"bystrov.arterm\" Быстров") \
+               NL() \
+               FUNCTION(_("Korean")) \
+                       PERSON(Jisoo "s6e9x" Lim) \
+                       PERSON(Kim "coughingmouse" Lee) \
+               NL() \
                FUNCTION(_("Polish")) \
                        PERSON(4m) \
                        PERSON(Alex "tiprogrammierer.alex" Progger) \
                        PERSON(Amadeusz "amade/proraide" Sławiński) \
+                       PERSON(Artur "artur9010" Motyka) \
+                       PERSON(Jakub "KubeQ11" Pędziszewski) \
                NL() \
                FUNCTION(_("Portuguese")) \
+                       PERSON(Ivan Paulos "greylica" Tomé) \
+                       PERSON(Jean Trindade "Muleke_Trairao" Pereira) \
                        PERSON(Ricardo Manuel "Hellgardia" da Cruz Coelho da Silva) \
                        PERSON(xXxCHAOTICxXx) \
                NL() \
                        PERSON(Adrian-Ciprian "adrian.tinjala" Tînjală) \
                        PERSON(BusterDBK) \
                        PERSON(Mircea "Taoki" Kitsune) \
+                       PERSON(Sorin "unic_sorin" Botirla) \
                        PERSON(Tudor "TropiKo" Ionel) \
                NL() \
                FUNCTION(_("Russian")) \
                        PERSON(Alex "alextalker7" Talker) \
                        PERSON(Alexandr "zrg") \
                        PERSON(Andrei "adem4ik" Stepanov) \
+                       PERSON(Andrey "dekrY" P.) \
                        PERSON(gravicappa) \
                        PERSON(Hot Dog) \
                        PERSON(Lord Canistra) \
                        PERSON(marcus256) \
                NL() \
                FUNCTION(_("Ukrainian")) \
+                       PERSON(Dmitro "Gamebot" Sokhin) \
                        PERSON(Oleh "BlaXpirit" Prypin) \
                        PERSON(Vasyl "Harmata" Melnyk) \
                        PERSON(Yuriy "herrniemand" Ackermann) \
@@ -402,7 +437,6 @@ void XonoticCreditsList_resizeNotify(entity me, vector relOrigin, vector relSize
 }
 void XonoticCreditsList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
 {
-       // layout: Ping, Credits name, Map name, NP, TP, MP
        string s;
        float theAlpha;
        vector theColor;
index f060383..aeb8c8c 100644 (file)
@@ -34,7 +34,7 @@ void XonoticHUDItemsTimeDialog_fill(entity me)
        me.TR(me);
                me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidespawned", _("Hide spawned items")));
        me.TR(me);
-               me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidelarge", _("Hide large armor and health")));
+               me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidebig", _("Hide big armor and health")));
        me.TR(me);
                me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_dynamicsize", _("Dynamic size")));
 }
index 85e0e9e..4819142 100644 (file)
@@ -58,6 +58,27 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax,
        t.configureXonoticTextSliderValues(t);
 }
 
+void GameType_ConfigureSliders_for_CurrentGametype(entity me)
+{
+       switch(MapInfo_CurrentGametype())
+       {
+               case MAPINFO_TYPE_CA:              GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_ca_teams_override",          _("The amount of frags needed before the match will end")); break;
+               case MAPINFO_TYPE_FREEZETAG:       GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_freezetag_teams_override",   _("The amount of frags needed before the match will end")); break;
+               case MAPINFO_TYPE_CTF:             GameType_ConfigureSliders(me, _("Capture limit:"),   1,   20,  1, "capturelimit_override",     string_null,                    _("The amount of captures needed before the match will end")); break;
+               case MAPINFO_TYPE_DOMINATION:      GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, "g_domination_point_limit",  "g_domination_teams_override",  _("The amount of points needed before the match will end")); break;
+               case MAPINFO_TYPE_KEYHUNT:         GameType_ConfigureSliders(me, _("Point limit:"),   200, 1500, 50, "g_keyhunt_point_limit",     "g_keyhunt_teams_override",     _("The amount of points needed before the match will end")); break;
+               case MAPINFO_TYPE_LMS:             GameType_ConfigureSliders(me, _("Lives:"),           3,   50,  1, "g_lms_lives_override",      string_null,                    string_null); break;
+               case MAPINFO_TYPE_RACE:            GameType_ConfigureSliders(me, _("Laps:"),            1,   25,  1, "g_race_laps_limit",         string_null,                    string_null); break;
+               case MAPINFO_TYPE_NEXBALL:         GameType_ConfigureSliders(me, _("Goals:"),           1,   50,  1, "g_nexball_goallimit",       string_null,                    _("The amount of goals needed before the match will end")); break;
+               case MAPINFO_TYPE_ASSAULT:         GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_ONSLAUGHT:       GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_CTS:             GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_INVASION:        GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
+               case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"),     5,  100,  5, "g_tdm_point_limit",         "g_tdm_teams_override",         _("The amount of points needed before the match will end")); break;
+               default:                           GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        string_null,                    _("The amount of frags needed before the match will end")); break;
+       }
+}
+
 entity makeXonoticServerCreateTab()
 {
        entity me;
@@ -210,28 +231,12 @@ void XonoticServerCreateTab_fill(entity me)
                        e.onClickEntity = me.mapListBox;
                        me.mapListBox.startButton = e;
 
-       me.gameTypeChangeNotify(me);
+       GameType_ConfigureSliders_for_CurrentGametype(me);
 }
 
 void XonoticServerCreateTab_gameTypeChangeNotify(entity me)
 {
-       switch(MapInfo_CurrentGametype())
-       {
-               case MAPINFO_TYPE_CA:              GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_ca_teams_override",          _("The amount of frags needed before the match will end")); break;
-               case MAPINFO_TYPE_FREEZETAG:       GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_freezetag_teams_override",   _("The amount of frags needed before the match will end")); break;
-               case MAPINFO_TYPE_CTF:             GameType_ConfigureSliders(me, _("Capture limit:"),   1,   20,  1, "capturelimit_override",     string_null,                    _("The amount of captures needed before the match will end")); break;
-               case MAPINFO_TYPE_DOMINATION:      GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, "g_domination_point_limit",  "g_domination_teams_override",  _("The amount of points needed before the match will end")); break;
-               case MAPINFO_TYPE_KEYHUNT:         GameType_ConfigureSliders(me, _("Point limit:"),   200, 1500, 50, "g_keyhunt_point_limit",     "g_keyhunt_teams_override",     _("The amount of points needed before the match will end")); break;
-               case MAPINFO_TYPE_LMS:             GameType_ConfigureSliders(me, _("Lives:"),           3,   50,  1, "g_lms_lives_override",      string_null,                    string_null); break;
-               case MAPINFO_TYPE_RACE:            GameType_ConfigureSliders(me, _("Laps:"),            1,   25,  1, "g_race_laps_limit",         string_null,                    string_null); break;
-               case MAPINFO_TYPE_NEXBALL:         GameType_ConfigureSliders(me, _("Goals:"),           1,   50,  1, "g_nexball_goallimit",       string_null,                    _("The amount of goals needed before the match will end")); break;
-               case MAPINFO_TYPE_ASSAULT:         GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_ONSLAUGHT:       GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_CTS:             GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_INVASION:        GameType_ConfigureSliders(me, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null); break;
-               case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"),     5,  100,  5, "g_tdm_point_limit",         "g_tdm_teams_override",         _("The amount of points needed before the match will end")); break;
-               default:                           GameType_ConfigureSliders(me, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        string_null,                    _("The amount of frags needed before the match will end")); break;
-       }
+       GameType_ConfigureSliders_for_CurrentGametype(me);
 
        me.mapListBox.refilter(me.mapListBox);
 }
index 65edcde..ab52949 100644 (file)
@@ -71,7 +71,7 @@ string XonoticMutatorsDialog_toString(entity me)
                s = strcat(s, ", ", _("Invincible Projectiles"));
        if(cvar_string("g_weaponarena") != "0")
                s = strcat(s, ", ", WeaponArenaString());
-       else if(cvar("g_balance_blaster_weaponstart") == 0)
+       else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
                s = strcat(s, ", ", _("No start weapons"));
        if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
                s = strcat(s, ", ", _("Low gravity"));
@@ -143,7 +143,7 @@ float checkCompatibility_weaponarena_weapon(entity me)
                return 0;
        if(cvar_string("g_weaponarena") == "0")
                return 0;
-       if(cvar_string("g_balance_blaster_weaponstart") == "0")
+       if(cvar_string("g_balance_blaster_weaponstartoverride") == "0")
                return 0;
        return 1;
 }
@@ -279,9 +279,9 @@ void XonoticMutatorsDialog_fill(entity me)
                        setDependent(e, "g_nix", 1, 1);
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstart", "0", _("No start weapons"), "-"));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstartoverride", "0", _("No start weapons"), "-"));
                        e.cvarOffValue = "-1";
-                       makeMulti(e, "g_balance_shotgun_weaponstart g_balance_machinegun_weaponstart g_balance_devastator_weaponstart g_balance_minelayer_weaponstart g_balance_electro_weaponstart g_balance_crylink_weaponstart g_balance_hagar_weaponstart g_balance_porto_weaponstart g_balance_vaporizer_weaponstart g_balance_hook_weaponstart g_balance_rifle_weaponstart g_balance_fireball_weaponstart g_balance_seeker_weaponstart g_balance_tuba_weaponstart g_balance_arc_weaponstart g_balance_vortex_weaponstart g_balance_mortar_weaponstart");
+                       makeMulti(e, "g_balance_shotgun_weaponstartoverride g_balance_machinegun_weaponstartoverride g_balance_devastator_weaponstartoverride g_balance_minelayer_weaponstartoverride g_balance_electro_weaponstartoverride g_balance_crylink_weaponstartoverride g_balance_hagar_weaponstartoverride g_balance_porto_weaponstartoverride g_balance_vaporizer_weaponstartoverride g_balance_hook_weaponstartoverride g_balance_rifle_weaponstartoverride g_balance_fireball_weaponstartoverride g_balance_seeker_weaponstartoverride g_balance_tuba_weaponstartoverride g_balance_arc_weaponstartoverride g_balance_vortex_weaponstartoverride g_balance_mortar_weaponstartoverride");
 
        me.gotoRC(me, me.rows - 1, 0);
                me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
index 3359f13..95bf715 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "bot/_mod.inc"
 #include "command/_mod.inc"
+#include "compat/_mod.inc"
 #include "mutators/_mod.inc"
 #include "pathlib/_mod.inc"
 #include "weapons/_mod.inc"
index 4ecc610..eb74529 100644 (file)
@@ -30,6 +30,3 @@
 #endif
 #include <server/teamplay.qc>
 #include <server/tests.qc>
-#include <server/t_halflife.qc>
-#include <server/t_quake.qc>
-#include <server/t_quake3.qc>
index 8162606..a0d160d 100644 (file)
@@ -30,6 +30,3 @@
 #endif
 #include <server/teamplay.qh>
 #include <server/tests.qh>
-#include <server/t_halflife.qh>
-#include <server/t_quake.qh>
-#include <server/t_quake3.qh>
index 64957b9..dbe3197 100644 (file)
@@ -54,6 +54,7 @@ void bot_endgame();
 bool bot_fixcount();
 void bot_list_commands();
 void bot_queuecommand(entity bot, string cmdstring);
+void bot_clear(entity this);
 void bot_relinkplayerlist();
 void bot_resetqueues();
 void bot_serverframe();
@@ -84,3 +85,6 @@ void waypoint_spawnforitem_force(entity e, vector org);
 void waypoint_spawnforteleporter(entity e, vector destination, float timetaken);
 void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken);
 entity waypoint_spawn(vector m1, vector m2, float f);
+
+.entity goalcurrent;
+void navigation_clearroute(entity this);
index 2a7478d..fb6f1e0 100644 (file)
@@ -64,6 +64,8 @@ void bot_think(entity this)
                this.flags |= FL_GODMODE;
 
        this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
+       if(this.bot_nextthink < time)
+               this.bot_nextthink = time + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill);
        //if (this.bot_painintensity > 0)
        //      this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
 
@@ -72,6 +74,7 @@ void bot_think(entity this)
 
        if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
        {
+               this.movement = '0 0 0';
                this.bot_nextthink = time + 0.5;
                return;
        }
@@ -118,6 +121,7 @@ void bot_think(entity this)
        // if dead, just wait until we can respawn
        if (IS_DEAD(this))
        {
+               this.movement = '0 0 0';
                if (this.deadflag == DEAD_DEAD)
                {
                        PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
@@ -338,9 +342,8 @@ void bot_custom_weapon_priority_setup()
 
 void bot_endgame()
 {
-       entity e;
-       //dprint("bot_endgame\n");
-       e = bot_list;
+       bot_relinkplayerlist();
+       entity e = bot_list;
        while (e)
        {
                setcolor(e, e.bot_preferredcolors);
@@ -397,7 +400,7 @@ void bot_clientdisconnect(entity this)
        this.playerskin_freeme = string_null;
        if(this.bot_cmd_current)
                delete(this.bot_cmd_current);
-       if(bot_waypoint_queue_owner==this)
+       if(bot_waypoint_queue_owner == this)
                bot_waypoint_queue_owner = NULL;
 }
 
@@ -630,9 +633,42 @@ float bot_fixcount()
        return true;
 }
 
+void bot_remove_from_bot_list(entity this)
+{
+       entity e = bot_list;
+       entity prev_bot = NULL;
+       while (e)
+       {
+               if(e == this)
+               {
+                       if(!prev_bot)
+                               bot_list = this.nextbot;
+                       else
+                               prev_bot.nextbot = this.nextbot;
+                       if(bot_strategytoken == this)
+                       {
+                               bot_strategytoken = this.nextbot;
+                               bot_strategytoken_taken = true;
+                       }
+                       this.nextbot = NULL;
+                       break;
+               }
+               prev_bot = e;
+               e = e.nextbot;
+       }
+}
+
+void bot_clear(entity this)
+{
+       bot_remove_from_bot_list(this);
+       if(bot_waypoint_queue_owner == this)
+               bot_waypoint_queue_owner = NULL;
+       this.aistatus &= ~AI_STATUS_STUCK; // otherwise bot_waypoint_queue_owner will be set again to this by navigation_unstuck
+}
+
 void bot_serverframe()
 {
-       if (intermission_running)
+       if (gameover)
                return;
 
        if (time < 2)
index 0959307..f615c04 100644 (file)
@@ -3,18 +3,18 @@
  * Globals and Fields
  */
 
-const int AI_STATUS_ROAMING                                            = BIT(0);       // Bot is just crawling the map. No enemies at sight
-const int AI_STATUS_ATTACKING                                  = BIT(1);       // There are enemies at sight
-const int AI_STATUS_RUNNING                                            = BIT(2);       // Bot is bunny hopping
-const int AI_STATUS_DANGER_AHEAD                               = BIT(3);       // There is lava/slime/trigger_hurt ahead
-const int AI_STATUS_OUT_JUMPPAD                                        = BIT(4);       // Trying to get out of a "vertical" jump pad
-const int AI_STATUS_OUT_WATER                                  = BIT(5);       // Trying to get out of water
-const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = BIT(6);       // Waiting for the personal waypoint to be linked
-const int AI_STATUS_WAYPOINT_PERSONAL_GOING            = BIT(7);       // Going to a personal waypoint
-const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = BIT(8);       // Personal waypoint reached
-const int AI_STATUS_JETPACK_FLYING                             = BIT(9);
-const int AI_STATUS_JETPACK_LANDING                            = BIT(10);
-const int AI_STATUS_STUCK                                              = BIT(11);  // Cannot reach any goal
+const int AI_STATUS_ROAMING                    = BIT(0); // Bot is just crawling the map. No enemies at sight
+const int AI_STATUS_ATTACKING                  = BIT(1); // There are enemies at sight
+const int AI_STATUS_RUNNING                    = BIT(2); // Bot is bunny hopping
+const int AI_STATUS_DANGER_AHEAD               = BIT(3); // There is lava/slime/trigger_hurt ahead
+const int AI_STATUS_OUT_JUMPPAD                = BIT(4); // Trying to get out of a "vertical" jump pad
+const int AI_STATUS_OUT_WATER                  = BIT(5); // Trying to get out of water
+const int AI_STATUS_WAYPOINT_PERSONAL_LINKING  = BIT(6); // Waiting for the personal waypoint to be linked
+const int AI_STATUS_WAYPOINT_PERSONAL_GOING    = BIT(7); // Going to a personal waypoint
+const int AI_STATUS_WAYPOINT_PERSONAL_REACHED  = BIT(8); // Personal waypoint reached
+const int AI_STATUS_JETPACK_FLYING             = BIT(9);
+const int AI_STATUS_JETPACK_LANDING            = BIT(10);
+const int AI_STATUS_STUCK                      = BIT(11); // Cannot reach any goal
 
 .float isbot; // true if this client is actually a bot
 .int aistatus;
@@ -89,6 +89,7 @@ void bot_setnameandstuff(entity this);
 void bot_custom_weapon_priority_setup();
 void bot_endgame();
 void bot_relinkplayerlist();
+void bot_clear(entity this);
 void bot_clientdisconnect(entity this);
 void bot_clientconnect(entity this);
 void bot_removefromlargestteam();
index dd44bbb..8e6b391 100644 (file)
@@ -29,10 +29,6 @@ void havocbot_ai(entity this)
        if(bot_execute_commands(this))
                return;
 
-       if(this.goalcurrent)
-       if(wasfreed(this.goalcurrent))
-               navigation_poproute(this);
-
        if (bot_strategytoken == this)
        if (!bot_strategytoken_taken)
        {
@@ -48,7 +44,7 @@ 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(!(IS_DEAD(this)))
                if(!this.goalcurrent)
                if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
                {
@@ -134,7 +130,7 @@ void havocbot_ai(entity this)
                //heading = this.velocity;
                //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n");
                if(
-                       this.goalstack01 != this && this.goalstack01 != NULL && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
+                       this.goalstack01 != this && this.goalstack01 && !wasfreed(this.goalstack01) && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
                        !(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
                )
                        next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs);
@@ -325,7 +321,7 @@ void havocbot_bunnyhop(entity this, vector dir)
                                        if(this.goalcurrent.classname=="waypoint")
                                        if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
                                        if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z)
-                                       if(this.goalstack01!=NULL)
+                                       if(this.goalstack01 && !wasfreed(this.goalstack01))
                                        {
                                                gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
                                                deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin);
@@ -347,7 +343,8 @@ void havocbot_bunnyhop(entity this, vector dir)
                                        if(checkdistance)
                                        {
                                                this.aistatus &= ~AI_STATUS_RUNNING;
-                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
+                                               // increase stop distance in case the goal is on a slope or a lower platform 
+                                               if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z))
                                                        PHYS_INPUT_BUTTON_JUMP(this) = true;
                                        }
                                        else
@@ -477,7 +474,7 @@ void havocbot_movetogoal(entity this)
 
                // Flying
                PHYS_INPUT_BUTTON_HOOK(this) = true;
-               if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z)
+               if(this.navigation_jetpack_point.z - STAT(PL_MAX, this).z + STAT(PL_MIN, this).z < this.origin.z)
                {
                        this.movement_x = dir * v_forward * maxspeed;
                        this.movement_y = dir * v_right * maxspeed;
@@ -489,6 +486,7 @@ void havocbot_movetogoal(entity this)
        if(this.jumppadcount)
        {
                // If got stuck on the jump pad try to reach the farthest visible waypoint
+               // but with some randomness so it can try out different paths
                if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
                {
                        if(fabs(this.velocity.z)<50)
@@ -501,7 +499,7 @@ void havocbot_movetogoal(entity this)
                                        if(trace_fraction < 1)
                                                continue;
 
-                                       if(!newgoal || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
+                                       if(!newgoal || ((random() < 0.8) && vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin)))
                                                newgoal = it;
                                });
 
@@ -511,6 +509,8 @@ void havocbot_movetogoal(entity this)
                                        this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
                                        navigation_clearroute(this);
                                        navigation_routetogoal(this, newgoal, this.origin);
+                                       if(autocvar_bot_debug_goalstack)
+                                               debuggoalstack(this);
                                        this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
                                }
                        }
@@ -639,8 +639,7 @@ void havocbot_movetogoal(entity this)
        if (this.goalcurrent == NULL)
                return;
 
-       if (this.goalcurrent)
-               navigation_poptouchedgoals(this);
+       navigation_poptouchedgoals(this);
 
        // if ran out of goals try to use an alternative goal or get a new strategy asap
        if(this.goalcurrent == NULL)
@@ -715,10 +714,8 @@ void havocbot_movetogoal(entity this)
                        }
 
                        // avoiding dangers and obstacles
-                       vector dst_ahead, dst_down;
-                       makevectors(this.v_angle.y * '0 1 0');
-                       dst_ahead = this.origin + this.view_ofs + (this.velocity * 0.4) + (v_forward * 32 * 3);
-                       dst_down = dst_ahead - '0 0 1500';
+                       vector dst_ahead = this.origin + this.view_ofs + this.velocity * 0.5;
+                       vector dst_down = dst_ahead - '0 0 3000';
 
                        // Look ahead
                        traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
@@ -755,12 +752,12 @@ void havocbot_movetogoal(entity this)
                        this.aistatus &= ~AI_STATUS_DANGER_AHEAD;
 
                        if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired )
-                       if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || PHYS_INPUT_BUTTON_JUMP(this))
+                       if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || (this.aistatus & AI_STATUS_ROAMING) || PHYS_INPUT_BUTTON_JUMP(this))
                        {
                                // Look downwards
                                traceline(dst_ahead , dst_down, true, NULL);
-                       //      te_lightning2(NULL, this.origin, dst_ahead);    // Draw "ahead" look
-                       //      te_lightning2(NULL, dst_ahead, dst_down);               // Draw "downwards" look
+                               //te_lightning2(NULL, this.origin + this.view_ofs, dst_ahead); // Draw "ahead" look
+                               //te_lightning2(NULL, dst_ahead, dst_down); // Draw "downwards" look
                                if(trace_endpos.z < this.origin.z + this.mins.z)
                                {
                                        s = pointcontents(trace_endpos + '0 0 1');
@@ -769,17 +766,22 @@ void havocbot_movetogoal(entity this)
                                                evadelava = normalize(this.velocity) * -1;
                                        else if (s == CONTENT_SKY)
                                                evadeobstacle = normalize(this.velocity) * -1;
-                                       else if (!boxesoverlap(dst_ahead - this.view_ofs + this.mins, dst_ahead - this.view_ofs + this.maxs,
-                                                               this.goalcurrent.absmin, this.goalcurrent.absmax))
+                                       else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
                                        {
-                                               // if ain't a safe goal with "holes" (like the jumpad on soylent)
-                                               // and there is a trigger_hurt below
-                                               if(tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
+                                               // the traceline check isn't enough but is good as optimization,
+                                               // when not true (most of the time) this tracebox call is avoided
+                                               tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this);
+                                               if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
                                                {
-                                                       // Remove dangerous dynamic goals from stack
-                                                       LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
-                                                       navigation_clearroute(this);
-                                                       return;
+                                                       if (gco.z > this.origin.z + jumpstepheightvec.z)
+                                                       { 
+                                                               // the goal is probably on an upper platform, assume bot can't get there
+                                                               LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
+                                                               navigation_clearroute(this);
+                                                               this.bot_strategytime = 0;
+                                                       }
+                                                       else
+                                                               evadelava = normalize(this.velocity) * -1;
                                                }
                                        }
                                }
index 677ef57..536c8b2 100644 (file)
@@ -34,7 +34,7 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float
                        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)
+                       if(d == CONTENT_WATER || d == CONTENT_SLIME || d == CONTENT_LAVA)
                                continue;
                        if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
                                continue;
@@ -145,12 +145,12 @@ void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org
                        continue;
 
                // not falling
-               if((IS_ONGROUND(it)) == 0)
+               if(!IS_ONGROUND(it))
                {
                        traceline(it.origin, it.origin + '0 0 -1500', true, NULL);
                        t = pointcontents(trace_endpos + '0 0 1');
                        if(t != CONTENT_SOLID )
-                       if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
+                       if(t == CONTENT_WATER || t == CONTENT_SLIME || t == CONTENT_LAVA)
                                continue;
                        if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos))
                                continue;
index 7c71720..4c8982e 100644 (file)
@@ -360,12 +360,12 @@ float navigation_waypoint_will_link(vector v, vector org, entity ent, float walk
                {
                        if (walkfromwp)
                        {
-                               if (tracewalk(ent, v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), org, bot_navigation_movemode))
+                               if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, org, bot_navigation_movemode))
                                        return true;
                        }
                        else
                        {
-                               if (tracewalk(ent, org, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, bot_navigation_movemode))
+                               if (tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, v, bot_navigation_movemode))
                                        return true;
                        }
                }
@@ -387,7 +387,7 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
        });
 
        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
+       org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
        // TODO possibly make other code have the same support for bboxes
        if(ent.tag_entity)
                org = org + ent.tag_entity.origin;
@@ -684,7 +684,7 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
                        float zdistance, xydistance, cost, t, fuel;
                        vector down, npa, npb;
 
-                       down = '0 0 -1' * (STAT(PL_MAX, NULL).z - STAT(PL_MIN, NULL).z) * 10;
+                       down = '0 0 -1' * (STAT(PL_MAX, this).z - STAT(PL_MIN, this).z) * 10;
 
                        do{
                                npa = pointa + down;
@@ -842,7 +842,7 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
                return true;
 
        // if it can reach the goal there is nothing more to do
-       if (tracewalk(this, startposition, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
+       if (tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
                return true;
 
        // see if there are waypoints describing a path to the item
@@ -876,10 +876,15 @@ void navigation_poptouchedgoals(entity this)
        m1 = org + this.mins;
        m2 = org + this.maxs;
 
+       while(this.goalcurrent && wasfreed(this.goalcurrent))
+               navigation_poproute(this);
+
        if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
        {
+               // make sure jumppad is really hit, don't rely on distance based checks
+               // as they may report a touch even if it didn't really happen
                if(this.lastteleporttime>0)
-               if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
+               if(time - this.lastteleporttime < ((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) ? 2 : 0.15))
                {
                        if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
                        if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
@@ -893,7 +898,7 @@ void navigation_poptouchedgoals(entity this)
        }
 
        // If for some reason the bot is closer to the next goal, pop the current one
-       if(this.goalstack01)
+       if(this.goalstack01 && !wasfreed(this.goalstack01))
        if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin))
        if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
        if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode))
@@ -913,14 +918,11 @@ void navigation_poptouchedgoals(entity this)
        if(IS_PLAYER(this.goalcurrent))
                navigation_poproute(this);
 
-       // aid for detecting jump pads better (distance based check fails sometimes)
-       if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && this.jumppadcount > 0 )
-               navigation_poproute(this);
-
        // Loose goal touching check when running
        if(this.aistatus & AI_STATUS_RUNNING)
        if(this.speed >= autocvar_sv_maxspeed) // if -really- running
        if(this.goalcurrent.classname=="waypoint")
+       if(!(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
        {
                if(vdist(this.origin - this.goalcurrent.origin, <, 150))
                {
@@ -942,6 +944,9 @@ void navigation_poptouchedgoals(entity this)
 
        while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
        {
+               if((this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
+                       break;
+
                // Detect personal waypoints
                if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
                if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
@@ -1032,7 +1037,7 @@ void navigation_unstuck(entity this)
 
        if (!bot_waypoint_queue_owner)
        {
-               LOG_DEBUG(this.netname, " sutck, taking over the waypoints queue");
+               LOG_DEBUG(this.netname, " stuck, taking over the waypoints queue");
                bot_waypoint_queue_owner = this;
                bot_waypoint_queue_bestgoal = NULL;
                bot_waypoint_queue_bestgoalrating = 0;
@@ -1044,9 +1049,9 @@ void navigation_unstuck(entity this)
        if (bot_waypoint_queue_goal)
        {
                // evaluate the next goal on the queue
-               float d = vlen(this.origin - bot_waypoint_queue_goal.origin);
+               float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
                LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d));
-               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
+               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
                {
                        if( d > bot_waypoint_queue_bestgoalrating)
                        {
index f960990..0d34ae3 100644 (file)
@@ -485,6 +485,7 @@ float bot_cmd_impulse(entity this)
 
 float bot_cmd_continue(entity this)
 {
+       bot_relinkplayerlist();
        this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
        return CMD_STATUS_FINISHED;
 }
@@ -991,6 +992,7 @@ float bot_cmd_pause(entity this)
        this.movement = '0 0 0';
        this.bot_cmd_keys = BOT_CMD_KEY_NONE;
 
+       bot_clear(this);
        this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
        return CMD_STATUS_FINISHED;
 }
index c83d0f6..1e03bf5 100644 (file)
 #include <lib/warpzone/common.qh>
 #include <lib/warpzone/util_server.qh>
 
+void waypoint_setupmodel(entity wp)
+{
+       if (autocvar_g_waypointeditor)
+       {
+               // TODO: add some sort of visible box in edit mode for box waypoints
+               vector m1 = wp.mins;
+               vector m2 = wp.maxs;
+               setmodel(wp, MDL_WAYPOINT);
+               setsize(wp, m1, m2);
+               wp.effects = EF_LOWPRECISION;
+               if (wp.wpflags & WAYPOINTFLAG_ITEM)
+                       wp.colormod = '1 0 0';
+               else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
+                       wp.colormod = '1 1 0';
+               else
+                       wp.colormod = '1 1 1';
+       }
+       else
+               wp.model = "";
+}
+
 // create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
 // them back to it as well
 // (suitable for spawnfunc_waypoint editor)
@@ -40,7 +61,7 @@ entity waypoint_spawn(vector m1, vector m2, float f)
 
        if(!w.wpisbox)
        {
-               setsize(w, STAT(PL_MIN, NULL) - '1 1 0', STAT(PL_MAX, NULL) + '1 1 0');
+               setsize(w, PL_MIN_CONST - '1 1 0', PL_MAX_CONST + '1 1 0');
                if(!move_out_of_solid(w))
                {
                        if(!(f & WAYPOINTFLAG_GENERATED))
@@ -64,21 +85,7 @@ entity waypoint_spawn(vector m1, vector m2, float f)
        waypoint_clearlinks(w);
        //waypoint_schedulerelink(w);
 
-       if (autocvar_g_waypointeditor)
-       {
-               m1 = w.mins;
-               m2 = w.maxs;
-               setmodel(w, MDL_WAYPOINT); w.effects = EF_LOWPRECISION;
-               setsize(w, m1, m2);
-               if (w.wpflags & WAYPOINTFLAG_ITEM)
-                       w.colormod = '1 0 0';
-               else if (w.wpflags & WAYPOINTFLAG_GENERATED)
-                       w.colormod = '1 1 0';
-               else
-                       w.colormod = '1 1 1';
-       }
-       else
-               w.model = "";
+       waypoint_setupmodel(w);
 
        return w;
 }
@@ -209,7 +216,7 @@ void waypoint_think(entity this)
                        navigation_testtracewalk = 0;
                        if (!this.wpisbox)
                        {
-                               tracebox(sv - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, false, this);
+                               tracebox(sv - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, sv, false, this);
                                if (!trace_startsolid)
                                {
                                        //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
@@ -218,7 +225,7 @@ void waypoint_think(entity this)
                        }
                        if (!it.wpisbox)
                        {
-                               tracebox(ev - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, false, it);
+                               tracebox(ev - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, ev, false, it);
                                if (!trace_startsolid)
                                {
                                        //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
@@ -227,11 +234,11 @@ void waypoint_think(entity this)
                        }
                        //traceline(this.origin, it.origin, false, NULL);
                        //if (trace_fraction == 1)
-                       if (!this.wpisbox && tracewalk(this, sv, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, MOVE_NOMONSTERS))
+                       if (!this.wpisbox && tracewalk(this, sv, PL_MIN_CONST, PL_MAX_CONST, ev, MOVE_NOMONSTERS))
                                waypoint_addlink(this, it);
                        else
                                relink_walkculled += 0.5;
-                       if (!it.wpisbox && tracewalk(it, ev, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, MOVE_NOMONSTERS))
+                       if (!it.wpisbox && tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv, MOVE_NOMONSTERS))
                                waypoint_addlink(it, this);
                        else
                                relink_walkculled += 0.5;
@@ -264,23 +271,8 @@ void waypoint_schedulerelink(entity wp)
 {
        if (wp == NULL)
                return;
-       // TODO: add some sort of visible box in edit mode for box waypoints
-       if (autocvar_g_waypointeditor)
-       {
-               vector m1, m2;
-               m1 = wp.mins;
-               m2 = wp.maxs;
-               setmodel(wp, MDL_WAYPOINT); wp.effects = EF_LOWPRECISION;
-               setsize(wp, m1, m2);
-               if (wp.wpflags & WAYPOINTFLAG_ITEM)
-                       wp.colormod = '1 0 0';
-               else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
-                       wp.colormod = '1 1 0';
-               else
-                       wp.colormod = '1 1 1';
-       }
-       else
-               wp.model = "";
+
+       waypoint_setupmodel(wp);
        wp.wpisbox = vdist(wp.size, >, 0);
        wp.enemy = NULL;
        if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL))
@@ -693,7 +685,7 @@ float waypoint_loadall()
 
 vector waypoint_fixorigin(vector position)
 {
-       tracebox(position + '0 0 1' * (1 - STAT(PL_MIN, NULL).z), STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), position + '0 0 -512', MOVE_NOMONSTERS, NULL);
+       tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
        if(trace_fraction < 1)
                position = trace_endpos;
        //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
@@ -841,7 +833,7 @@ void botframe_showwaypointlinks()
 
 float botframe_autowaypoints_fixdown(vector v)
 {
-       tracebox(v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v + '0 0 -64', MOVE_NOMONSTERS, NULL);
+       tracebox(v, PL_MIN_CONST, PL_MAX_CONST, v + '0 0 -64', MOVE_NOMONSTERS, NULL);
        if(trace_fraction >= 1)
                return 0;
        return 1;
index cf8a456..3d56922 100644 (file)
@@ -444,7 +444,7 @@ float CheatCommand(entity this, int argc)
                        e.nextthink = time;
                        e.solid = 0; // nothing special
                        setmodel(e, MDL_MARKER);
-                       setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+                       setsize(e, STAT(PL_MIN, this), STAT(PL_MAX, this));
                        e.skin = 2;
                        if(argc == 3)
                                e.cnt = stof(argv(1));
index 04c35a1..d4a3e9f 100644 (file)
@@ -236,7 +236,7 @@ void PutObserverInServer(entity this)
         this.angles_z = 0;
         this.fixangle = true;
         // offset it so that the spectator spawns higher off the ground, looks better this way
-        setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL));
+        setorigin(this, spot.origin + STAT(PL_VIEW_OFS, this));
         this.prevorigin = this.origin;
         if (IS_REAL_CLIENT(this))
         {
@@ -253,7 +253,7 @@ void PutObserverInServer(entity this)
                FixPlayermodel(this);
         }
         setmodel(this, MDL_Null);
-        setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL));
+        setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
         this.view_ofs = '0 0 0';
     }
 
@@ -284,8 +284,8 @@ void PutObserverInServer(entity this)
        if (this.killcount != FRAGS_SPECTATOR)
        {
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
-               if(!intermission_running)
-               if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
+               if(!gameover)
+               if(autocvar_g_chat_nospectators == 1 || (!warmup_stage && autocvar_g_chat_nospectators == 2))
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
 
                if(this.just_joined == false) {
@@ -2320,8 +2320,9 @@ void PlayerPreThink (entity this)
        if (IS_PLAYER(this)) {
                CheckRules_Player(this);
 
-               if (intermission_running) {
-                       IntermissionThink(this);
+               if (gameover || intermission_running) {
+                       if(intermission_running)
+                               IntermissionThink(this);
                        return;
                }
 
@@ -2472,8 +2473,9 @@ void PlayerPreThink (entity this)
 
                this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
        }
-       else if (gameover) {
-               if (intermission_running) IntermissionThink(this);
+       else if (gameover || intermission_running) {
+               if(intermission_running)
+                       IntermissionThink(this);
                return;
        }
        else if (IS_OBSERVER(this)) {
@@ -2615,13 +2617,23 @@ void PlayerPostThink (entity this)
        CheatFrame(this);
 
        //CheckPlayerJump();
+       if (gameover)
+       {
+               this.solid = SOLID_NOT;
+               this.takedamage = DAMAGE_NO;
+               set_movetype(this, MOVETYPE_NONE);
+       }
 
        if (IS_PLAYER(this)) {
                DrownPlayer(this);
                CheckRules_Player(this);
                UpdateChatBubble(this);
                if (this.impulse) ImpulseCommands(this);
-               if (intermission_running) return; // intermission or finale
+               if (gameover)
+               {
+                       CSQCMODEL_AUTOUPDATE(this);
+                       return;
+               }
                GetPressedKeys(this);
        }
 
index 5f2c86e..3f8e4c4 100644 (file)
@@ -2,7 +2,6 @@
 
 .float cmd_floodtime;
 .float cmd_floodcount;
-.float lms_spectate_warning;
 
 string MapVote_Suggest(entity this, string m);
 
index 5017e81..d01448a 100644 (file)
@@ -242,16 +242,22 @@ string getmaplist()
        return sprintf("^7Maps in list: %s\n", maplist);
 }
 
-
+const int LSMAPS_MAX = 250;
 string getlsmaps()
 {
        string lsmaps = "", col;
-       float i, newmaps = 0;
+       bool newmaps = false;
+       int added = 0;
 
-       for (i = 0; i < MapInfo_count; ++i)
+       for (int i = 0; i < MapInfo_count; ++i)
        {
                if ((MapInfo_Get_ByID(i)) && !(MapInfo_Map_flags & MapInfo_ForbiddenFlags()))
                {
+                       ++added;
+
+                       if(added > LSMAPS_MAX)
+                               continue; // we still get the added count, but skip the actual processing
+
                        // todo: Check by play count of maps for other game types?
                        if (
                            (g_race && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time"))))
@@ -271,19 +277,22 @@ string getlsmaps()
                }
        }
 
+       if(added > LSMAPS_MAX)
+               lsmaps = sprintf("%s^7(%d not listed)", lsmaps, added - LSMAPS_MAX);
+
        MapInfo_ClearTemps();
-       return sprintf("^7Maps available (%d)%s: %s\n", tokenize_console(lsmaps), (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps);
+       return sprintf("^7Maps available (%d)%s: %s\n", added, (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps);
 }
 
 string getmonsterlist()
 {
-       string monsterlist = "", col;
+       string monsterlist = "";
 
-       for (int i = MON_FIRST; i <= MON_LAST; ++i)
+       FOREACH(Monsters, it != MON_Null,
        {
-               if (i % 2) col = "^2"; else col = "^3";
-               monsterlist = sprintf("%s%s%s ", monsterlist, col, (get_monsterinfo(i)).netname);
-       }
+               string col = ((i % 2) ? "^2" : "^3");
+               monsterlist = sprintf("%s%s%s ", monsterlist, col, it.netname);
+       });
 
        return sprintf("^7Monsters available: %s\n", monsterlist);
 }
index f99ed23..8c9d915 100644 (file)
@@ -382,10 +382,16 @@ void GameCommand_bot_cmd(float request, float argc, string command)
                                                }
                                                else
                                                {
-                                                       // let's start at token 2 so we can skip sv_cmd bot_cmd
-                                                       bot = find_bot_by_number(stof(argv(2)));
-                                                       if (bot == NULL) bot = find_bot_by_name(argv(2));
-                                                       if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1));
+                                                       if(argv(2) == "*" || argv(2) == "all")
+                                                               FOREACH_CLIENT(IS_BOT_CLIENT(it), {
+                                                                       bot_queuecommand(it, substring(s, argv_start_index(3), -1));
+                                                               });
+                                                       else
+                                                       {
+                                                               bot = find_bot_by_number(stof(argv(2)));
+                                                               if (bot == NULL) bot = find_bot_by_name(argv(2));
+                                                               if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1));
+                                                       }
                                                }
                                        }
                                        else
@@ -407,17 +413,31 @@ void GameCommand_bot_cmd(float request, float argc, string command)
                        }
                        else if (argc >= 3)  // this comes last
                        {
-                               bot = find_bot_by_number(stof(argv(1)));
-                               if (bot == NULL) bot = find_bot_by_name(argv(1));
-                               if (bot)
+                               if(argv(1) == "*" || argv(1) == "all")
                                {
-                                       LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n"));
-                                       bot_queuecommand(bot, substring(command, argv_start_index(2), -1));
+                                       int bot_num = 0;
+                                       FOREACH_CLIENT(IS_BOT_CLIENT(it), {
+                                               bot_queuecommand(it, substring(command, argv_start_index(2), -1));
+                                               bot_num++;
+                                       });
+                                       if(bot_num)
+                                               LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to all bots (", ftos(bot_num), ")\n"));
                                        return;
                                }
                                else
                                {
-                                       LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n"));  // don't return so that usage is shown
+                                       bot = find_bot_by_number(stof(argv(1)));
+                                       if (bot == NULL) bot = find_bot_by_name(argv(1));
+                                       if (bot)
+                                       {
+                                               LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n"));
+                                               bot_queuecommand(bot, substring(command, argv_start_index(2), -1));
+                                               return;
+                                       }
+                                       else
+                                       {
+                                               LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n"));  // don't return so that usage is shown
+                                       }
                                }
                        }
                }
@@ -427,10 +447,12 @@ void GameCommand_bot_cmd(float request, float argc, string command)
                case CMD_REQUEST_USAGE:
                {
                        LOG_INFO("\nUsage:^3 sv_cmd bot_cmd client command [argument]\n");
-                       LOG_INFO("  'client' can be either the name or entity id of the bot\n");
+                       LOG_INFO("  'client' can be either the name of the bot or a progressive number (not the entity number!)\n");
+                       LOG_INFO("           can also be '*' or 'all' to allow sending the command to all the bots\n");
                        LOG_INFO("  For full list of commands, see bot_cmd help [command].\n");
-                       LOG_INFO("Examples: sv_cmd bot_cmd client cc \"say something\"\n");
-                       LOG_INFO("          sv_cmd bot_cmd client presskey jump\n");
+                       LOG_INFO("Examples: sv_cmd bot_cmd 1 cc \"say something\"\n");
+                       LOG_INFO("          sv_cmd bot_cmd 1 presskey jump\n");
+                       LOG_INFO("          sv_cmd bot_cmd * pause\n");
                        return;
                }
        }
@@ -1452,15 +1474,15 @@ void GameCommand_trace(float request, float argc)
                                                start = stov(vtos(start));
                                                end = stov(vtos(end));
 
-                                               tracebox(start, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), end, MOVE_NOMONSTERS, NULL);
+                                               tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL);
                                                if (!trace_startsolid && trace_fraction < 1)
                                                {
                                                        p = trace_endpos;
-                                                       tracebox(p, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), p, MOVE_NOMONSTERS, NULL);
+                                                       tracebox(p, PL_MIN_CONST, PL_MAX_CONST, p, MOVE_NOMONSTERS, NULL);
                                                        if (trace_startsolid)
                                                        {
                                                                rint(42);  // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
-                                                               tracebox(start, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), end, MOVE_NOMONSTERS, NULL);
+                                                               tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL);
 
                                                                // how much do we need to back off?
                                                                safe = 1;
@@ -1468,7 +1490,7 @@ void GameCommand_trace(float request, float argc)
                                                                for ( ; ; )
                                                                {
                                                                        pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
-                                                                       tracebox(pos, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), pos, MOVE_NOMONSTERS, NULL);
+                                                                       tracebox(pos, PL_MIN_CONST, PL_MAX_CONST, pos, MOVE_NOMONSTERS, NULL);
                                                                        if (trace_startsolid)
                                                                        {
                                                                                if ((safe + unsafe) * 0.5 == unsafe) break;
@@ -1484,7 +1506,7 @@ void GameCommand_trace(float request, float argc)
                                                                LOG_INFO("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n");
                                                                LOG_INFO("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n");
 
-                                                               tracebox(p, STAT(PL_MIN, NULL) + '0.1 0.1 0.1', STAT(PL_MAX, NULL) - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL);
+                                                               tracebox(p, PL_MIN_CONST + '0.1 0.1 0.1', PL_MAX_CONST - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL);
                                                                if (trace_startsolid) LOG_INFO("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
                                                                else LOG_INFO("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
                                                                if (++hitcount >= 10) break;
@@ -1498,7 +1520,7 @@ void GameCommand_trace(float request, float argc)
                                                                {
                                                                        q = p + normalize(end - p) * (dq + dqf);
                                                                        if (q == q0) break;
-                                                                       tracebox(p, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), q, MOVE_NOMONSTERS, NULL);
+                                                                       tracebox(p, PL_MIN_CONST, PL_MAX_CONST, q, MOVE_NOMONSTERS, NULL);
                                                                        if (trace_startsolid) error("THIS ONE cannot happen");
                                                                        if (trace_fraction > 0) dq += dqf * trace_fraction;
                                                                        dqf *= 0.5;
index 85c5d18..b91a8f4 100644 (file)
@@ -464,8 +464,7 @@ void ReadyRestart_force()
 
 void ReadyRestart()
 {
-       // no assault support yet...
-       if (g_assault | gameover | intermission_running | race_completing) localcmd("restart\n");
+       if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || gameover || race_completing) localcmd("restart\n");
        else localcmd("\nsv_hook_gamerestart\n");
 
        // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
diff --git a/qcsrc/server/compat/_mod.inc b/qcsrc/server/compat/_mod.inc
new file mode 100644 (file)
index 0000000..69d031e
--- /dev/null
@@ -0,0 +1,5 @@
+// generated file; do not modify
+#include <server/compat/halflife.qc>
+#include <server/compat/quake.qc>
+#include <server/compat/quake3.qc>
+#include <server/compat/wop.qc>
diff --git a/qcsrc/server/compat/_mod.qh b/qcsrc/server/compat/_mod.qh
new file mode 100644 (file)
index 0000000..bd65858
--- /dev/null
@@ -0,0 +1,5 @@
+// generated file; do not modify
+#include <server/compat/halflife.qh>
+#include <server/compat/quake.qh>
+#include <server/compat/quake3.qh>
+#include <server/compat/wop.qh>
diff --git a/qcsrc/server/compat/halflife.qc b/qcsrc/server/compat/halflife.qc
new file mode 100644 (file)
index 0000000..74e5013
--- /dev/null
@@ -0,0 +1,35 @@
+#include "halflife.qh"
+.float  roomtype;
+.float  radius;
+.float  pitch;
+.float  renderamt;
+.float  rendermode;
+.vector rendercolor;
+
+spawnfunc(weapon_crossbow) {}
+spawnfunc(weapon_handgrenade) {}
+spawnfunc(ammo_crossbow) {}
+spawnfunc(ammo_9mmclip) {}
+spawnfunc(ammo_gaussclip) {}
+spawnfunc(weapon_rpg) {}
+spawnfunc(weapon_357) {}
+void ammo_ARgrenades() {}
+spawnfunc(item_battery) {}
+spawnfunc(ammo_rpgclip) {}
+void weapon_9mmAR() {}
+spawnfunc(weapon_tripmine) {}
+spawnfunc(weapon_snark) {}
+spawnfunc(ammo_buckshot) {}
+void ammo_9mmAR() {}
+spawnfunc(ammo_357) {}
+spawnfunc(weapon_gauss) {}
+spawnfunc(weapon_hornetgun) {}
+//spawnfunc(weapon_shotgun) {}
+spawnfunc(item_healthkit) {}
+spawnfunc(item_longjump) {}
+spawnfunc(item_antidote) {}
+spawnfunc(func_recharge) {}
+spawnfunc(info_node) {}
+spawnfunc(env_sound) {}
+spawnfunc(light_spot) {}
+spawnfunc(func_healthcharger) {}
diff --git a/qcsrc/server/compat/halflife.qh b/qcsrc/server/compat/halflife.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/compat/quake.qc b/qcsrc/server/compat/quake.qc
new file mode 100644 (file)
index 0000000..539042d
--- /dev/null
@@ -0,0 +1,30 @@
+#include "quake.qh"
+
+#include <common/weapons/_all.qh>
+
+spawnfunc(weapon_electro);
+spawnfunc(weapon_hagar);
+spawnfunc(weapon_machinegun);
+spawnfunc(item_bullets);
+spawnfunc(item_armor_mega);
+spawnfunc(item_health_mega);
+spawnfunc(item_health_medium);
+
+//***********************
+//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons
+//***********************
+spawnfunc(weapon_nailgun) {spawnfunc_weapon_electro(this);}
+spawnfunc(weapon_supernailgun) {spawnfunc_weapon_hagar(this);}
+spawnfunc(weapon_supershotgun) {spawnfunc_weapon_machinegun(this);}
+
+spawnfunc(item_spikes) {spawnfunc_item_bullets(this);}
+//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);}  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
+spawnfunc(item_armor2) {spawnfunc_item_armor_mega(this);}
+spawnfunc(item_armorInv) {spawnfunc_item_armor_mega(this);} // TODO: make sure we actually want this
+spawnfunc(item_health) {if (this.spawnflags & 2) spawnfunc_item_health_mega(this);else spawnfunc_item_health_medium(this);}
+
+//spawnfunc_item_spikes
+//spawnfunc_item_health
+
+
+
diff --git a/qcsrc/server/compat/quake.qh b/qcsrc/server/compat/quake.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc
new file mode 100644 (file)
index 0000000..1c53de3
--- /dev/null
@@ -0,0 +1,208 @@
+#include "quake3.qh"
+
+#include <common/weapons/_all.qh>
+
+spawnfunc(weapon_crylink);
+spawnfunc(weapon_electro);
+spawnfunc(weapon_hagar);
+spawnfunc(weapon_machinegun);
+spawnfunc(weapon_vortex);
+
+spawnfunc(target_items);
+
+spawnfunc(item_bullets);
+spawnfunc(item_cells);
+spawnfunc(item_rockets);
+spawnfunc(item_shells);
+
+spawnfunc(item_jetpack);
+
+spawnfunc(item_armor_big);
+spawnfunc(item_armor_mega);
+spawnfunc(item_armor_small);
+
+spawnfunc(item_health_medium);
+spawnfunc(item_health_mega);
+
+//***********************
+//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons
+//***********************
+
+// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG
+
+// SG -> SG
+spawnfunc(ammo_shells)         { spawnfunc_item_shells(this);         }
+
+// MG -> MG
+spawnfunc(ammo_bullets)        { spawnfunc_item_bullets(this);        }
+
+// GL -> Mortar
+spawnfunc(ammo_grenades)       { spawnfunc_item_rockets(this);        }
+
+// LG -> Lightning
+spawnfunc(weapon_lightning)    { spawnfunc_weapon_electro(this);      }
+spawnfunc(ammo_lightning)      { spawnfunc_item_cells(this);          }
+
+// Plasma -> Hagar
+spawnfunc(weapon_plasmagun)    { spawnfunc_weapon_hagar(this);        }
+spawnfunc(ammo_cells)          { spawnfunc_item_rockets(this);        }
+
+// Rail -> Vortex
+spawnfunc(weapon_railgun)      { spawnfunc_weapon_vortex(this);          }
+spawnfunc(ammo_slugs)          { spawnfunc_item_cells(this);          }
+
+// BFG -> Crylink
+spawnfunc(weapon_bfg)          { spawnfunc_weapon_crylink(this);      }
+spawnfunc(ammo_bfg)            { spawnfunc_item_cells(this);          }
+
+// RL -> RL
+spawnfunc(ammo_rockets)        { spawnfunc_item_rockets(this);        }
+
+// Armor
+spawnfunc(item_armor_body)     { spawnfunc_item_armor_mega(this);    }
+spawnfunc(item_armor_combat)   { spawnfunc_item_armor_big(this);      }
+spawnfunc(item_armor_shard)    { spawnfunc_item_armor_small(this);    }
+spawnfunc(item_enviro)         { spawnfunc_item_invincible(this);     }
+
+.float wait;
+.float delay;
+
+// weapon remove ent from df
+void target_init_verify(entity this)
+{
+       entity trigger, targ;
+       for(trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); )
+               for(targ = NULL; (targ = find(targ, targetname, trigger.target)); )
+                       if (targ.classname == "target_init" || targ.classname == "target_give" || targ.classname == "target_items")
+                       {
+                               trigger.wait = 0;
+                               trigger.delay = 0;
+                               targ.wait = 0;
+                               targ.delay = 0;
+
+                               //setsize(targ, trigger.mins, trigger.maxs);
+                               //setorigin(targ, trigger.origin);
+                               //remove(trigger);
+                       }
+}
+
+spawnfunc(target_init)
+{
+       this.spawnflags = 0; // remove all weapons except the ones listed below
+       this.netname = "shotgun"; // keep these weapons through the remove trigger
+       spawnfunc_target_items(this);
+       InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET);
+}
+
+// weapon give ent from defrag
+void target_give_init(entity this)
+{
+       IL_EACH(g_items, it.targetname == this.target,
+       {
+               if (it.classname == "weapon_rocketlauncher" || it.classname == "weapon_devastator") {
+                       this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo);
+                       this.netname = "devastator";
+               }
+               else if (it.classname == "weapon_plasmagun") {
+                       this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
+                       if(this.netname == "")
+                               this.netname = "hagar";
+                       else
+                               this.netname = strcat(this.netname, " hagar");
+               }
+               else if (it.classname == "weapon_bfg") {
+                       this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo);
+                       if(this.netname == "")
+                               this.netname = "crylink";
+                       else
+                               this.netname = strcat(this.netname, " crylink");
+               }
+               else if (it.classname == "weapon_grenadelauncher" || it.classname == "weapon_mortar") {
+                       this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO
+                       if(this.netname == "")
+                               this.netname = "mortar";
+                       else
+                               this.netname = strcat(this.netname, " mortar");
+               }
+               else if (it.classname == "item_armor_body")
+                       this.armorvalue = 100;
+               else if (it.classname == "item_health_mega")
+                       this.health = 200;
+               //remove(it); // removing ents in init functions causes havoc, workaround:
+        setthink(it, SUB_Remove);
+        it.nextthink = time;
+       });
+       this.spawnflags = 2;
+       spawnfunc_target_items(this);
+       InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET);
+}
+
+spawnfunc(target_give)
+{
+       InitializeEntity(this, target_give_init, INITPRIO_FINDTARGET);
+}
+
+//spawnfunc(item_flight)       /* handled by jetpack */
+//spawnfunc(item_haste)        /* handled by buffs mutator */
+//spawnfunc(item_health)       /* handled in t_quake.qc */
+//spawnfunc(item_health_large) /* handled in t_items.qc */
+//spawnfunc(item_health_small) /* handled in t_items.qc */
+//spawnfunc(item_health_mega)  /* handled in t_items.qc */
+//spawnfunc(item_invis)        /* handled by buffs mutator */
+//spawnfunc(item_regen)        /* handled by buffs mutator */
+
+// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
+
+spawnfunc(item_flight)
+{
+       spawnfunc_item_jetpack(this);
+}
+
+.float notteam;
+.float notsingle;
+.float notfree;
+.float notq3a;
+.float notta;
+.string gametype;
+bool DoesQ3ARemoveThisEntity(entity this)
+{
+       // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY)
+
+       if(this.notq3a)
+               if(!teamplay || g_tdm || g_ctf)
+                       return true;
+
+       if(this.notta)
+               if (!(!teamplay || g_tdm || g_ctf))
+                       return true;
+
+       if(this.notsingle)
+               if(maxclients == 1)
+                       return true;
+
+       if(this.notteam)
+               if(teamplay)
+                       return true;
+
+       if(this.notfree)
+               if(!teamplay)
+                       return true;
+
+       if(this.gametype)
+       {
+               string gametypename;
+               // static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"}
+               gametypename = "ffa";
+               if(teamplay)
+                       gametypename = "team";
+               if(g_ctf)
+                       gametypename = "ctf";
+               if(maxclients == 1)
+                       gametypename = "single";
+               // we do not have the other types (oneflag, obelisk, harvester, teamtournament)
+               if(strstrofs(this.gametype, gametypename, 0) < 0)
+                       return true;
+       }
+
+       return false;
+}
diff --git a/qcsrc/server/compat/quake3.qh b/qcsrc/server/compat/quake3.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/qcsrc/server/compat/wop.qc b/qcsrc/server/compat/wop.qc
new file mode 100644 (file)
index 0000000..6d53e18
--- /dev/null
@@ -0,0 +1,62 @@
+#include "wop.qh"
+
+#include <common/weapons/_all.qh>
+// #include <server/mutators/gamemode.qh>
+
+spawnfunc(weapon_arc);
+spawnfunc(weapon_crylink);
+spawnfunc(weapon_electro);
+spawnfunc(weapon_mortar);
+spawnfunc(weapon_hagar);
+spawnfunc(weapon_machinegun);
+spawnfunc(weapon_devastator);
+spawnfunc(weapon_shotgun);
+spawnfunc(weapon_vortex);
+
+spawnfunc(item_armor_big);
+spawnfunc(item_armor_mega);
+spawnfunc(item_armor_small);
+
+spawnfunc(item_bullets);
+spawnfunc(item_cells);
+spawnfunc(item_quad);
+spawnfunc(item_rockets);
+spawnfunc(item_shells);
+
+spawnfunc(item_jetpack);
+
+spawnfunc(item_haste);
+spawnfunc(item_health_medium);
+spawnfunc(item_health_mega);
+spawnfunc(item_invis);
+spawnfunc(item_medic);
+
+//***********************
+//WORD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
+//***********************
+
+spawnfunc(weapon_punchy)       { spawnfunc_weapon_arc(this);                   }
+spawnfunc(weapon_nipper)       { spawnfunc_weapon_machinegun(this);    }
+spawnfunc(weapon_pumper)       { spawnfunc_weapon_shotgun(this);               }
+spawnfunc(weapon_boaster)      { spawnfunc_weapon_electro(this);               }
+spawnfunc(weapon_splasher)     { spawnfunc_weapon_vortex(this);                }
+spawnfunc(weapon_bubbleg)      { spawnfunc_weapon_hagar(this);                 }
+spawnfunc(weapon_balloony)     { spawnfunc_weapon_mortar(this);                }
+spawnfunc(weapon_betty)                { spawnfunc_weapon_devastator(this);    }
+spawnfunc(weapon_imperius)     { spawnfunc_weapon_crylink(this);               }
+
+spawnfunc(ammo_pumper)         { spawnfunc_item_shells(this);                  }
+spawnfunc(ammo_nipper)         { spawnfunc_item_bullets(this);                 }
+spawnfunc(ammo_balloony)       { spawnfunc_item_rockets(this);                 }
+spawnfunc(ammo_bubbleg)                { spawnfunc_item_rockets(this);                 }
+spawnfunc(ammo_boaster)                { spawnfunc_item_cells(this);                   }
+spawnfunc(ammo_betty)          { spawnfunc_item_rockets(this);                 }
+spawnfunc(ammo_imperius)       { spawnfunc_item_cells(this);                   }
+
+spawnfunc(item_padpower)       { spawnfunc_item_quad(this);                    }
+spawnfunc(item_climber)                { spawnfunc_item_invincible(this);              }
+spawnfunc(item_speedy)         { spawnfunc_item_haste(this);                   }
+spawnfunc(item_jump)           { spawnfunc_item_jetpack(this);                 }
+spawnfunc(item_visionless)     { spawnfunc_item_invis(this);                   }
+spawnfunc(item_revival)                { spawnfunc_item_medic(this);                   }
+spawnfunc(item_armor_padshield)        { spawnfunc_item_armor_mega(this);      }
diff --git a/qcsrc/server/compat/wop.qh b/qcsrc/server/compat/wop.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 52153fc..fedd5eb 100644 (file)
@@ -129,7 +129,6 @@ const int W_TICSPERFRAME = 2;
 
 void weapon_defaultspawnfunc(entity this, Weapon e);
 
-float gameover;
 float intermission_running;
 float intermission_exittime;
 float alreadychangedlevel;
diff --git