Merge branch 'master' into terencehill/bot_waypoints
authorterencehill <piuntn@gmail.com>
Fri, 1 Dec 2017 14:39:56 +0000 (15:39 +0100)
committerterencehill <piuntn@gmail.com>
Fri, 1 Dec 2017 14:39:56 +0000 (15:39 +0100)
176 files changed:
.gitlab-ci.yml
defaultServer.cfg
mutators.cfg
physics.cfg
physicsCPMA.cfg
physicsFruit.cfg
physicsHavoc.cfg
physicsLeeStricklin-ModdedFruit.cfg
physicsLeeStricklin.cfg
physicsLeeStricklinOld.cfg
physicsLzd.cfg
physicsNexuiz10.cfg
physicsNexuiz11.cfg
physicsNexuiz151.cfg
physicsNexuiz151b.cfg
physicsNexuiz16rc1.cfg
physicsNexuiz20.cfg
physicsNexuiz25.cfg
physicsNexuiz26.cfg
physicsNoQWBunny-nexbased.cfg
physicsOverkill.cfg
physicsQ.cfg
physicsQ2.cfg
physicsQ2a.cfg
physicsQ3.cfg
physicsQBF.cfg
physicsQBFplus.cfg
physicsSamual.cfg
physicsWarsow.cfg
physicsWarsowClassicBunny.cfg
physicsWarsowDev.cfg
physicsX.cfg
physicsX010.cfg
physicsX07.cfg
physicsXDF.cfg
physicsXDFLight.cfg
qcsrc/client/hud/panel/physics.qc
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/client/view.qc
qcsrc/common/gamemodes/gamemode/nexball/nexball.qc
qcsrc/common/items/inventory.qh
qcsrc/common/items/item.qh
qcsrc/common/items/item/ammo.qc
qcsrc/common/items/item/ammo.qh
qcsrc/common/items/item/armor.qh
qcsrc/common/items/item/health.qh
qcsrc/common/items/item/jetpack.qc
qcsrc/common/items/item/jetpack.qh
qcsrc/common/items/item/pickup.qc
qcsrc/common/items/item/pickup.qh
qcsrc/common/items/item/powerup.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/_mod.inc
qcsrc/common/mutators/mutator/_mod.qh
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/dodging/sv_dodging.qc
qcsrc/common/mutators/mutator/instagib/_mod.inc
qcsrc/common/mutators/mutator/instagib/items.qh
qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
qcsrc/common/mutators/mutator/instagib/sv_instagib.qh
qcsrc/common/mutators/mutator/instagib/sv_items.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc
qcsrc/common/mutators/mutator/overkill/hmg.qc
qcsrc/common/mutators/mutator/overkill/hmg.qh
qcsrc/common/mutators/mutator/overkill/rpc.qc
qcsrc/common/mutators/mutator/overkill/rpc.qh
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
qcsrc/common/mutators/mutator/overkill/sv_overkill.qh
qcsrc/common/mutators/mutator/random_items/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_items/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_items/sv_random_items.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_items/sv_random_items.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/stale_move_negation/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/stale_move_negation/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qh [new file with mode: 0644]
qcsrc/common/notifications/all.inc
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/physics/player.qc
qcsrc/common/physics/player.qh
qcsrc/common/playerstats.qc
qcsrc/common/playerstats.qh
qcsrc/common/scores.qh
qcsrc/common/stats.qh
qcsrc/common/t_items.qc
qcsrc/common/t_items.qh
qcsrc/common/triggers/func/button.qc
qcsrc/common/triggers/func/button.qh
qcsrc/common/triggers/misc/teleport_dest.qc
qcsrc/common/triggers/teleporters.qc
qcsrc/common/triggers/trigger/heal.qc
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/triggers/trigger/relay.qc
qcsrc/common/triggers/trigger/teleport.qc
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/arc.qh
qcsrc/common/weapons/weapon/blaster.qc
qcsrc/common/weapons/weapon/blaster.qh
qcsrc/common/weapons/weapon/crylink.qc
qcsrc/common/weapons/weapon/crylink.qh
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/devastator.qh
qcsrc/common/weapons/weapon/electro.qc
qcsrc/common/weapons/weapon/electro.qh
qcsrc/common/weapons/weapon/fireball.qc
qcsrc/common/weapons/weapon/fireball.qh
qcsrc/common/weapons/weapon/hagar.qc
qcsrc/common/weapons/weapon/hagar.qh
qcsrc/common/weapons/weapon/hlac.qc
qcsrc/common/weapons/weapon/hlac.qh
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/hook.qh
qcsrc/common/weapons/weapon/machinegun.qc
qcsrc/common/weapons/weapon/machinegun.qh
qcsrc/common/weapons/weapon/minelayer.qc
qcsrc/common/weapons/weapon/minelayer.qh
qcsrc/common/weapons/weapon/mortar.qc
qcsrc/common/weapons/weapon/mortar.qh
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/porto.qh
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/common/weapons/weapon/rifle.qh
qcsrc/common/weapons/weapon/seeker.qc
qcsrc/common/weapons/weapon/seeker.qh
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shockwave.qh
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/shotgun.qh
qcsrc/common/weapons/weapon/tuba.qc
qcsrc/common/weapons/weapon/tuba.qh
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/weapons/weapon/vaporizer.qh
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/common/weapons/weapon/vortex.qh
qcsrc/lib/warpzone/common.qc
qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/anticheat.qc
qcsrc/server/antilag.qc
qcsrc/server/antilag.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/default/havocbot/roles.qc
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/command/vote.qc
qcsrc/server/compat/quake.qc
qcsrc/server/compat/quake2.qc
qcsrc/server/compat/quake3.qc
qcsrc/server/compat/wop.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_world.qc
qcsrc/server/items.qc [new file with mode: 0644]
qcsrc/server/items.qh [new file with mode: 0644]
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/events.qh
qcsrc/server/mutators/mutator/gamemode_ca.qc
qcsrc/server/mutators/mutator/gamemode_cts.qc
qcsrc/server/mutators/mutator/gamemode_lms.qc
qcsrc/server/player.qc
qcsrc/server/scores.qc
qcsrc/server/scores.qh
qcsrc/server/scores_rules.qc
qcsrc/server/teamplay.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/spawning.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qh
randomitems-xonotic.cfg [new file with mode: 0644]

index 9305f527cd3abc9640d17bff880e021b854d7b17..9e6a7b6cda7cbe603a22e16bd0631195160c6168 100644 (file)
@@ -29,7 +29,7 @@ test_sv_game:
     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
     - make
-    - EXPECT=ed9be8d1b1a544f89bcdd7d36876fede
+    - EXPECT=3fb0c7a99263dd44e026804c12da2fa2
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index 47037c069b62ca6aea1538be444c949192e3f721..99845f6480f256dd2eaf79057b48ce76997b5a5b 100644 (file)
@@ -77,6 +77,7 @@ set sv_jumpspeedcap_min "" "lower bound on the baseline velocity of a jump; fina
 set sv_jumpspeedcap_max "" "upper bound on the baseline velocity of a jump; final velocity will be <= (jumpheight * max + jumpheight)"
 set sv_jumpspeedcap_max_disable_on_ramps 0 "disable upper baseline velocity bound on ramps to preserve the old rampjump style"
 set sv_track_canjump 0 "track if the player released the jump key between 2 jumps to decide if they are able to jump or not"
+set sv_jumpvelocity_crouch 0 "jump height while crouching, set to 0 to use regular jump height"
 
 set sv_precacheplayermodels 1
 set sv_precacheweapons 0
index c57088dd92910c1e1915d8bd61ba31918b3eb085..79ac41791ade308fde3b9371f47757b23b2656b0 100644 (file)
@@ -12,17 +12,21 @@ seta cl_dodging_timeout 0.2 "determines how long apart (in seconds) two taps on
 
 set sv_dodging_air_dodging 0
 set sv_dodging_wall_dodging 0 "allow dodging off walls"
-set sv_dodging_delay 0.5 "determines how long a player has to wait to be able to dodge again after dodging"
+set sv_dodging_delay 0.6 "determines how long a player has to wait to be able to dodge again after dodging"
 set sv_dodging_up_speed 200 "the jump velocity of the dodge"
-set sv_dodging_horiz_speed 400 "the horizontal velocity of the dodge"
-set sv_dodging_horiz_speed_frozen 200 "the horizontal velocity of the dodge while frozen"
+set sv_dodging_horiz_speed_min 200 "the lower bound of current velocity for force scaling"
+set sv_dodging_horiz_speed_max 1000 "the upper bound of current velocity for force scaling"
+set sv_dodging_horiz_force_slowest 400 "the horizontal velocity of the dodge when current velocity is <= sv_dodging_horiz_speed_min, values between min and max are linearly scaled"
+set sv_dodging_horiz_force_fastest 400 "the horizontal velocity of the dodge when current velocity is >= sv_dodging_horiz_speed_max, values between min and max are linearly scaled"
+set sv_dodging_horiz_force_frozen 200 "the horizontal velocity of the dodge while frozen"
 set sv_dodging_ramp_time 0.1 "a ramp so that the horizontal part of the dodge is added smoothly (seconds)"
 set sv_dodging_height_threshold 10 "the maximum height above ground where to allow dodging"
 set sv_dodging_wall_distance_threshold 10 "the maximum distance from a wall that still allows dodging"
 set sv_dodging_sound 1 "if 1 dodging makes a sound. if 0 dodging is silent"
 set sv_dodging_frozen 0 "allow dodging while frozen"
 set sv_dodging_frozen_doubletap 0
-set sv_dodging_maxspeed 450 "maximum speed a player can be moving at before they dodge again"
+set sv_dodging_maxspeed 350 "maximum speed a player can be moving at to use the standard dodging from an (almost) standstill"
+set sv_dodging_air_maxspeed 450 "maximum speed a player can be moving at before they dodge again when air dodging is enabled"
 
 
 // ===========
@@ -36,7 +40,9 @@ set g_instagib_ammo_convert_bullets 0 "convert bullet ammo packs to insta cell a
 set g_instagib_ammo_convert_cells 0 "convert normal cell ammo packs to insta cell ammo packs"
 set g_instagib_ammo_convert_rockets 0 "convert rocket ammo packs to insta cell ammo packs"
 set g_instagib_ammo_convert_shells 0 "convert shell ammo packs to insta cell ammo packs"
+set g_instagib_invisibility_time 30 "Time of ivisibility powerup in seconds."
 set g_instagib_invis_alpha 0.15
+set g_instagib_speed_time 30 "Time of speed powerup in seconds."
 set g_instagib_speed_highspeed 1.5 "speed-multiplier that applies while you carry the invincibility powerup"
 set g_instagib_damagedbycontents 1 "allow damage from lava pits in instagib"
 set g_instagib_blaster_keepdamage 0 "allow secondary fire to hurt players"
@@ -473,3 +479,24 @@ set g_dynamic_handicap_scale 0.2 "The scale of the handicap. Larger values mean
 set g_dynamic_handicap_exponent 1 "The exponent used to calculate handicap. 1 means linear scale. Values more than 1 mean stronger non-linear handicap. Values less than 1 mean weaker non-linear handicap"
 set g_dynamic_handicap_min 0 "The minimum value of the handicap."
 set g_dynamic_handicap_max 0 "The maximum value of the handicap."
+
+// ===============
+// kick teamkiller
+// ===============
+set g_kick_teamkiller_rate 0 "Limit for teamkills per minute before the client gets dropped. 0 means that the teamkillers don't get kicked automatically"
+set g_kick_teamkiller_lower_limit 5 "Minimum number of teamkills before the teamkill rate is considered"
+
+// =====================
+//  stale-move negation
+// =====================
+set g_smneg 0 "Stale-move negation: penalize repeated use of the same weapon"
+set g_smneg_bonus 1 "Stale-move negation: allow weapons to become stronger than their baseline"
+set g_smneg_bonus_asymptote 4 "Stale-move negation: damage = infinity at this bonus level"
+set g_smneg_cooldown_factor 0.25 "Stale-move negation: penalty cooldown factor"
+
+// ==============
+//  random items
+// ==============
+set g_random_items 0 "Whether to enable random items."
+set g_random_loot 0 "Whether to enable random loot."
+exec randomitems-xonotic.cfg
index 88742ff0b89943675ba5347037376d6fe96325a6..b74f68b3db4d1c3c341c70216e80765f7c28f8cc 100644 (file)
@@ -18,6 +18,7 @@ set g_physics_xonotic_airstrafeaccel_qw -0.95
 set g_physics_xonotic_airspeedlimit_nonqw 900
 set g_physics_xonotic_maxspeed 360
 set g_physics_xonotic_jumpvelocity 260
+set g_physics_xonotic_jumpvelocity_crouch 0
 set g_physics_xonotic_maxairstrafespeed 100
 set g_physics_xonotic_maxairspeed 360
 set g_physics_xonotic_airstrafeaccelerate 18
@@ -48,6 +49,7 @@ set g_physics_nexuiz_airstrafeaccel_qw 0
 set g_physics_nexuiz_airspeedlimit_nonqw 0
 set g_physics_nexuiz_maxspeed 400
 set g_physics_nexuiz_jumpvelocity 300 "333 to match xonotic physics"
+set g_physics_nexuiz_jumpvelocity_crouch 0 "333 to match xonotic physics"
 set g_physics_nexuiz_maxairstrafespeed 0
 set g_physics_nexuiz_maxairspeed 220
 set g_physics_nexuiz_airstrafeaccelerate 0
@@ -78,6 +80,7 @@ set g_physics_quake_airstrafeaccel_qw 0
 set g_physics_quake_airspeedlimit_nonqw 0
 set g_physics_quake_maxspeed 320
 set g_physics_quake_jumpvelocity 270
+set g_physics_quake_jumpvelocity_crouch 0
 set g_physics_quake_maxairstrafespeed 0
 set g_physics_quake_maxairspeed 30
 set g_physics_quake_airstrafeaccelerate 0
@@ -108,6 +111,7 @@ set g_physics_warsow_airstrafeaccel_qw 0
 set g_physics_warsow_airspeedlimit_nonqw 0
 set g_physics_warsow_maxspeed 320
 set g_physics_warsow_jumpvelocity 280
+set g_physics_warsow_jumpvelocity_crouch 0
 set g_physics_warsow_maxairstrafespeed 30
 set g_physics_warsow_maxairspeed 320
 set g_physics_warsow_airstrafeaccelerate 70
@@ -138,6 +142,7 @@ set g_physics_defrag_airstrafeaccel_qw 1
 set g_physics_defrag_airspeedlimit_nonqw 0
 set g_physics_defrag_maxspeed 320
 set g_physics_defrag_jumpvelocity 270
+set g_physics_defrag_jumpvelocity_crouch 0
 set g_physics_defrag_maxairstrafespeed 30
 set g_physics_defrag_maxairspeed 320
 set g_physics_defrag_airstrafeaccelerate 70
@@ -168,6 +173,7 @@ set g_physics_quake3_airstrafeaccel_qw 0
 set g_physics_quake3_airspeedlimit_nonqw 0
 set g_physics_quake3_maxspeed 320
 set g_physics_quake3_jumpvelocity 270
+set g_physics_quake3_jumpvelocity_crouch 0
 set g_physics_quake3_maxairstrafespeed 0
 set g_physics_quake3_maxairspeed 320
 set g_physics_quake3_airstrafeaccelerate 0
@@ -198,6 +204,7 @@ set g_physics_vecxis_airstrafeaccel_qw 0
 set g_physics_vecxis_airspeedlimit_nonqw 0
 set g_physics_vecxis_maxspeed 400
 set g_physics_vecxis_jumpvelocity 300 "333 to match xonotic physics"
+set g_physics_vecxis_jumpvelocity_crouch 0 "333 to match xonotic physics"
 set g_physics_vecxis_maxairstrafespeed 0
 set g_physics_vecxis_maxairspeed 220
 set g_physics_vecxis_airstrafeaccelerate 0
@@ -228,6 +235,7 @@ set g_physics_quake2_airstrafeaccel_qw 0
 set g_physics_quake2_airspeedlimit_nonqw 0
 set g_physics_quake2_maxspeed 300
 set g_physics_quake2_jumpvelocity 270
+set g_physics_quake2_jumpvelocity_crouch 0
 set g_physics_quake2_maxairstrafespeed 0
 set g_physics_quake2_maxairspeed 300
 set g_physics_quake2_airstrafeaccelerate 0
@@ -258,6 +266,7 @@ set g_physics_bones_airstrafeaccel_qw 1
 set g_physics_bones_airspeedlimit_nonqw 0
 set g_physics_bones_maxspeed 320
 set g_physics_bones_jumpvelocity 270
+set g_physics_bones_jumpvelocity_crouch 0
 set g_physics_bones_maxairstrafespeed 30
 set g_physics_bones_maxairspeed 320
 set g_physics_bones_airstrafeaccelerate 70
@@ -288,6 +297,7 @@ set g_physics_overkill_airstrafeaccel_qw -0.95
 set g_physics_overkill_airspeedlimit_nonqw 900
 set g_physics_overkill_maxspeed 400
 set g_physics_overkill_jumpvelocity 260
+set g_physics_overkill_jumpvelocity_crouch 0
 set g_physics_overkill_maxairstrafespeed 100
 set g_physics_overkill_maxairspeed 360
 set g_physics_overkill_airstrafeaccelerate 24
index 612a779dac5fb69ad978febcc0a3cbdc9b7279aa..3c1614dcd3b1943061a3c35ca3489127fdb7f2e1 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 8
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 4
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index 8e0a7f0fe73f8084a776eac1274dd255a71a5f9c..8c034b8a9010faf99eab9bab51d2f1cd7a662bb5 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 8
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0 // breaks strafing?
index e5d431b3e7b6c7d3f5babb72291b6cd144a30361..f45a73f14ca2eb09447feb72868eb1abd3861b91 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 7
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.65
index 81d54d2eede716dc852bde66054b6063b5130bf3..b2c35d086b6cd2241877cf61c879659f35e0a098 100644 (file)
@@ -18,6 +18,7 @@ sv_stepheight 26
 // actually, what we want is 266.6666 for 180bpm
 // but 260 takes same amount of frames and is nicer to mappers
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0 // breaks strafing?
index 741224b1195736d1f0b1bcb35499eb8cdadbda0f..f529ed66dcc993101481bcca323b95948dc40d12 100644 (file)
@@ -18,6 +18,7 @@ sv_stepheight 26
 // actually, what we want is 266.6666 for 180bpm
 // but 260 takes same amount of frames and is nicer to mappers
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 83689d1614e26474644270f2ecfd3ebbc5d28c55..522c30197186f99c6fa06c6d3164f93e87ec3382 100644 (file)
@@ -13,6 +13,7 @@ sv_friction 9.6 // higher values make you slide less
 edgefriction 1 // div0 says no! lol
 sv_stepheight 26
 sv_jumpvelocity 304
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0 // pain in the ass to tweak without screwing up the strafing
index de271c2987b5e75cce1b3b3293c1d8fdc8fef502..0c32adb2ef49436e331559d07bfb793b5a747c43 100644 (file)
@@ -12,6 +12,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 310
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.35
index 28d7c7f2cebce23ea74fe1a0e9b4d5d9ba875008..1f0b8bde150e79bab98a249c31669c6689a57af5 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 7633e5a28878384f09a1418796937a91d0cdb979..51ef497e7fcdc61d47f6041898805bca772c743f 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index aaec2756f6bece0a7a24572766dd275b299cbddd..0dd5b0da2948dcbc8ebb9b32d97c6d80c2b21818 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 3cd9df61ee398d349f77e55b87b43553f76c6b78..5b9b21bf6717c88494704ddc4000065130da157d 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 17b8de8e4c0a88138d661683a27e63e6f685f74b..4bad86850de9b29cab187f76d94296855f8995e5 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 168ec2d349a65ebdd3256e7fca7992a4255244b7..531952a36533c992e390871454a511b7cafb0a96 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.3
index 6038e7b6af70ea7a654e9c2b5de3105db5a7bbb5..4de91d19a812122e2659726b1214597770626b38 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 7
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.35
index cb088f9df067d3f0f8360c70941f58787057c707..151d360cb6179aac640729e144bfd9e1d9337406 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 7
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.35
index 2b054788a1cf314fc17a257c3543c2acb15b500d..7acc35544a1c4211a6859383d45f37debfaa05a5 100644 (file)
@@ -19,6 +19,7 @@ sv_stepheight 26
 // actually, what we want is 266.6666 for 180bpm
 // but 260 takes same amount of frames and is nicer to mappers
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 526f7e22226d4442519db20892e50c6e6ff5fb9c..a6f36a77e8320a7f8d4719a4f57987b88553cc91 100644 (file)
@@ -24,6 +24,7 @@ sv_stepheight 31
 // this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in
 // the 16 grid, and the 8th unit used for wall/floor) just lets a player jump!
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index c880f511e4d678f7c958d047ef46062ed283da8e..4f880c90fc128acd897c7007fb9e60769c81ee31 100644 (file)
@@ -12,6 +12,7 @@ sv_friction 4
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 992cb3e62b7740b9015fe88233b9953be0212d47..f45a81b7739fb4f55ed8dd63d5504b01c2a6a236 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 6
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index ce323269699ac3a5189d4ec5b399b4ffcb617d22..585262a2ed0080811a1be8d47ac7fa926de4c1b9 100644 (file)
@@ -12,6 +12,7 @@ sv_friction 6
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index a6d46ca820ba0022beda3220eaa9e860bac5244e..3ce18b52a98bef30d6c6f32da3ba0fa0a2a22f5d 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 6
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 4
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index 39b705637a312df7c3cbe1bf69da98c26a76aed0..6b210f55b92e61f893298fc23c2dffb35acd79b1 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.8
index 2e092b1230cb00afc2feb3607d529091385dcdf6..e8772b3f3f48037726fb5cfe2397f8a4d8561cff 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 5
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.5
index 8506e2255c5e54150ed95f66de075d9178402e85..4f22085f9a11d8c1829e2024b4f880f11ceb0347 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 4
 edgefriction 1
 sv_stepheight 34
 sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0.3
index 1bd23b647f636fca8aa9d759a133c69bffff0e08..de7352aab54e56de9803d38cdec59a0298038583 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 8
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 280
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 10
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index 985be7e95cf7a1fa82eec39bb8b67c0b6ea172e1..7705a4d512d70856f008aa0d2c1148d282581525 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 8
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 280
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 10
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index dff451087b10aacd24bb57504002da5d61a47717..9009beab33a14e949a7f0d575d67dc7673617387 100644 (file)
@@ -11,6 +11,7 @@ sv_friction 8
 edgefriction 1
 sv_stepheight 18
 sv_jumpvelocity 280
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 10
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index 6b154508ec63d798e0f2ba18eae78287e7f8f971..6076dd73d19702226a4fe2efe3a842c4582a843f 100644 (file)
@@ -25,6 +25,7 @@ sv_stepheight 31
 // this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in
 // the 16 grid, and the 8th unit used for wall/floor) just lets a player jump!
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 9c5a7d334a214804d0a84a0daf2a18e768e70be8..a5349c98fdfbd4212256335b808f8df22325e2c1 100644 (file)
@@ -18,6 +18,7 @@ sv_stepheight 26
 // actually, what we want is 266.6666 for 180bpm
 // but 260 takes same amount of frames and is nicer to mappers
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index 1fd86cd8000140b33d0af6001cd0b513ef26a794..8ae771f1cf468a9ac21ec05d4b94cdc4e2782708 100644 (file)
@@ -24,6 +24,7 @@ sv_stepheight 26
 // this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in
 // the 16 grid, and the 8th unit used for wall/floor) just lets a player jump!
 sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate -1
 sv_waterfriction -1
 sv_airaccel_sideways_friction 0
index e192ab47627b613e10f21974d0c1cdac813dc956..e0bea6a0ea560a17c0c442a280c7293417b84505 100644 (file)
@@ -16,6 +16,7 @@ edgefriction 1
 sv_stepheight 26
 // CPMA: 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 4
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index 24705deb89a93b23e972c173ec33c476577906c0..f9bed43f6e65096b7edcfa4309e7e969237b2168 100644 (file)
@@ -16,6 +16,7 @@ edgefriction 1
 sv_stepheight 26
 // CPMA: 18
 sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
 sv_wateraccelerate 4
 sv_waterfriction 1
 sv_airaccel_sideways_friction 0
index e1fffb59f34e52684ee1ac0ec2191322e61e8c59..a6c65183d4541cc0cb23c1cdf133e594fc3c1e0c 100644 (file)
@@ -88,10 +88,10 @@ void HUD_Physics()
        const int acc_decimals = 2;
        if(time > physics_update_time)
        {
+               discrete_acceleration = acceleration;
                // workaround for ftos_decimals returning a negative 0
                if(discrete_acceleration > -1 / (10 ** acc_decimals) && discrete_acceleration < 0)
                        discrete_acceleration = 0;
-               discrete_acceleration = acceleration;
                discrete_speed = speed;
                physics_update_time += autocvar_hud_panel_physics_update_interval;
                if(physics_update_time < time)
index 8e0d0f057695d8e237fbcbe2816b11298c6fce89..063cb887f92d51a734ff4d3d740ce09fca7bece6 100644 (file)
@@ -96,6 +96,7 @@ string TranslateScoresLabel(string l)
                case "kd": return CTX(_("SCO^k/d"));
                case "kdr": return CTX(_("SCO^kdr"));
                case "kills": return CTX(_("SCO^kills"));
+               case "teamkills": return CTX(_("SCO^teamkills"));
                case "laps": return CTX(_("SCO^laps"));
                case "lives": return CTX(_("SCO^lives"));
                case "losses": return CTX(_("SCO^losses"));
@@ -312,6 +313,7 @@ void Cmd_Scoreboard_Help()
        LOG_INFO(_("^3deaths^7                   Number of deaths"));
        LOG_INFO(_("^3suicides^7                 Number of suicides"));
        LOG_INFO(_("^3frags^7                    kills - suicides"));
+       LOG_INFO(_("^3teamkills^7                Number of teamkills"));
        LOG_INFO(_("^3kd^7                       The kill-death ratio"));
        LOG_INFO(_("^3dmg^7                      The total damage done"));
        LOG_INFO(_("^3dmgtaken^7                 The total damage taken"));
@@ -361,6 +363,7 @@ void Cmd_Scoreboard_Help()
 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
 " -teams,lms/deaths +ft,tdm/deaths" \
 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" +teams/teamkills"\
 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
index 7a054606313f1e64d90b61105c2ae98683a909b9..4f2b43e914fb602d83c6f653a50fb7e2463740d1 100644 (file)
@@ -12,6 +12,7 @@
 #include "mutators/events.qh"
 
 #include <common/animdecide.qh>
+#include <common/deathtypes/all.qh>
 #include <common/ent_cs.qh>
 #include <common/anim.qh>
 #include <common/constants.qh>
@@ -910,7 +911,8 @@ vector crosshair_getcolor(entity this, float health_stat)
 
                case 2: // crosshair_color_by_health
                {
-                       float hp = health_stat;
+                       vector v = healtharmor_maxdamage(health_stat, STAT(ARMOR), armorblockpercent, DEATH_WEAPON.m_id);
+                       float hp = floor(v.x + 1);
 
                        //x = red
                        //y = green
index 5a0ff2a2c22ade90150d22cad3fbcfdb8be07f3b..7eb8ecb4b0bd62a30004870d7c4a869ec87b85da 100644 (file)
@@ -909,7 +909,7 @@ MUTATOR_HOOKFUNCTION(nb, FilterItem)
 {
        entity item = M_ARGV(0, entity);
 
-       if(item.classname == "droppedweapon")
+       if(Item_IsLoot(item))
        if(item.weapon == WEP_NEXBALL.m_id)
                return true;
 
index a022979a7065f7ee07c8cdfb0fc94c3ab9a0038a..8520075019b97c616107cabf94420ef9a27d58d6 100644 (file)
@@ -1,7 +1,6 @@
 #pragma once
 
 #include "all.qh"
-#include "item/pickup.qh"
 
 CLASS(Inventory, Object)
     /** Stores counts of items, the id being the index */
@@ -81,7 +80,7 @@ void Inventory_Write(Inventory data)
             if (!(minorBits & BIT(j))) { \
                 continue; \
             } \
-            const GameItem it = Items_from(Inventory_groups_minor * maj + j); \
+            const entity it = Items_from(Inventory_groups_minor * maj + j); \
             WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); \
         } \
     } \
index c302ae40281d43699b335ab24a7d382b7d3d667e..b7fc933e8b5dbaf35dde8a37fca1762c51a4e08a 100644 (file)
@@ -1,9 +1,10 @@
 #pragma once
-#include <common/t_items.qh>
 
 #ifdef GAMEQC
+#include <common/models/all.qh>
 #include <common/sounds/all.qh>
 #include <common/sounds/all.inc>
+#include <common/stats.qh>
 #endif
 
 const int IT_UNLIMITED_WEAPON_AMMO             =  BIT(0); // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
@@ -45,11 +46,30 @@ const int IT_PICKUPMASK                     = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FU
 #ifdef SVQC
 .float  strength_finished = _STAT(STRENGTH_FINISHED);
 .float  invincible_finished = _STAT(INVINCIBLE_FINISHED);
+
+#define SPAWNFUNC_ITEM(name, item) \
+    spawnfunc(name) { StartItem(this, item); }
+
+#else
+
+#define SPAWNFUNC_ITEM(name, item)
+
 #endif
 
+enum
+{
+       ITEM_FLAG_NORMAL = BIT(0), ///< Item is usable during normal gameplay.
+       ITEM_FLAG_INSTAGIB = BIT(1), ///< Item is usable in instagib.
+       ITEM_FLAG_OVERKILL = BIT(2), ///< Item is usable in overkill.
+       ITEM_FLAG_MUTATORBLOCKED = BIT(3)
+};
+
 #define ITEM_HANDLE(signal, ...) __Item_Send_##signal(__VA_ARGS__)
 CLASS(GameItem, Object)
     ATTRIB(GameItem, m_id, int, 0);
+    /** the canonical spawnfunc name */
+    ATTRIB(GameItem, m_canonical_spawnfunc, string);
+    METHOD(GameItem, m_spawnfunc_hookreplace, GameItem(GameItem this, entity e)) { return this; }
     ATTRIB(GameItem, m_name, string);
     ATTRIB(GameItem, m_icon, string);
     ATTRIB(GameItem, m_color, vector, '1 1 1');
index d7e0dcc6872d0d55b9d7b00aad234920acf5cb45..3a13a1f81855c51c2eaeea4bea5adda6e10c07b3 100644 (file)
@@ -1 +1,23 @@
 #include "ammo.qh"
+
+#ifdef SVQC
+
+METHOD(Bullets, m_spawnfunc_hookreplace, GameItem(Bullets this, entity e))
+{
+       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
+       {
+               return ITEM_Shells;
+       }
+       return this;
+}
+
+METHOD(Shells, m_spawnfunc_hookreplace, GameItem(Shells this, entity e))
+{
+       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
+       {
+               return ITEM_Bullets;
+       }
+       return this;
+}
+
+#endif
index 20963928526a12ddfe94313004fffe946ca89b8c..1d5bd87baceb116b7e241f9955e4e12b8f6d3ee9 100644 (file)
@@ -1,6 +1,23 @@
 #pragma once
 
 #include "pickup.qh"
+#ifdef SVQC
+    #include <common/t_items.qh>
+#endif
+
+.int ammo_none;
+.int ammo_shells;
+.int ammo_nails;
+.int ammo_rockets;
+.int ammo_cells;
+#ifdef SVQC
+.int ammo_plasma = _STAT(PLASMA);
+.int ammo_fuel = _STAT(FUEL);
+#else
+.int ammo_plasma;
+.int ammo_fuel;
+#endif
+
 #ifdef SVQC
 PROPERTY(float, g_pickup_ammo_anyway);
 #endif
@@ -14,9 +31,6 @@ CLASS(Ammo, Pickup)
 #endif
 ENDCLASS(Ammo)
 
-#ifdef SVQC
-    #include <common/t_items.qh>
-#endif
 
 #ifdef GAMEQC
 MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl"));
@@ -30,8 +44,14 @@ void ammo_bullets_init(entity item)
         item.ammo_nails = g_pickup_nails;
 }
 #endif
-REGISTER_ITEM(Bullets, Ammo) {
+
+CLASS(Bullets, Ammo)
+ENDCLASS(Bullets)
+
+REGISTER_ITEM(Bullets, Bullets) {
+    this.m_canonical_spawnfunc = "item_bullets";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model    =   MDL_Bullets_ITEM;
 #endif
     this.netname    =   "bullets";
@@ -44,6 +64,8 @@ REGISTER_ITEM(Bullets, Ammo) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_bullets, ITEM_Bullets)
+
 #ifdef GAMEQC
 MODEL(Cells_ITEM, Item_Model("a_cells.md3"));
 #endif
@@ -57,7 +79,9 @@ void ammo_cells_init(entity item)
 }
 #endif
 REGISTER_ITEM(Cells, Ammo) {
+    this.m_canonical_spawnfunc = "item_cells";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model    =   MDL_Cells_ITEM;
 #endif
     this.netname    =   "cells";
@@ -70,6 +94,8 @@ REGISTER_ITEM(Cells, Ammo) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_cells, ITEM_Cells)
+
 #ifdef GAMEQC
 MODEL(Plasma_ITEM, Item_Model("a_cells.md3"));
 #endif
@@ -83,7 +109,9 @@ void ammo_plasma_init(entity item)
 }
 #endif
 REGISTER_ITEM(Plasma, Ammo) {
+    this.m_canonical_spawnfunc = "item_plasma";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model    =   MDL_Plasma_ITEM;
 #endif
     this.netname    =   "plasma";
@@ -96,6 +124,8 @@ REGISTER_ITEM(Plasma, Ammo) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_plasma, ITEM_Plasma)
+
 #ifdef GAMEQC
 MODEL(Rockets_ITEM, Item_Model("a_rockets.md3"));
 #endif
@@ -109,7 +139,9 @@ void ammo_rockets_init(entity item)
 }
 #endif
 REGISTER_ITEM(Rockets, Ammo) {
+    this.m_canonical_spawnfunc = "item_rockets";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model    =   MDL_Rockets_ITEM;
 #endif
     this.netname    =   "rockets";
@@ -122,6 +154,8 @@ REGISTER_ITEM(Rockets, Ammo) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_rockets, ITEM_Rockets)
+
 #ifdef GAMEQC
 MODEL(Shells_ITEM, Item_Model("a_shells.md3"));
 #endif
@@ -134,8 +168,14 @@ void ammo_shells_init(entity item)
         item.ammo_shells = g_pickup_shells;
 }
 #endif
-REGISTER_ITEM(Shells, Ammo) {
+
+CLASS(Shells, Ammo)
+ENDCLASS(Shells)
+
+REGISTER_ITEM(Shells, Shells) {
+    this.m_canonical_spawnfunc = "item_shells";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model    =   MDL_Shells_ITEM;
 #endif
     this.netname    =   "shells";
@@ -147,3 +187,5 @@ REGISTER_ITEM(Shells, Ammo) {
     this.m_iteminit =   ammo_shells_init;
 #endif
 }
+
+SPAWNFUNC_ITEM(item_shells, ITEM_Shells)
index 0258cf8811c7682e0922f3f6960ed6af2f9c7065..7f37c75aec002465260b1852810253e05c6a4b11 100644 (file)
@@ -32,7 +32,9 @@ void item_armorsmall_init(entity item)
 #endif
 
 REGISTER_ITEM(ArmorSmall, Armor) {
+    this.m_canonical_spawnfunc = "item_armor_small";
 #ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
     this.m_model                =   MDL_ArmorSmall_ITEM;
     this.m_sound                =   SND_ArmorSmall;
 #endif
@@ -48,6 +50,8 @@ REGISTER_ITEM(ArmorSmall, Armor) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_armor_small, ITEM_ArmorSmall)
+
 #ifdef GAMEQC
 MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3"));
 SOUND(ArmorMedium, Item_Sound("armor10"));
@@ -66,7 +70,9 @@ void item_armormedium_init(entity item)
 #endif
 
 REGISTER_ITEM(ArmorMedium, Armor) {
+    this.m_canonical_spawnfunc = "item_armor_medium";
 #ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
     this.m_model                =   MDL_ArmorMedium_ITEM;
     this.m_sound                =   SND_ArmorMedium;
 #endif
@@ -82,6 +88,8 @@ REGISTER_ITEM(ArmorMedium, Armor) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_armor_medium, ITEM_ArmorMedium)
+
 #ifdef GAMEQC
 MODEL(ArmorBig_ITEM, Item_Model("item_armor_big.md3"));
 SOUND(ArmorBig, Item_Sound("armor17_5"));
@@ -100,7 +108,9 @@ void item_armorbig_init(entity item)
 #endif
 
 REGISTER_ITEM(ArmorBig, Armor) {
+    this.m_canonical_spawnfunc = "item_armor_big";
 #ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
     this.m_model                =   MDL_ArmorBig_ITEM;
     this.m_sound                =   SND_ArmorBig;
 #endif
@@ -118,6 +128,8 @@ REGISTER_ITEM(ArmorBig, Armor) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_armor_big, ITEM_ArmorBig)
+
 #ifdef GAMEQC
 MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3"));
 SOUND(ArmorMega, Item_Sound("armor25"));
@@ -136,7 +148,9 @@ void item_armormega_init(entity item)
 #endif
 
 REGISTER_ITEM(ArmorMega, Armor) {
+    this.m_canonical_spawnfunc = "item_armor_mega";
 #ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
     this.m_model                =   MDL_ArmorMega_ITEM;
     this.m_sound                =   SND_ArmorMega;
 #endif
@@ -155,3 +169,5 @@ REGISTER_ITEM(ArmorMega, Armor) {
     this.m_iteminit             =   item_armormega_init;
 #endif
 }
+
+SPAWNFUNC_ITEM(item_armor_mega, ITEM_ArmorMega)
index cad5a376e753d3cd18cfb997261ed37141072881..da431086e18587448cf697c8127390fd166134f0 100644 (file)
@@ -32,7 +32,9 @@ void item_healthsmall_init(entity item)
 #endif
 
 REGISTER_ITEM(HealthSmall, Health) {
+    this.m_canonical_spawnfunc = "item_health_small";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model                =   MDL_HealthSmall_ITEM;
     this.m_sound                =   SND_HealthSmall;
 #endif
@@ -48,6 +50,8 @@ REGISTER_ITEM(HealthSmall, Health) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_health_small, ITEM_HealthSmall)
+
 #ifdef GAMEQC
 MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3"));
 SOUND(HealthMedium, Item_Sound("mediumhealth"));
@@ -66,7 +70,9 @@ void item_healthmedium_init(entity item)
 #endif
 
 REGISTER_ITEM(HealthMedium, Health) {
+    this.m_canonical_spawnfunc = "item_health_medium";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model                =   MDL_HealthMedium_ITEM;
     this.m_sound                =   SND_HealthMedium;
 #endif
@@ -82,6 +88,8 @@ REGISTER_ITEM(HealthMedium, Health) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_health_medium, ITEM_HealthMedium)
+
 #ifdef GAMEQC
 MODEL(HealthBig_ITEM, Item_Model("g_h50.md3"));
 SOUND(HealthBig, Item_Sound("mediumhealth"));
@@ -100,7 +108,9 @@ void item_healthbig_init(entity item)
 #endif
 
 REGISTER_ITEM(HealthBig, Health) {
+    this.m_canonical_spawnfunc = "item_health_big";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model                =   MDL_HealthBig_ITEM;
     this.m_sound                =   SND_HealthBig;
 #endif
@@ -118,6 +128,8 @@ REGISTER_ITEM(HealthBig, Health) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_health_big, ITEM_HealthBig)
+
 #ifdef GAMEQC
 MODEL(HealthMega_ITEM, Item_Model("g_h100.md3"));
 SOUND(HealthMega, Item_Sound("megahealth"));
@@ -136,7 +148,9 @@ void item_healthmega_init(entity item)
 #endif
 
 REGISTER_ITEM(HealthMega, Health) {
+    this.m_canonical_spawnfunc = "item_health_mega";
 #ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
     this.m_model                =   MDL_HealthMega_ITEM;
     this.m_sound                =   SND_HealthMega;
 #endif
@@ -155,3 +169,5 @@ REGISTER_ITEM(HealthMega, Health) {
     this.m_iteminit             =   item_healthmega_init;
 #endif
 }
+
+SPAWNFUNC_ITEM(item_health_mega, ITEM_HealthMega)
index ec09d5c45d62d904c6e4a2ada5ec570ae3b5c4bc..11b9d924309d32e5e666c38827ae841dce043ae3 100644 (file)
@@ -1 +1,23 @@
 #include "jetpack.qh"
+
+#ifdef SVQC
+
+METHOD(Jetpack, m_spawnfunc_hookreplace, GameItem(Jetpack this, entity e))
+{
+       if(start_items & ITEM_Jetpack.m_itemid)
+       {
+               return ITEM_JetpackFuel;
+       }
+       return this;
+}
+
+METHOD(JetpackRegen, m_spawnfunc_hookreplace, GameItem(JetpackRegen this, entity e))
+{
+       if (start_items & ITEM_JetpackRegen.m_itemid)
+       {
+               return ITEM_JetpackFuel;
+       }
+       return this;
+}
+
+#endif
index a6d1c8dae89751694d03d16bc6f35238eceec0c5..284bf3d390fce1c7b62653a9570b9ef20a7dffe5 100644 (file)
@@ -23,8 +23,14 @@ void powerup_jetpack_init(entity item)
         item.ammo_fuel = g_pickup_fuel_jetpack;
 }
 #endif
+
+CLASS(Jetpack, Powerup)
+ENDCLASS(Jetpack)
+
 REGISTER_ITEM(Jetpack, Powerup) {
+    this.m_canonical_spawnfunc = "item_jetpack";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model                =   MDL_Jetpack_ITEM;
     this.m_itemid               =   IT_JETPACK;
 #endif
@@ -41,6 +47,8 @@ REGISTER_ITEM(Jetpack, Powerup) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_jetpack, ITEM_Jetpack)
+
 #ifdef GAMEQC
 MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3"));
 #endif
@@ -54,7 +62,9 @@ void ammo_fuel_init(entity item)
 }
 #endif
 REGISTER_ITEM(JetpackFuel, Ammo) {
+    this.m_canonical_spawnfunc = "item_fuel";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model    =   MDL_JetpackFuel_ITEM;
 #endif
     this.netname    =   "fuel";
@@ -67,12 +77,19 @@ REGISTER_ITEM(JetpackFuel, Ammo) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_fuel, ITEM_JetpackFuel)
+
 #ifdef GAMEQC
 MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3"));
 #endif
 
-REGISTER_ITEM(JetpackRegen, Powerup) {
+CLASS(JetpackRegen, Powerup)
+ENDCLASS(JetpackRegen)
+
+REGISTER_ITEM(JetpackRegen, JetpackRegen) {
+    this.m_canonical_spawnfunc = "item_fuel_regen";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model                =   MDL_JetpackRegen_ITEM;
 #endif
     this.netname                =   "fuel_regen";
@@ -87,3 +104,5 @@ REGISTER_ITEM(JetpackRegen, Powerup) {
     this.m_pickupevalfunc       =   ammo_pickupevalfunc;
 #endif
 }
+
+SPAWNFUNC_ITEM(item_fuel_regen, ITEM_JetpackRegen)
index fc958709e87d66af6eb50b958ce71ab8ef8f11b2..b5944fc0a3def7d7235f09bfc66e6ad33f2fb11e 100644 (file)
@@ -1,7 +1,21 @@
 #include "pickup.qh"
+#include <common/items/inventory.qh>
 
 #ifdef SVQC
 bool ITEM_HANDLE(Pickup, entity this, entity item, entity player) {
     return this.giveTo(this, item, player);
 }
+
+METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player))
+{
+    TC(Pickup, this);
+    bool b = Item_GiveTo(item, player);
+    if (b) {
+        LOG_DEBUGF("entity %i picked up %s", player, this.m_name);
+        player.inventory.inv_items[this.m_id]++;
+        Inventory_update(player);
+    }
+    return b;
+}
+
 #endif
index 39cf78cc3016e3d3b4714bcf169c843892342f59..fb4bc28cd8ede336f7d6d656ca25edbdd4972e42 100644 (file)
@@ -17,15 +17,7 @@ PROPERTY(float, g_pickup_respawntimejitter_long)
 PROPERTY(float, g_pickup_respawntimejitter_powerup)
 #endif
 
-#include <common/items/inventory.qh>
 #include <common/items/item.qh>
-#include <common/t_items.qh>
-
-#ifdef GAMEQC
-#include <common/models/all.qh>
-#include <common/sounds/all.qh>
-#include <common/sounds/all.inc>
-#endif
 
 CLASS(Pickup, GameItem)
 #ifdef GAMEQC
@@ -52,17 +44,7 @@ CLASS(Pickup, GameItem)
     ATTRIB(Pickup, m_pickupanyway, float());
     ATTRIB(Pickup, m_iteminit, void(entity item));
     float Item_GiveTo(entity item, entity player);
-    METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player))
-    {
-        TC(Pickup, this);
-        bool b = Item_GiveTo(item, player);
-        if (b) {
-            LOG_DEBUGF("entity %i picked up %s", player, this.m_name);
-            player.inventory.inv_items[this.m_id]++;
-            Inventory_update(player);
-        }
-        return b;
-    }
+    METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player));
     bool ITEM_HANDLE(Pickup, Pickup this, entity item, entity player);
 #endif
 ENDCLASS(Pickup)
index 41b658c2fc5a25afe03eefcaeea7867e07f1a6de..fe47b63430ddd1726b774f080f56905a4dc7568c 100644 (file)
@@ -31,7 +31,9 @@ void powerup_strength_init(entity item)
 }
 #endif
 REGISTER_ITEM(Strength, Powerup) {
+    this.m_canonical_spawnfunc = "item_strength";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model            =   MDL_Strength_ITEM;
     this.m_sound            =   SND_Strength;
     this.m_glow             =   true;
@@ -49,6 +51,8 @@ REGISTER_ITEM(Strength, Powerup) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_strength, ITEM_Strength)
+
 #ifdef GAMEQC
 MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
 SOUND(Shield, Item_Sound("powerup_shield"));
@@ -63,7 +67,9 @@ void powerup_shield_init(entity item)
 }
 #endif
 REGISTER_ITEM(Shield, Powerup) {
+    this.m_canonical_spawnfunc = "item_shield";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
     this.m_model            =   MDL_Shield_ITEM;
     this.m_sound            =   SND_Shield;
     this.m_glow             =   true;
@@ -80,3 +86,6 @@ REGISTER_ITEM(Shield, Powerup) {
     this.m_iteminit         =   powerup_shield_init;
 #endif
 }
+
+SPAWNFUNC_ITEM(item_shield, ITEM_Shield)
+SPAWNFUNC_ITEM(item_invincible, ITEM_Shield)
index 80cd21b481b41474dcf860d2d42178c95428f228..e6eb9b03defa392e3d5fb87b03118b4eb0e76d0b 100644 (file)
@@ -35,7 +35,8 @@ void monster_dropitem(entity this, entity attacker)
                return;
 
        vector org = CENTER_OR_VIEWOFS(this);
-       entity e = new(droppedweapon); // use weapon handling to remove it on touch
+       entity e = spawn();
+       Item_SetLoot(e, true);
        e.spawnfunc_checked = true;
 
        e.monster_loot = this.monster_loot;
@@ -48,8 +49,6 @@ void monster_dropitem(entity this, entity attacker)
                e.noalign = true;
                StartItem(e, e.monster_loot);
                e.gravity = 1;
-               set_movetype(e, MOVETYPE_TOSS);
-               e.reset = SUB_Remove;
                setorigin(e, org);
                e.velocity = randomvec() * 175 + '0 0 325';
                e.item_spawnshieldtime = time + 0.7;
index eeb93ba5e83f7cb9252274e397c994bf9e93a73e..9d52fa20c44af25537d2b11bf68b04b2020cac0c 100644 (file)
@@ -15,6 +15,7 @@
 #include <common/mutators/mutator/instagib/_mod.inc>
 #include <common/mutators/mutator/invincibleproj/_mod.inc>
 #include <common/mutators/mutator/itemstime/_mod.inc>
+#include <common/mutators/mutator/kick_teamkiller/_mod.inc>
 #include <common/mutators/mutator/melee_only/_mod.inc>
 #include <common/mutators/mutator/midair/_mod.inc>
 #include <common/mutators/mutator/multijump/_mod.inc>
 #include <common/mutators/mutator/physical_items/_mod.inc>
 #include <common/mutators/mutator/pinata/_mod.inc>
 #include <common/mutators/mutator/random_gravity/_mod.inc>
+#include <common/mutators/mutator/random_items/_mod.inc>
 #include <common/mutators/mutator/rocketflying/_mod.inc>
 #include <common/mutators/mutator/rocketminsta/_mod.inc>
 #include <common/mutators/mutator/running_guns/_mod.inc>
 #include <common/mutators/mutator/sandbox/_mod.inc>
 #include <common/mutators/mutator/spawn_near_teammate/_mod.inc>
+#include <common/mutators/mutator/stale_move_negation/_mod.inc>
 #include <common/mutators/mutator/superspec/_mod.inc>
 #include <common/mutators/mutator/touchexplode/_mod.inc>
 #include <common/mutators/mutator/vampire/_mod.inc>
index 956c0d97536fbfeaddc4d4b8b1728ce147d09bc5..f9edf4c56c47868e01161b34793a5c05e33f64d9 100644 (file)
@@ -15,6 +15,7 @@
 #include <common/mutators/mutator/instagib/_mod.qh>
 #include <common/mutators/mutator/invincibleproj/_mod.qh>
 #include <common/mutators/mutator/itemstime/_mod.qh>
+#include <common/mutators/mutator/kick_teamkiller/_mod.qh>
 #include <common/mutators/mutator/melee_only/_mod.qh>
 #include <common/mutators/mutator/midair/_mod.qh>
 #include <common/mutators/mutator/multijump/_mod.qh>
 #include <common/mutators/mutator/physical_items/_mod.qh>
 #include <common/mutators/mutator/pinata/_mod.qh>
 #include <common/mutators/mutator/random_gravity/_mod.qh>
+#include <common/mutators/mutator/random_items/_mod.qh>
 #include <common/mutators/mutator/rocketflying/_mod.qh>
 #include <common/mutators/mutator/rocketminsta/_mod.qh>
 #include <common/mutators/mutator/running_guns/_mod.qh>
 #include <common/mutators/mutator/sandbox/_mod.qh>
 #include <common/mutators/mutator/spawn_near_teammate/_mod.qh>
+#include <common/mutators/mutator/stale_move_negation/_mod.qh>
 #include <common/mutators/mutator/superspec/_mod.qh>
 #include <common/mutators/mutator/touchexplode/_mod.qh>
 #include <common/mutators/mutator/vampire/_mod.qh>
index e039a96b9501a9e99900884377b625fcab16861c..6994c81761ad8ac68f289516ea405eda1a75f7c4 100644 (file)
@@ -750,7 +750,7 @@ MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
        switch(ent.classname)
        {
                case "item_strength":
-               case "item_invincible":
+               case "item_shield":
                {
                        entity e = spawn();
                        buff_SpawnReplacement(e, ent);
index 70aac4d9a3fcda663448d035199f3d46b93dc1a0..6640cb8bf23a51a48c08cdd4662fbd89a4fcefcf 100644 (file)
@@ -1,32 +1,42 @@
 #include "sv_dodging.qh"
 
+// TODO the CSQC blocks in this sv_ file are currently not compiled but will be when dodging prediction gets enabled
+
 #define PHYS_DODGING                                           g_dodging
 #define PHYS_DODGING_DELAY                                     autocvar_sv_dodging_delay
 #define PHYS_DODGING_DISTANCE_THRESHOLD        autocvar_sv_dodging_wall_distance_threshold
-#define PHYS_DODGING_FROZEN_NODOUBLETAP                autocvar_sv_dodging_frozen_doubletap
+#define PHYS_DODGING_FROZEN_DOUBLETAP          autocvar_sv_dodging_frozen_doubletap
 #define PHYS_DODGING_HEIGHT_THRESHOLD          autocvar_sv_dodging_height_threshold
-#define PHYS_DODGING_HORIZ_SPEED                       autocvar_sv_dodging_horiz_speed
-#define PHYS_DODGING_HORIZ_SPEED_FROZEN        autocvar_sv_dodging_horiz_speed_frozen
+#define PHYS_DODGING_HORIZ_SPEED_MIN           autocvar_sv_dodging_horiz_speed_min
+#define PHYS_DODGING_HORIZ_SPEED_MAX           autocvar_sv_dodging_horiz_speed_max
+#define PHYS_DODGING_HORIZ_FORCE_SLOWEST       autocvar_sv_dodging_horiz_force_slowest
+#define PHYS_DODGING_HORIZ_FORCE_FASTEST       autocvar_sv_dodging_horiz_force_fastest
+#define PHYS_DODGING_HORIZ_FORCE_FROZEN        autocvar_sv_dodging_horiz_force_frozen
 #define PHYS_DODGING_RAMP_TIME                                 autocvar_sv_dodging_ramp_time
 #define PHYS_DODGING_UP_SPEED                          autocvar_sv_dodging_up_speed
 #define PHYS_DODGING_WALL                                      autocvar_sv_dodging_wall_dodging
 #define PHYS_DODGING_AIR                                       autocvar_sv_dodging_air_dodging
 #define PHYS_DODGING_MAXSPEED                          autocvar_sv_dodging_maxspeed
+#define PHYS_DODGING_AIR_MAXSPEED                      autocvar_sv_dodging_air_maxspeed
 
 // we ran out of stats slots! TODO: re-enable this when prediction is available for dodging
 #if 0
 #define PHYS_DODGING                                           STAT(DODGING, this)
 #define PHYS_DODGING_DELAY                                     STAT(DODGING_DELAY, this)
 #define PHYS_DODGING_DISTANCE_THRESHOLD        STAT(DODGING_DISTANCE_THRESHOLD, this)
-#define PHYS_DODGING_FROZEN_NODOUBLETAP                STAT(DODGING_FROZEN_NO_DOUBLETAP, this)
+#define PHYS_DODGING_FROZEN_DOUBLETAP          STAT(DODGING_FROZEN_DOUBLETAP, this)
 #define PHYS_DODGING_HEIGHT_THRESHOLD          STAT(DODGING_HEIGHT_THRESHOLD, this)
-#define PHYS_DODGING_HORIZ_SPEED                       STAT(DODGING_HORIZ_SPEED, this)
-#define PHYS_DODGING_HORIZ_SPEED_FROZEN        STAT(DODGING_HORIZ_SPEED_FROZEN, this)
+#define PHYS_DODGING_HORIZ_SPEED_MIN           STAT(DODGING_HORIZ_SPEED_MIN, this)
+#define PHYS_DODGING_HORIZ_SPEED_MAX           STAT(DODGING_HORIZ_SPEED_MAX, this)
+#define PHYS_DODGING_HORIZ_FORCE_SLOWEST       STAT(DODGING_HORIZ_FORCE_SLOWEST, this)
+#define PHYS_DODGING_HORIZ_FORCE_FASTEST       STAT(DODGING_HORIZ_FORCE_FASTEST, this)
+#define PHYS_DODGING_HORIZ_FORCE_FROZEN        STAT(DODGING_HORIZ_FORCE_FROZEN, this)
 #define PHYS_DODGING_RAMP_TIME                                 STAT(DODGING_RAMP_TIME, this)
 #define PHYS_DODGING_UP_SPEED                          STAT(DODGING_UP_SPEED, this)
 #define PHYS_DODGING_WALL                                      STAT(DODGING_WALL, this)
 #define PHYS_DODGING_AIR                                       STAT(DODGING_AIR, this)
 #define PHYS_DODGING_MAXSPEED                          STAT(DODGING_MAXSPEED, this)
+#define PHYS_DODGING_AIR_MAXSPEED                      STAT(DODGING_AIR_MAXSPEED, this)
 #endif
 
 #ifdef CSQC
 
 bool autocvar_sv_dodging_sound;
 
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
 #include <common/animdecide.qh>
 #include <common/physics/player.qh>
 
@@ -81,7 +85,6 @@ REGISTER_MUTATOR(dodging, true);
 // the jump part of the dodge cannot be ramped
 .float dodging_single_action;
 
-
 // these are used to store the last key press time for each of the keys..
 .float last_FORWARD_KEY_time;
 .float last_BACKWARD_KEY_time;
@@ -95,123 +98,126 @@ REGISTER_MUTATOR(dodging, true);
 // and to ramp up the dodge acceleration in the physics hook.
 .float last_dodging_time;
 
-// This is the velocity gain to be added over the ramp time.
-// It will decrease from frame to frame during dodging_action = 1
-// until it's 0.
-.float dodging_velocity_gain;
+// the total speed that will be added over the ramp time
+.float dodging_force_total;
+// the part of total yet to be added
+.float dodging_force_remaining;
 
 #ifdef CSQC
 .int pressedkeys;
 #endif
 
-// returns true if the player is close to a wall
-bool check_close_to_wall(entity this, float threshold)
-{
-       if (PHYS_DODGING_WALL == 0) { return false; }
-
 #define X(dir) \
        tracebox(this.origin, this.mins, this.maxs, this.origin + threshold * dir, true, this); \
        if (trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) \
                return true;
 
+// returns true if the player is close to a wall
+bool is_close_to_wall(entity this, float threshold)
+{
        X(v_right);
        X(-v_right);
        X(v_forward);
        X(-v_forward);
-#undef X
 
        return false;
 }
 
-bool check_close_to_ground(entity this, float threshold)
+bool is_close_to_ground(entity this, float threshold)
 {
-       return IS_ONGROUND(this) ? true : false;
+       if (IS_ONGROUND(this)) return true;
+       X(-v_up); // necessary for dodging down a slope using doubletap (using `+dodge` works anyway)
+
+       return false;
+}
+
+#undef X
+
+float determine_force(entity player) {
+       if (PHYS_FROZEN(player)) return PHYS_DODGING_HORIZ_FORCE_FROZEN;
+
+       float horiz_vel = vlen(vec2(player.velocity));
+       return map_bound_ranges(horiz_vel,
+                               PHYS_DODGING_HORIZ_SPEED_MIN, PHYS_DODGING_HORIZ_SPEED_MAX,
+                               PHYS_DODGING_HORIZ_FORCE_SLOWEST, PHYS_DODGING_HORIZ_FORCE_FASTEST);
 }
 
 bool PM_dodging_checkpressedkeys(entity this)
 {
-       if(!PHYS_DODGING)
-               return false;
-
        bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
-       bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+       bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_DOUBLETAP);
+
+       float tap_direction_x = 0;
+       float tap_direction_y = 0;
+       bool dodge_detected = false;
+       vector mymovement = PHYS_CS(this).movement;
 
-       // first check if the last dodge is far enough back in time so we can dodge again
+       #define X(COND,BTN,RESULT)                                                                                                                                                              \
+       if (mymovement_##COND) {                                                                                                                                                                \
+               /* is this a state change? */                                                                                                                                           \
+               if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) {                                                     \
+                       tap_direction_##RESULT;                                                                                                                                                 \
+                       if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) {  \
+                               dodge_detected = true;                                                                                                                                          \
+                       } else if(PHYS_INPUT_BUTTON_DODGE(this)) {                                                                                                              \
+                               dodge_detected = true;                                                                                                                                          \
+                       }                                                                                                                                                                                               \
+                       this.last_##BTN##_KEY_time = time;                                                                                                                              \
+               }                                                                                                                                                                                                       \
+       }
+       X(x < 0, BACKWARD,      x--);
+       X(x > 0, FORWARD,       x++);
+       X(y < 0, LEFT,          y--);
+       X(y > 0, RIGHT,         y++);
+       #undef X
+
+       if (!dodge_detected) return false;
+
+       // this check has to be after checking keys:
+       // the first key press of the double tap is allowed to be before dodging delay,
+       // only the second has to be after, otherwise +dodge gives an advantage because typical repress time is 0.1 s
+       // or higher which means players using +dodge would be able to do it more often
        if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY)
                return false;
 
        makevectors(this.angles);
 
-       bool wall_dodge = false;
+       bool can_dodge = (is_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) && (PHYS_DODGING_MAXSPEED == 0 || vdist(this.velocity, <, PHYS_DODGING_MAXSPEED)));
+       bool can_wall_dodge = (PHYS_DODGING_WALL && is_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD));
+       bool can_air_dodge = (PHYS_DODGING_AIR && (PHYS_DODGING_AIR_MAXSPEED == 0 || vdist(this.velocity, <, PHYS_DODGING_AIR_MAXSPEED)));
+       if (!can_dodge && !can_wall_dodge && !can_air_dodge) return false;
 
-       if(!PHYS_DODGING_AIR)
-       if(!check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD))
-       {
-               wall_dodge = check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD);
-               if(!wall_dodge) // we're not on the ground, and wall dodging isn't allowed, end it!
-                       return true;
-       }
+       this.last_dodging_time = time;
 
-       if(!wall_dodge && PHYS_DODGING_MAXSPEED && vdist(this.velocity, >, PHYS_DODGING_MAXSPEED))
-               return false;
+       this.dodging_action = 1;
+       this.dodging_single_action = 1;
 
-       float tap_direction_x = 0;
-       float tap_direction_y = 0;
-       bool dodge_detected = false;
-       vector mymovement = PHYS_CS(this).movement;
+       this.dodging_force_total = determine_force(this);
+       this.dodging_force_remaining = this.dodging_force_total;
 
-       #define X(COND,BTN,RESULT)                                                                                                                      \
-       if (mymovement_##COND)                                                                                          \
-               /* is this a state change? */                                                                                                   \
-               if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) {             \
-                               tap_direction_##RESULT;                                                                                                 \
-                               if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap)    \
-                                       dodge_detected = true;                                                                                          \
-                               if(PHYS_INPUT_BUTTON_DODGE(this))                                                                               \
-                                       dodge_detected = true;                                                                                          \
-                               this.last_##BTN##_KEY_time = time;                                                                              \
-               }
-       X(x < 0, BACKWARD,      x--);
-       X(x > 0, FORWARD,       x++);
-       X(y < 0, LEFT,          y--);
-       X(y > 0, RIGHT,         y++);
-       #undef X
-
-       if (dodge_detected)
-       {
-               this.last_dodging_time = time;
-
-               this.dodging_action = 1;
-               this.dodging_single_action = 1;
+       this.dodging_direction.x = tap_direction_x;
+       this.dodging_direction.y = tap_direction_y;
 
-               this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+       // normalize the dodging_direction vector.. (unlike UT99) XD
+       float length = sqrt(this.dodging_direction.x ** 2 + this.dodging_direction.y ** 2);
 
-               this.dodging_direction_x = tap_direction_x;
-               this.dodging_direction_y = tap_direction_y;
+       this.dodging_direction.x = this.dodging_direction.x / length;
+       this.dodging_direction.y = this.dodging_direction.y / length;
 
-               // normalize the dodging_direction vector.. (unlike UT99) XD
-               float length = this.dodging_direction_x * this.dodging_direction_x
-                                       + this.dodging_direction_y * this.dodging_direction_y;
-               length = sqrt(length);
-
-               this.dodging_direction_x = this.dodging_direction_x * 1.0 / length;
-               this.dodging_direction_y = this.dodging_direction_y * 1.0 / length;
-               return true;
-       }
-       return false;
+       return true;
 }
 
 void PM_dodging(entity this)
 {
-       if (!PHYS_DODGING)
-               return;
+       // can't use return value from PM_dodging_checkpressedkeys because they're called from different hooks
+       if (!this.dodging_action) return;
 
        // when swimming or dead, no dodging allowed..
        if (this.waterlevel >= WATERLEVEL_SWIMMING || IS_DEAD(this))
        {
                this.dodging_action = 0;
-               this.dodging_direction_x = 0;
-               this.dodging_direction_y = 0;
+               this.dodging_direction.x = 0;
+               this.dodging_direction.y = 0;
                return;
        }
 
@@ -221,31 +227,18 @@ void PM_dodging(entity this)
        else
                makevectors(this.angles);
 
+       // fraction of the force to apply each frame
        // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
        // will be called ramp_time/frametime times = 2 times. so, we need to
        // add 0.5 * the total speed each frame until the dodge action is done..
        float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
+       // NOTE: depending on cl_netfps the client may (and probably will) send more input frames during each server frame
+       // but common_factor uses server frame rate so players with higher cl_netfps will ramp slightly faster
 
-       // if ramp time is smaller than frametime we get problems ;D
-       common_factor = min(common_factor, 1);
-
-       float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
-       float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed);
-       new_velocity_gain = max(0, new_velocity_gain);
-
-       float velocity_difference = this.dodging_velocity_gain - new_velocity_gain;
-
-       // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
-       if (this.dodging_action == 1)
-       {
-               //disable jump key during dodge accel phase
-               if(PHYS_CS(this).movement.z > 0) { PHYS_CS(this).movement_z = 0; }
-
-               this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right)
-                                       + ((this.dodging_direction_x * velocity_difference) * v_forward);
-
-               this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference;
-       }
+       float velocity_increase = min(common_factor * this.dodging_force_total, this.dodging_force_remaining);
+       this.dodging_force_remaining -= velocity_increase;
+       this.velocity += this.dodging_direction.x * velocity_increase * v_forward
+                      + this.dodging_direction.y * velocity_increase * v_right;
 
        // the up part of the dodge is a single shot action
        if (this.dodging_single_action == 1)
@@ -264,21 +257,18 @@ void PM_dodging(entity this)
                this.dodging_single_action = 0;
        }
 
-       // are we done with the dodging ramp yet?
-       if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
+       if(this.dodging_force_remaining <= 0)
        {
                // reset state so next dodge can be done correctly
                this.dodging_action = 0;
-               this.dodging_direction_x = 0;
-               this.dodging_direction_y = 0;
+               this.dodging_direction.x = 0;
+               this.dodging_direction.y = 0;
        }
 }
 
+#ifdef CSQC
 void PM_dodging_GetPressedKeys(entity this)
 {
-#ifdef CSQC
-       if(!PHYS_DODGING) { return; }
-
        PM_dodging_checkpressedkeys(this);
 
        int keys = this.pressedkeys;
@@ -292,15 +282,16 @@ void PM_dodging_GetPressedKeys(entity this)
        keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
        keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
        this.pressedkeys = keys;
-#endif
 }
+#endif
 
 MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
 {
-    entity player = M_ARGV(0, entity);
+       entity player = M_ARGV(0, entity);
 
-       // print("dodging_PlayerPhysics\n");
+#ifdef CSQC
        PM_dodging_GetPressedKeys(player);
+#endif
        PM_dodging(player);
 }
 
@@ -308,13 +299,6 @@ MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
 
 REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
 
-MUTATOR_HOOKFUNCTION(dodging, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       STAT(DODGING_TIMEOUT, player) = CS(player).cvar_cl_dodging_timeout;
-}
-
 MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
 {
        entity player = M_ARGV(0, entity);
index 2195111f0f269e2903f767e3badf17644e0aa27d..55a67b1e38a95abcd62c87af38cd29425a308510 100644 (file)
@@ -1,5 +1,8 @@
 // generated file; do not modify
 #include <common/mutators/mutator/instagib/items.qc>
+#ifdef SVQC
+    #include <common/mutators/mutator/instagib/sv_items.qc>
+#endif
 #ifdef SVQC
     #include <common/mutators/mutator/instagib/sv_instagib.qc>
 #endif
index e80f36e1a087542eb7d3ee0c06f5c493c7902d0c..ab6843ed53ea7e358ceed756771c0515d3f07e65 100644 (file)
@@ -23,11 +23,13 @@ void ammo_vaporizercells_init(entity item)
 }
 #endif
 REGISTER_ITEM(VaporizerCells, Ammo) {
+    this.m_canonical_spawnfunc = "item_vaporizer_cells";
 #ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_INSTAGIB | ITEM_FLAG_MUTATORBLOCKED;
     this.m_model                =   MDL_VaporizerCells_ITEM;
     this.m_sound                =   SND_VaporizerCells;
 #endif
-    this.netname                =   "minst_cells";
+    this.netname                =   "vaporizer_cells";
     this.m_name                 =   "Vaporizer Ammo";
     this.m_icon                 =   "ammo_supercells";
 #ifdef SVQC
@@ -39,17 +41,22 @@ REGISTER_ITEM(VaporizerCells, Ammo) {
 #endif
 }
 
+SPAWNFUNC_ITEM(item_vaporizer_cells, ITEM_VaporizerCells)
+SPAWNFUNC_ITEM(item_minst_cells, ITEM_VaporizerCells)
+
 #ifdef GAMEQC
 MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3"));
 SOUND(ExtraLife, Item_Sound("megahealth"));
 #endif
 
 REGISTER_ITEM(ExtraLife, Powerup) {
+    this.m_canonical_spawnfunc = "item_extralife";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_INSTAGIB;
     this.m_model                =   MDL_ExtraLife_ITEM;
     this.m_sound                =   SND_ExtraLife;
 #endif
-    this.netname                =   "health_mega";
+    this.netname                =   "extralife";
     this.m_name                 =   "Extra life";
     this.m_icon                 =   "item_mega_health";
     this.m_color                =   '1 0 0';
@@ -58,40 +65,74 @@ REGISTER_ITEM(ExtraLife, Powerup) {
     this.m_itemid               =   IT_NAILS;
 }
 
+SPAWNFUNC_ITEM(item_extralife, ITEM_ExtraLife)
+
 #ifdef GAMEQC
 MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
 SOUND(Invisibility, Item_Sound("powerup"));
 #endif
 
+#ifdef SVQC
+/// \brief Initializes the invisibility powerup.
+/// \param[in,out] item Item to initialize.
+/// \return No return.
+void powerup_invisibility_init(entity item);
+#endif
+
 REGISTER_ITEM(Invisibility, Powerup) {
+    this.m_canonical_spawnfunc = "item_invisibility";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_INSTAGIB;
     this.m_model            =   MDL_Invisibility_ITEM;
     this.m_sound            =   SND_Invisibility;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
 #endif
-    this.netname            =   "strength";
+    this.netname            =   "invisibility";
     this.m_name             =   "Invisibility";
     this.m_icon             =   "strength";
     this.m_color            =   '0 0 1';
     this.m_waypoint         =   _("Invisibility");
     this.m_waypointblink    =   2;
     this.m_itemid           =   IT_STRENGTH;
+#ifdef SVQC
+    this.m_iteminit         =   powerup_invisibility_init;
+#endif
 }
 
+SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
+
 #ifdef GAMEQC
 MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
 SOUND(Speed, Item_Sound("powerup_shield"));
 #endif
 
+#ifdef SVQC
+/// \brief Initializes the speed powerup.
+/// \param[in,out] item Item to initialize.
+/// \return No return.
+void powerup_speed_init(entity item);
+#endif
+
 REGISTER_ITEM(Speed, Powerup) {
+    this.m_canonical_spawnfunc = "item_speed";
 #ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_INSTAGIB;
     this.m_model            =   MDL_Speed_ITEM;
     this.m_sound            =   SND_Speed;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_SHIELD_RESPAWN;
 #endif
-    this.netname            =   "invincible";
+    this.netname            =   "speed";
     this.m_name             =   "Speed";
     this.m_icon             =   "shield";
     this.m_color            =   '1 0 1';
     this.m_waypoint         =   _("Speed");
     this.m_waypointblink    =   2;
     this.m_itemid           =   IT_INVINCIBLE;
+#ifdef SVQC
+    this.m_iteminit         =   powerup_speed_init;
+#endif
 }
+
+SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
index 1561dc10db9a326acf932de70df86365e179bd4f..c21623f0e5e12d0bb63d96644e4d4e6a22441f1a 100644 (file)
@@ -17,12 +17,16 @@ float autocvar_g_instagib_speed_highspeed;
 
 #include <common/items/_mod.qh>
 
-REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball);
-
-spawnfunc(item_minst_cells)
+REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball)
 {
-       if (!g_instagib) { delete(this); return; }
-       StartItem(this, ITEM_VaporizerCells);
+    MUTATOR_ONADD
+    {
+        ITEM_VaporizerCells.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+    }
+    MUTATOR_ONROLLBACK_OR_REMOVE
+    {
+        ITEM_VaporizerCells.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+    }
 }
 
 void instagib_invisibility(entity this)
@@ -377,13 +381,13 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena)
 
 void replace_with_insta_cells(entity item)
 {
-       entity e = spawn();
+       entity e = new(item_vaporizer_cells);
        setorigin(e, item.origin);
        e.noalign = item.noalign;
        e.cnt = item.cnt;
        e.team = item.team;
        e.spawnfunc_checked = true;
-       spawnfunc_item_minst_cells(e);
+       spawnfunc_item_vaporizer_cells(e);
 }
 
 MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
@@ -423,7 +427,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
                return true;
        }
 
-       if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon")
+       if(item.weapon == WEP_VAPORIZER.m_id && Item_IsLoot(item))
        {
                SetResourceAmount(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop);
                return false;
@@ -439,7 +443,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
                return false;
 
        float cells = GetResourceAmount(item, RESOURCE_CELLS);
-       if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
+       if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_vaporizer_cells")
                SetResourceAmount(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop);
 
        if(cells && !item.weapon)
@@ -508,18 +512,27 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn)
        if (!autocvar_g_powerups) { return; }
        entity ent = M_ARGV(0, entity);
        // Can't use .itemdef here
-       if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega"))
+       if (!(ent.classname == "item_strength" || ent.classname == "item_shield" || ent.classname == "item_health_mega"))
                return;
 
        entity e = spawn();
 
        float r = random();
        if (r < 0.3)
+       {
+               e.classname = "item_invisibility";
                setthink(e, instagib_invisibility);
+       }
        else if (r < 0.6)
+       {
+               e.classname = "item_extralife";
                setthink(e, instagib_extralife);
+       }
        else
+       {
+               e.classname = "item_speed";
                setthink(e, instagib_speed);
+       }
 
        e.nextthink = time + 0.1;
        e.spawnflags = ent.spawnflags;
index 4c6d20b1293c7f317ab89d8c5f61ac89a2088ea1..9020b93124777851f80a80eb1e2dc4b0edab75fa 100644 (file)
@@ -3,3 +3,7 @@
 #include "items.qh"
 
 float autocvar_g_instagib_invis_alpha;
+
+void instagib_invisibility(entity this);
+void instagib_extralife(entity this);
+void instagib_speed(entity this);
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_items.qc b/qcsrc/common/mutators/mutator/instagib/sv_items.qc
new file mode 100644 (file)
index 0000000..ffd9bfb
--- /dev/null
@@ -0,0 +1,23 @@
+#include "items.qh"
+
+/// \brief Time of ivisibility powerup in seconds.
+float autocvar_g_instagib_invisibility_time;
+/// \brief Time of speed powerup in seconds.
+float autocvar_g_instagib_speed_time;
+
+void powerup_invisibility_init(entity item)
+{
+       if(!item.strength_finished)
+       {
+               item.strength_finished = autocvar_g_instagib_invisibility_time;
+       }
+}
+
+
+void powerup_speed_init(entity item)
+{
+       if(!item.invincible_finished)
+       {
+               item.invincible_finished = autocvar_g_instagib_speed_time;
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc
new file mode 100644 (file)
index 0000000..a374a0e
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh
new file mode 100644 (file)
index 0000000..98fb481
--- /dev/null
@@ -0,0 +1 @@
+// generated file; do not modify
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc
new file mode 100644 (file)
index 0000000..a3b028f
--- /dev/null
@@ -0,0 +1,33 @@
+
+float autocvar_g_kick_teamkiller_rate;
+float autocvar_g_kick_teamkiller_lower_limit;
+
+REGISTER_MUTATOR(kick_teamkiller, (autocvar_g_kick_teamkiller_rate > 0));
+
+MUTATOR_HOOKFUNCTION(kick_teamkiller, PlayerDies)
+{
+       if (!teamplay)
+       {
+               return;
+       }
+       if (warmup_stage)
+       {
+               return;
+       }
+       entity attacker = M_ARGV(1, entity);
+       if (!IS_REAL_CLIENT(attacker))
+       {
+               return;
+       }
+
+       int teamkills = PlayerScore_Get(attacker, SP_TEAMKILLS);
+       // use the players actual playtime
+       float playtime = time - CS(attacker).startplaytime;
+       // rate is in teamkills/minutes, playtime in seconds
+       if (teamkills >= autocvar_g_kick_teamkiller_lower_limit && 
+           teamkills >= autocvar_g_kick_teamkiller_rate*playtime/60.0)
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname);
+               dropclient(attacker);
+       }
+}
index af364995a1e92bc13c42b287babb5e3471938441..ec2593215a09b5187abd522608e07dc6f978e141 100644 (file)
@@ -1,5 +1,7 @@
 #include "sv_new_toys.qh"
 
+#include "../random_items/sv_random_items.qh"
+
 /*
 
 CORE    laser   vortex     lg      rl      cry     gl      elec    hagar   fireb   hook
@@ -193,6 +195,11 @@ MUTATOR_HOOKFUNCTION(nt, SetStartItems)
 
 MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace)
 {
+       if (autocvar_g_random_items)
+       {
+               // Do not replace weapons when random items are enabled.
+               return;
+       }
        entity wep = M_ARGV(0, entity);
        entity wepinfo = M_ARGV(1, entity);
        string ret_string = M_ARGV(2, string);
index 41327956f383b062deb21356e896170cb57c82f9..f88e52a9dfea6ab2d4efd9f8d61581ac28683b44 100644 (file)
@@ -10,8 +10,6 @@ MUTATOR_HOOKFUNCTION(hmg_nadesupport, Nade_Damage)
        M_ARGV(3, float) /* damage */ = (M_ARGV(0, entity)).max_health * 0.1;
 }
 
-spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(this, WEP_HMG); }
-
 void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
        if (!PHYS_INPUT_BUTTON_ATCK(actor))
index 07b31ea3cd107c6e9af918b749b17d2ac4c0673f..99c8093970e4bc6ef7acd2c69bbc516b8093a244 100644 (file)
@@ -1,6 +1,9 @@
 #pragma once
 
+#include <common/weapons/all.qh>
+
 CLASS(HeavyMachineGun, Weapon)
+/* spawnfunc */ ATTRIB(HeavyMachineGun, m_canonical_spawnfunc, string, "weapon_hmg");
 /* ammotype  */ ATTRIB(HeavyMachineGun, ammo_type, int, RESOURCE_BULLETS);
 /* impulse   */ ATTRIB(HeavyMachineGun, impulse, int, 3);
 /* flags     */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
@@ -40,3 +43,5 @@ CLASS(HeavyMachineGun, Weapon)
 
 ENDCLASS(HeavyMachineGun)
 REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun));
+
+SPAWNFUNC_WEAPON(weapon_hmg, WEP_HMG)
index f901d11593d2951ccf0e542002be2d4230886f85..b38e0ee52ce78ab021f179fabe8e42ca64d04a2e 100644 (file)
@@ -1,7 +1,6 @@
 #include "rpc.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); }
 
 void W_RocketPropelledChainsaw_Explode(entity this, entity directhitentity)
 {
index 560f7f4fa4313cdb68a2b69f1dc0306a696b96e0..78d5de51ae4621249a2c23f55fbefdd5a5df468b 100644 (file)
@@ -1,6 +1,9 @@
 #pragma once
 
+#include <common/weapons/all.qh>
+
 CLASS(RocketPropelledChainsaw, Weapon)
+/* spawnfunc */ ATTRIB(RocketPropelledChainsaw, m_canonical_spawnfunc, string, "weapon_rpc");
 /* ammotype  */ ATTRIB(RocketPropelledChainsaw, ammo_type, int, RESOURCE_ROCKETS);
 /* impulse   */ ATTRIB(RocketPropelledChainsaw, impulse, int, 9);
 /* flags     */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
@@ -45,3 +48,5 @@ CLASS(RocketPropelledChainsaw, Weapon)
 
 ENDCLASS(RocketPropelledChainsaw)
 REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw));
+
+SPAWNFUNC_WEAPON(weapon_rpc, WEP_RPC)
index b47e587511643e3076e172835052c16787d3079d..7462de81f2a03a37cccab8794fee8e884f6fec79 100644 (file)
@@ -9,34 +9,52 @@ bool autocvar_g_overkill_powerups_replace;
 
 bool autocvar_g_overkill_itemwaypoints = true;
 
-bool autocvar_g_overkill_filter_healthmega;
-bool autocvar_g_overkill_filter_armormedium;
-bool autocvar_g_overkill_filter_armorbig;
-bool autocvar_g_overkill_filter_armormega;
-
-.float ok_item;
-
 .Weapon ok_lastwep[MAX_WEAPONSLOTS];
 
-void ok_Initialize();
-
 REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
 {
        MUTATOR_ONADD
        {
-               ok_Initialize();
+               precache_all_playermodels("models/ok_player/*.dpm");
+
+               if (autocvar_g_overkill_filter_healthmega)
+               {
+                       ITEM_HealthMega.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+               }
+               if (autocvar_g_overkill_filter_armormedium)
+               {
+                       ITEM_ArmorMedium.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+               }
+               if (autocvar_g_overkill_filter_armorbig)
+               {
+                       ITEM_ArmorBig.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+               }
+               if (autocvar_g_overkill_filter_armormega)
+               {
+                       ITEM_ArmorMega.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+               }
+
+               WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+               WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+
+               WEP_SHOTGUN.mdl = "ok_shotgun";
+               WEP_MACHINEGUN.mdl = "ok_mg";
+               WEP_VORTEX.mdl = "ok_sniper";
        }
 
        MUTATOR_ONREMOVE
        {
+               ITEM_HealthMega.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+               ITEM_ArmorMedium.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+               ITEM_ArmorBig.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+               ITEM_ArmorMega.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+
                WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
                WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
        }
 }
 
 void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float);
-spawnfunc(weapon_hmg);
-spawnfunc(weapon_rpc);
 
 MUTATOR_HOOKFUNCTION(ok, Damage_Calculate, CBC_ORDER_LAST)
 {
@@ -61,20 +79,10 @@ MUTATOR_HOOKFUNCTION(ok, Damage_Calculate, CBC_ORDER_LAST)
 
 void ok_DropItem(entity this, entity targ)
 {
-       entity e = new(droppedweapon); // hax
+       entity e = spawn();
        e.ok_item = true;
-       e.noalign = true;
-       e.pickup_anyway = true;
-       e.spawnfunc_checked = true;
-       spawnfunc_item_armor_small(e);
-       if (!wasfreed(e)) { // might have been blocked by a mutator
-        set_movetype(e, MOVETYPE_TOSS);
-        e.gravity = 1;
-        e.reset = SUB_Remove;
-        setorigin(e, this.origin + '0 0 32');
-        e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500;
-        SUB_SetFade(e, time + 5, 1);
-       }
+       Item_InitializeLoot(e, "item_armor_small", this.origin + '0 0 32',
+               '0 0 200' + normalize(targ.origin - this.origin) * 500, 5);
 }
 
 MUTATOR_HOOKFUNCTION(ok, PlayerDies)
@@ -207,7 +215,7 @@ MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
                        wep.nextthink = time + 0.1;
                        return true;
                }
-               else if(ent.classname == "item_invincible")
+               else if(ent.classname == "item_shield")
                {
                        entity wep = new(weapon_rpc);
                        setorigin(wep, ent.origin);
@@ -261,14 +269,6 @@ MUTATOR_HOOKFUNCTION(ok, FilterItem)
        if(item.ok_item)
                return false;
 
-       switch(item.itemdef)
-       {
-               case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega;
-               case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium;
-               case ITEM_ArmorBig: return autocvar_g_overkill_filter_armorbig;
-               case ITEM_ArmorMega: return autocvar_g_overkill_filter_armormega;
-       }
-
        return true;
 }
 
@@ -304,15 +304,3 @@ MUTATOR_HOOKFUNCTION(ok, SetModname)
        M_ARGV(0, string) = "Overkill";
        return true;
 }
-
-void ok_Initialize()
-{
-       precache_all_playermodels("models/ok_player/*.dpm");
-
-       WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-       WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-
-       WEP_SHOTGUN.mdl = "ok_shotgun";
-       WEP_MACHINEGUN.mdl = "ok_mg";
-       WEP_VORTEX.mdl = "ok_sniper";
-}
index 6f70f09beec2219624baeca92e2cd7deaa104fb4..72324e6dbb01b375b00010231235a03d7a35e649 100644 (file)
@@ -1 +1,8 @@
 #pragma once
+
+bool autocvar_g_overkill_filter_healthmega;
+bool autocvar_g_overkill_filter_armormedium;
+bool autocvar_g_overkill_filter_armorbig;
+bool autocvar_g_overkill_filter_armormega;
+
+.float ok_item;
diff --git a/qcsrc/common/mutators/mutator/random_items/_mod.inc b/qcsrc/common/mutators/mutator/random_items/_mod.inc
new file mode 100644 (file)
index 0000000..191ea09
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/random_items/sv_random_items.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/random_items/_mod.qh b/qcsrc/common/mutators/mutator/random_items/_mod.qh
new file mode 100644 (file)
index 0000000..928d174
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/random_items/sv_random_items.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc
new file mode 100644 (file)
index 0000000..47234be
--- /dev/null
@@ -0,0 +1,438 @@
+#include "sv_random_items.qh"
+
+/// \file
+/// \brief Source file that contains implementation of the random items mutator.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+//============================ Constants ======================================
+
+enum
+{
+       RANDOM_ITEM_TYPE_HEALTH = 1,
+       RANDOM_ITEM_TYPE_ARMOR,
+       RANDOM_ITEM_TYPE_RESOURCE,
+       RANDOM_ITEM_TYPE_WEAPON,
+       RANDOM_ITEM_TYPE_POWERUP
+};
+
+//======================= Global variables ====================================
+
+// Replace cvars
+
+/// \brief Classnames to replace %s with.
+/// string autocvar_g_random_items_replace_%s;
+
+// Map probability cvars
+
+/// \brief Probability of random %s spawning in the map.
+/// float autocvar_g_random_items_%s_probability;
+
+/// \brief Probability of random %s spawning in the map during overkill.
+/// float autocvar_g_random_items_overkill_%s_probability;
+
+// Loot
+
+bool autocvar_g_random_loot; ///< Whether to enable random loot.
+
+float autocvar_g_random_loot_min; ///< Minimum amount of loot items.
+float autocvar_g_random_loot_max; ///< Maximum amount of loot items.
+float autocvar_g_random_loot_time; ///< Amount of time the loot will stay.
+float autocvar_g_random_loot_spread; ///< How far can loot be thrown.
+
+// Loot probability cvars
+
+/// \brief Probability of random %s spawning as loot.
+/// float autocvar_g_random_loot_%s_probability;
+
+/// \brief Probability of random %s spawning as loot during overkill.
+/// float autocvar_g_random_loot_overkill_%s_probability;
+
+/// \brief Holds whether random item is spawning. Used to prevent infinite
+/// recursion.
+bool random_items_is_spawning = false;
+
+//====================== Forward declarations =================================
+
+/// \brief Returns a random classname of the item with specific property.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the item.
+string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
+       .bool item_property);
+
+//=========================== Public API ======================================
+
+string RandomItems_GetRandomItemClassName(string prefix)
+{
+       if (autocvar_g_instagib)
+       {
+               return RandomItems_GetRandomInstagibItemClassName(prefix);
+       }
+       if (expr_evaluate(autocvar_g_overkill))
+       {
+               return RandomItems_GetRandomOverkillItemClassName(prefix);
+       }
+       return RandomItems_GetRandomVanillaItemClassName(prefix);
+}
+
+string RandomItems_GetRandomVanillaItemClassName(string prefix)
+{
+       RandomSelection_Init();
+       string cvar_name = sprintf("g_%s_health_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH, cvar(cvar_name), 1);
+       }
+       cvar_name = sprintf("g_%s_armor_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR, cvar(cvar_name), 1);
+       }
+       cvar_name = sprintf("g_%s_resource_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE, cvar(cvar_name), 1);
+       }
+       cvar_name = sprintf("g_%s_weapon_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1);
+       }
+       cvar_name = sprintf("g_%s_powerup_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
+       }
+       int item_type = RandomSelection_chosen_float;
+       switch (item_type)
+       {
+               case RANDOM_ITEM_TYPE_HEALTH:
+               {
+                       return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+                               instanceOfHealth);
+               }
+               case RANDOM_ITEM_TYPE_ARMOR:
+               {
+                       return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+                               instanceOfArmor);
+               }
+               case RANDOM_ITEM_TYPE_RESOURCE:
+               {
+                       return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+                               instanceOfAmmo);
+               }
+               case RANDOM_ITEM_TYPE_WEAPON:
+               {
+                       RandomSelection_Init();
+                       FOREACH(Weapons, it != WEP_Null &&
+                               !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
+                       {
+                               cvar_name = sprintf("g_%s_%s_probability", prefix,
+                                       it.m_canonical_spawnfunc);
+                               if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+                               {
+                                       LOG_WARNF("Random items: cvar %s doesn't exist.",
+                                               cvar_name);
+                                       continue;
+                               }
+                               RandomSelection_AddString(it.m_canonical_spawnfunc,
+                                       cvar(cvar_name), 1);
+                       });
+                       return RandomSelection_chosen_string;
+               }
+               case RANDOM_ITEM_TYPE_POWERUP:
+               {
+                       return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+                               instanceOfPowerup);
+               }
+       }
+       return "";
+}
+
+string RandomItems_GetRandomInstagibItemClassName(string prefix)
+{
+       RandomSelection_Init();
+       FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB,
+       {
+               string cvar_name = sprintf("g_%s_%s_probability", prefix,
+                       it.m_canonical_spawnfunc);
+               if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+               {
+                       LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+                       continue;
+               }
+               RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+       });
+       return RandomSelection_chosen_string;
+}
+
+string RandomItems_GetRandomOverkillItemClassName(string prefix)
+{
+       RandomSelection_Init();
+       FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
+               !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED),
+       {
+               string cvar_name = sprintf("g_%s_overkill_%s_probability", prefix,
+                       it.m_canonical_spawnfunc);
+               if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+               {
+                       LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+                       continue;
+               }
+               RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+       });
+       string cvar_name = sprintf("g_%s_overkill_weapon_hmg_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddString("weapon_hmg", cvar(cvar_name), 1);
+       }
+       cvar_name = sprintf("g_%s_overkill_weapon_rpc_probability", prefix);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+       }
+       else
+       {
+               RandomSelection_AddString("weapon_rpc", cvar(cvar_name), 1);
+       }
+       return RandomSelection_chosen_string;
+}
+
+//========================= Free functions ====================================
+
+/// \brief Returns list of classnames to replace a map item with.
+/// \param[in] item Item to inspect.
+/// \return List of classnames to replace a map item with.
+string RandomItems_GetItemReplacementClassNames(entity item)
+{
+       string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
+       if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+       {
+               LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+               return "";
+       }
+       return cvar_string(cvar_name);
+}
+
+string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
+       .bool item_property)
+{
+       RandomSelection_Init();
+       FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL),
+       {
+               string cvar_name = sprintf("g_%s_%s_probability", prefix,
+                       it.m_canonical_spawnfunc);
+               if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+               {
+                       LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+                       continue;
+               }
+               RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+       });
+       return RandomSelection_chosen_string;
+}
+
+/// \brief Replaces a map item.
+/// \param[in] item Item to replace.
+/// \return Spawned item on success, NULL otherwise.
+entity RandomItems_ReplaceMapItem(entity item)
+{
+       //PrintToChatAll(strcat("Replacing ", item.classname));
+       string new_classnames = RandomItems_GetItemReplacementClassNames(item);
+       if (new_classnames == "")
+       {
+               return NULL;
+       }
+       string new_classname;
+       if (new_classnames == "random")
+       {
+               new_classname = RandomItems_GetRandomItemClassName("random_items");
+               if (new_classname == "")
+               {
+                       return NULL;
+               }
+       }
+       else
+       {
+               int num_new_classnames = tokenize_console(new_classnames);
+               if (num_new_classnames == 1)
+               {
+                       new_classname = new_classnames;
+               }
+               else
+               {
+                       int classname_index = floor(random() * num_new_classnames);
+                       new_classname = argv(classname_index);
+               }
+       }
+       //PrintToChatAll(strcat("Replacing with ", new_classname));
+       if (new_classname == item.classname)
+       {
+               return NULL;
+       }
+       random_items_is_spawning = true;
+       entity new_item;
+       if (!expr_evaluate(autocvar_g_overkill))
+       {
+               new_item = Item_Create(strzone(new_classname), item.origin,
+                       item.noalign);
+               random_items_is_spawning = false;
+               if (new_item == NULL)
+               {
+                       return NULL;
+               }
+       }
+       else
+       {
+               new_item = spawn();
+               new_item.classname = strzone(new_classname);
+               new_item.spawnfunc_checked = true;
+               new_item.noalign = item.noalign;
+               new_item.ok_item = true;
+               Item_Initialize(new_item, new_classname);
+               random_items_is_spawning = false;
+               if (wasfreed(new_item))
+               {
+                       return NULL;
+               }
+               setorigin(new_item, item.origin);
+       }
+       if (item.team)
+       {
+               new_item.team = item.team;
+       }
+       return new_item;
+}
+
+/// \brief Spawns a random loot item.
+/// \param[in] position Position of the item.
+/// \return No return.
+void RandomItems_SpawnLootItem(vector position)
+{
+       string class_name = RandomItems_GetRandomItemClassName("random_loot");
+       if (class_name == "")
+       {
+               return;
+       }
+       vector spread = '0 0 0';
+       spread.z = autocvar_g_random_loot_spread / 2;
+       spread += randomvec() * autocvar_g_random_loot_spread;
+       random_items_is_spawning = true;
+       if (!expr_evaluate(autocvar_g_overkill))
+       {
+               Item_CreateLoot(class_name, position, spread,
+                       autocvar_g_random_loot_time);
+       }
+       else
+       {
+               entity item = spawn();
+               item.ok_item = true;
+               item.classname = class_name;
+               Item_InitializeLoot(item, class_name, position, spread,
+                       autocvar_g_random_loot_time);
+       }
+       random_items_is_spawning = false;
+}
+
+//============================= Hooks ========================================
+
+REGISTER_MUTATOR(random_items, (autocvar_g_random_items ||
+       autocvar_g_random_loot));
+
+MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
+}
+
+MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
+}
+
+/// \brief Hook that is called when an item is about to spawn.
+MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
+{
+       //PrintToChatAll("FilterItem");
+       if (!autocvar_g_random_items)
+       {
+               return false;
+       }
+       if (random_items_is_spawning == true)
+       {
+               return false;
+       }
+       entity item = M_ARGV(0, entity);
+       if (Item_IsLoot(item))
+       {
+               return false;
+       }
+       if (RandomItems_ReplaceMapItem(item) == NULL)
+       {
+               return false;
+       }
+       return true;
+}
+
+/// \brief Hook that is called after the player has touched an item.
+MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
+{
+       //PrintToChatAll("ItemTouched");
+       if (!autocvar_g_random_items)
+       {
+               return;
+       }
+       entity item = M_ARGV(0, entity);
+       if (Item_IsLoot(item))
+       {
+               return;
+       }
+       entity new_item = RandomItems_ReplaceMapItem(item);
+       if (new_item == NULL)
+       {
+               return;
+       }
+       Item_ScheduleRespawn(new_item);
+       delete(item);
+}
+
+/// \brief Hook which is called when the player dies.
+MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
+{
+       //PrintToChatAll("PlayerDies");
+       if (!autocvar_g_random_loot)
+       {
+               return;
+       }
+       entity victim = M_ARGV(2, entity);
+       vector loot_position = victim.origin + '0 0 32';
+       int num_loot_items = floor(autocvar_g_random_loot_min + random() *
+               autocvar_g_random_loot_max);
+       for (int item_index = 0; item_index < num_loot_items; ++item_index)
+       {
+               RandomItems_SpawnLootItem(loot_position);
+       }
+}
diff --git a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qh b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qh
new file mode 100644 (file)
index 0000000..c9b4dbb
--- /dev/null
@@ -0,0 +1,32 @@
+#pragma once
+
+/// \file
+/// \brief Header file that describes the random items mutator.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+bool autocvar_g_random_items; ///< Whether to enable random items.
+
+/// \brief Returns a random classname of the item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the item.
+/// \note This function will automatically detect gamemode and use cvars from
+/// that gamemode.
+string RandomItems_GetRandomItemClassName(string prefix);
+
+/// \brief Returns a random classname of the vanilla item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the vanilla item.
+/// \note This includes mutator items that don't change gameplay a lot such as
+/// jetpack and new toys.
+string RandomItems_GetRandomVanillaItemClassName(string prefix);
+
+/// \brief Returns a random classname of the instagib item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the instagib item.
+string RandomItems_GetRandomInstagibItemClassName(string prefix);
+
+/// \brief Returns a random classname of the overkill item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the overkill item.
+string RandomItems_GetRandomOverkillItemClassName(string prefix);
diff --git a/qcsrc/common/mutators/mutator/stale_move_negation/_mod.inc b/qcsrc/common/mutators/mutator/stale_move_negation/_mod.inc
new file mode 100644 (file)
index 0000000..0e94058
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/stale_move_negation/_mod.qh b/qcsrc/common/mutators/mutator/stale_move_negation/_mod.qh
new file mode 100644 (file)
index 0000000..a2c60b4
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qh>
+#endif
diff --git a/qcsrc/common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qc b/qcsrc/common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qc
new file mode 100644 (file)
index 0000000..c54a8a6
--- /dev/null
@@ -0,0 +1,49 @@
+#include "sv_stale_move_negation.qh"
+
+AUTOCVAR(g_smneg, bool, false, "Stale-move negation: penalize repeated use of the same weapon");
+AUTOCVAR(g_smneg_bonus, bool, true, "Stale-move negation: allow weapons to become stronger than their baseline");
+AUTOCVAR(g_smneg_bonus_asymptote, float, 4, "Stale-move negation: damage = infinity at this bonus level");
+AUTOCVAR(g_smneg_cooldown_factor, float, 1 / 4, "Stale-move negation: penalty cooldown factor");
+REGISTER_MUTATOR(mutator_smneg, autocvar_g_smneg);
+
+MUTATOR_HOOKFUNCTION(mutator_smneg, BuildMutatorsString) {
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), ":StaleMoveNegation");
+}
+
+MUTATOR_HOOKFUNCTION(mutator_smneg, BuildMutatorsPrettyString) {
+    M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Stale-move negation");
+}
+
+.float x_smneg_weight[Weapons_MAX];
+
+float smneg_multiplier(float weight) {
+    float a = autocvar_g_smneg_bonus_asymptote;
+    float x = max(
+        (!autocvar_g_smneg_bonus ? 0 : (-a + .1)),
+        weight / start_health
+    );
+    float z = (M_PI / 5) * a;
+    float f = (x > 0)
+        ? (atan(z / x) / (M_PI / 2))
+        : (tan(-(x / z)) + 1);
+    return f;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_smneg, Damage_Calculate) {
+    float deathtype = M_ARGV(3, float);
+    Weapon w = DEATH_WEAPONOF(deathtype);
+    if (w == WEP_Null) return;
+
+    entity frag_attacker = M_ARGV(1, entity);
+    entity c = CS(frag_attacker);
+    float weight = c.x_smneg_weight[w.m_id];
+    float f = smneg_multiplier(weight);
+    float frag_damage = M_ARGV(4, float) = f * M_ARGV(4, float);
+    M_ARGV(6, vector) = f * M_ARGV(6, vector); // force
+
+    c.x_smneg_weight[w.m_id] = weight + frag_damage;
+    float restore = frag_damage * autocvar_g_smneg_cooldown_factor;
+    FOREACH(Weapons, it != WEP_Null && it != w, {
+        c.x_smneg_weight[it.m_id] -= restore;
+    });
+}
diff --git a/qcsrc/common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qh b/qcsrc/common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qh
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 3b5492ac2d0adab12235d3512c13177e179b412b..b4c6146bb6662535e983df75a4e73962e6289636 100644 (file)
     MSG_INFO_NOTIF(QUIT_DISCONNECT,                         N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 disconnected"), "")
     MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for idling"), "")
     MSG_INFO_NOTIF(QUIT_KICK_SPECTATING,                    N_CONSOLE,  0, 0, "", "",           "",             _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "")
+    MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL,                      N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for excessive teamkilling"), "")
     MSG_INFO_NOTIF(QUIT_SPECTATE,                           N_CONSOLE,  1, 0, "s1", "",         "",             _("^BG%s^F3 is now spectating"), "")
 
     MSG_INFO_NOTIF(RACE_ABANDONED,                          N_CONSOLE,  1, 0, "s1", "",                                                                     "",                         _("^BG%s^BG has abandoned the race"), "")
index 995c65b4d322061a7f00d0b5f0899a4480af02a4..3fe2808583b94254d580b3c3960b2610c9192e6f 100644 (file)
@@ -544,7 +544,6 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
                        _Movetype_CheckWater(this);
                        this.origin = this.origin + movedt * this.velocity;
                        this.angles = this.angles + movedt * this.avelocity;
-                       _Movetype_LinkEdict(this, false);
                        break;
                case MOVETYPE_STEP:
                        _Movetype_Physics_Step(this, movedt);
@@ -563,6 +562,12 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
                case MOVETYPE_PHYSICS:
                        break;
        }
+
+       //_Movetype_CheckVelocity(this);
+
+       _Movetype_LinkEdict(this, true);
+
+       //_Movetype_CheckVelocity(this);
 }
 
 void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)  // to be run every move frame
index 20e2d4cc32f16d0b736e421d0a1172262492ff7c..20c580a8591b0e371981d671ba208a459229e2b2 100644 (file)
@@ -75,6 +75,7 @@ void Physics_UpdateStats(entity this)
        STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate", autocvar_sv_airaccelerate);
        STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate", autocvar_sv_airstopaccelerate);
        STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity);
+       STAT(MOVEVARS_JUMPVELOCITY_CROUCH, this) = Physics_ClientOption(this, "jumpvelocity_crouch", autocvar_sv_jumpvelocity_crouch);
        STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump);
 }
 #endif
@@ -303,7 +304,7 @@ bool PlayerJump(entity this)
 #endif
 
        bool doublejump = false;
-       float mjumpheight = PHYS_JUMPVELOCITY(this);
+       float mjumpheight = ((PHYS_JUMPVELOCITY_CROUCH(this) && IS_DUCKED(this)) ? PHYS_JUMPVELOCITY_CROUCH(this) : PHYS_JUMPVELOCITY(this));
        bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
 
        if (MUTATOR_CALLHOOK(PlayerJump, this, mjumpheight, doublejump))
index ae59381e5c11394ce93abcf441cd5c453be4d8a6..0ebea585f80e61071cedd24e4c8e2d22abdec445 100644 (file)
@@ -82,6 +82,7 @@ bool IsFlying(entity a);
 
 #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS)
 #define PHYS_JUMPVELOCITY(s)                STAT(MOVEVARS_JUMPVELOCITY, s)
+#define PHYS_JUMPVELOCITY_CROUCH(s)         STAT(MOVEVARS_JUMPVELOCITY_CROUCH, s)
 
 #define PHYS_MAXAIRSPEED(s)                 STAT(MOVEVARS_MAXAIRSPEED, s)
 #define PHYS_MAXAIRSTRAFESPEED(s)           STAT(MOVEVARS_MAXAIRSTRAFESPEED, s)
index d8af629624b5e8ea10c22f7cc446681fb8346362..0fcd70dfc08fd8e7afd9bdc2cd2b31c063331c79 100644 (file)
@@ -93,7 +93,6 @@ void PlayerStats_GameReport_AddEvent(string event_id)
        }
 }
 
-// referred to by PS_GR_P_ADDVAL and PS_GR_T_ADDVAL
 float PlayerStats_GameReport_Event(string prefix, string event_id, float value)
 {
        if((prefix == "") || PS_GR_OUT_DB < 0) { return 0; }
@@ -108,7 +107,8 @@ float PlayerStats_GameReport_Event(string prefix, string event_id, float value)
 void PlayerStats_GameReport_Accuracy(entity p)
 {
        #define ACCMAC(suffix, field) \
-               PS_GR_P_ADDVAL(p, sprintf("acc-%s-%s", it.netname, suffix), CS(p).accuracy.(field[i-1]));
+               PlayerStats_GameReport_Event_Player(p, \
+                       sprintf("acc-%s-%s", it.netname, suffix), CS(p).accuracy.(field[i-1]));
        FOREACH(Weapons, it != WEP_Null, {
                ACCMAC("hit", accuracy_hit)
                ACCMAC("fired", accuracy_fired)
@@ -126,7 +126,7 @@ void PlayerStats_GameReport_FinalizePlayer(entity p)
        // add global info!
        if(p.alivetime)
        {
-               PS_GR_P_ADDVAL(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
+               PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
                p.alivetime = 0;
        }
 
@@ -139,7 +139,7 @@ void PlayerStats_GameReport_FinalizePlayer(entity p)
                db_put(PS_GR_OUT_DB, sprintf("%s:_team", p.playerstats_id), ftos(p.team));
 
        if(stof(db_get(PS_GR_OUT_DB, sprintf("%s:%s", p.playerstats_id, PLAYERSTATS_ALIVETIME))) > 0)
-               PS_GR_P_ADDVAL(p, PLAYERSTATS_JOINS, 1);
+               PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_JOINS, 1);
 
        PlayerStats_GameReport_Accuracy(p);
        anticheat_report_to_playerstats(p);
@@ -149,7 +149,8 @@ void PlayerStats_GameReport_FinalizePlayer(entity p)
                if(CS(p).latency_cnt)
                {
                        float latency = (CS(p).latency_sum / CS(p).latency_cnt);
-                       if(latency) { PS_GR_P_ADDVAL(p, PLAYERSTATS_AVGLATENCY, latency); }
+                       if(latency)
+                               PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_AVGLATENCY, latency);
                }
        }
 
@@ -167,16 +168,16 @@ void PlayerStats_GameReport(float finished)
 
        FOREACH_CLIENT(true, {
                // add personal score rank
-               PS_GR_P_ADDVAL(it, PLAYERSTATS_RANK, it.score_dummyfield);
+               PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_RANK, it.score_dummyfield);
 
                // scoreboard data
                if(it.scoreboard_pos)
                {
                        // scoreboard is valid!
-                       PS_GR_P_ADDVAL(it, PLAYERSTATS_SCOREBOARD_VALID, 1);
+                       PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_SCOREBOARD_VALID, 1);
 
                        // add scoreboard position
-                       PS_GR_P_ADDVAL(it, PLAYERSTATS_SCOREBOARD_POS, it.scoreboard_pos);
+                       PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_SCOREBOARD_POS, it.scoreboard_pos);
 
                        // add scoreboard data
                        PlayerScore_PlayerStats(it);
@@ -184,8 +185,8 @@ void PlayerStats_GameReport(float finished)
                        // if the match ended normally, add winning info
                        if(finished)
                        {
-                               PS_GR_P_ADDVAL(it, PLAYERSTATS_WINS, it.winning);
-                               PS_GR_P_ADDVAL(it, PLAYERSTATS_MATCHES, 1);
+                               PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_WINS, it.winning);
+                               PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_MATCHES, 1);
                        }
                }
 
index 1fafb13978dc9c43c1467ff3accb10089c55b6ad..28f985e27d24a214afc7519a956f70dd79526cef 100644 (file)
@@ -69,8 +69,8 @@ void PlayerStats_GameReport_AddTeam(float t);
 void PlayerStats_GameReport_AddEvent(string event_id);
 
 // call on each event to track, or at player disconnect OR match end for "global stuff"
-#define PS_GR_P_ADDVAL(ent,eventid,val) PlayerStats_GameReport_Event(ent.playerstats_id, eventid, val)
-#define PS_GR_T_ADDVAL(team,eventid,val) PlayerStats_GameReport_Event(sprintf("team#%d", team), eventid, val)
+#define PlayerStats_GameReport_Event_Player(ent, eventid, val) PlayerStats_GameReport_Event(ent.playerstats_id, eventid, val)
+#define PlayerStats_GameReport_Event_Team(team, eventid, val) PlayerStats_GameReport_Event(sprintf("team#%d", team), eventid, val)
 float PlayerStats_GameReport_Event(string prefix, string event_id, float value);
 
 void PlayerStats_GameReport_Accuracy(entity p);
index 646638a80ca704778f8628513a6225021c154566..476d0dbbaa612445241bd1450c48642a7f78f347 100644 (file)
@@ -34,6 +34,7 @@ REGISTER_SP(DMGTAKEN);
 REGISTER_SP(KILLS);
 REGISTER_SP(DEATHS);
 REGISTER_SP(SUICIDES);
+REGISTER_SP(TEAMKILLS);
 REGISTER_SP(FRAGS);
 
 REGISTER_SP(ELO);
index 94e408d7f614be2d769fc229625a390bfd357ba0..daa1d3c8b7bd205640288198c94a0eb7f010dede 100644 (file)
@@ -217,28 +217,36 @@ float autocvar_sv_dodging_wall_distance_threshold;
 bool autocvar_sv_dodging_frozen;
 bool autocvar_sv_dodging_frozen_doubletap;
 float autocvar_sv_dodging_height_threshold;
-float autocvar_sv_dodging_horiz_speed;
-float autocvar_sv_dodging_horiz_speed_frozen;
+float autocvar_sv_dodging_horiz_speed_min;
+float autocvar_sv_dodging_horiz_speed_max;
+float autocvar_sv_dodging_horiz_force_slowest;
+float autocvar_sv_dodging_horiz_force_fastest;
+float autocvar_sv_dodging_horiz_force_frozen;
 float autocvar_sv_dodging_ramp_time;
 float autocvar_sv_dodging_up_speed;
 bool autocvar_sv_dodging_wall_dodging;
 bool autocvar_sv_dodging_air_dodging;
-float autocvar_sv_dodging_maxspeed = 450;
+float autocvar_sv_dodging_maxspeed;
+float autocvar_sv_dodging_air_maxspeed;
 #endif
 
 #if 0
 REGISTER_STAT(DODGING, int, g_dodging)
 REGISTER_STAT(DODGING_DELAY, float, autocvar_sv_dodging_delay)
 REGISTER_STAT(DODGING_DISTANCE_THRESHOLD, float, autocvar_sv_dodging_wall_distance_threshold)
-REGISTER_STAT(DODGING_FROZEN_NO_DOUBLETAP, int, autocvar_sv_dodging_frozen_doubletap)
+REGISTER_STAT(DODGING_FROZEN_DOUBLETAP, int, autocvar_sv_dodging_frozen_doubletap)
 REGISTER_STAT(DODGING_HEIGHT_THRESHOLD, float, autocvar_sv_dodging_height_threshold)
-REGISTER_STAT(DODGING_HORIZ_SPEED, float, autocvar_sv_dodging_horiz_speed)
-REGISTER_STAT(DODGING_HORIZ_SPEED_FROZEN, float, autocvar_sv_dodging_horiz_speed_frozen)
+REGISTER_STAT(DODGING_HORIZ_SPEED_MIN, float, autocvar_sv_dodging_horiz_speed_min)
+REGISTER_STAT(DODGING_HORIZ_SPEED_MAX, float, autocvar_sv_dodging_horiz_speed_max)
+REGISTER_STAT(DODGING_HORIZ_FORCE_SLOWEST, float, autocvar_sv_dodging_horiz_force_slowest)
+REGISTER_STAT(DODGING_HORIZ_FORCE_FASTEST, float, autocvar_sv_dodging_horiz_force_fastest)
+REGISTER_STAT(DODGING_HORIZ_FORCE_FROZEN, float, autocvar_sv_dodging_horiz_force_frozen)
 REGISTER_STAT(DODGING_RAMP_TIME, float, autocvar_sv_dodging_ramp_time)
 REGISTER_STAT(DODGING_UP_SPEED, float, autocvar_sv_dodging_up_speed)
 REGISTER_STAT(DODGING_WALL, bool, autocvar_sv_dodging_wall_dodging)
 REGISTER_STAT(DODGING_AIR, bool, autocvar_sv_dodging_air_dodging)
 REGISTER_STAT(DODGING_MAXSPEED, float, autocvar_sv_dodging_maxspeed)
+REGISTER_STAT(DODGING_AIR_MAXSPEED, float, autocvar_sv_dodging_air_maxspeed)
 #endif
 /** cvar loopback */
 REGISTER_STAT(DODGING_FROZEN, int, autocvar_sv_dodging_frozen)
@@ -295,6 +303,7 @@ REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity)
 
 #ifdef SVQC
 #include "physics/movetypes/movetypes.qh"
+float warmup_limit;
 #endif
 
 REGISTER_STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, float)
@@ -338,6 +347,7 @@ REGISTER_STAT(MOVEVARS_AIRACCELERATE, float)
 // FIXME: Was 0 on server, 1 on client. Still want that?
 REGISTER_STAT(MOVEVARS_ENTGRAVITY, float, (this.gravity) ? this.gravity : 1)
 REGISTER_STAT(MOVEVARS_JUMPVELOCITY, float)
+REGISTER_STAT(MOVEVARS_JUMPVELOCITY_CROUCH, float)
 REGISTER_STAT(MOVEVARS_MAXAIRSPEED, float)
 REGISTER_STAT(MOVEVARS_STEPHEIGHT, float, autocvar_sv_stepheight)
 REGISTER_STAT(MOVEVARS_AIRACCEL_QW, float)
index 50ae0c336c51393ee61d7a14e70875588fd1d6e3..f4431eed2887f84e812e7bd61bcc46dac87eb6c7 100644 (file)
@@ -877,9 +877,8 @@ float Item_GiveTo(entity item, entity player)
 
 void Item_Touch(entity this, entity toucher)
 {
-
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
-       if (this.classname == "droppedweapon")
+       if (Item_IsLoot(this))
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
@@ -904,17 +903,16 @@ void Item_Touch(entity this, entity toucher)
 
        toucher = M_ARGV(1, entity);
 
-       if (this.classname == "droppedweapon")
+       if (Item_IsExpiring(this))
        {
                this.strength_finished = max(0, this.strength_finished - time);
                this.invincible_finished = max(0, this.invincible_finished - time);
                this.superweapons_finished = max(0, this.superweapons_finished - time);
        }
-       entity it = this.itemdef;
-       bool gave = ITEM_HANDLE(Pickup, it, this, toucher);
+       bool gave = ITEM_HANDLE(Pickup, this.itemdef, this, toucher);
        if (!gave)
        {
-               if (this.classname == "droppedweapon")
+               if (Item_IsExpiring(this))
                {
                        // undo what we did above
                        this.strength_finished += time;
@@ -931,30 +929,40 @@ LABEL(pickup)
        Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
        _sound (toucher, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
 
-       if (this.classname == "droppedweapon")
+       MUTATOR_CALLHOOK(ItemTouched, this, toucher);
+       if (wasfreed(this))
+       {
+               return;
+       }
+
+       if (Item_IsLoot(this))
+       {
                delete(this);
-       else if (this.spawnshieldtime)
+               return;
+       }
+       if (!this.spawnshieldtime)
        {
-               entity e;
-               if(this.team)
+               return;
+       }
+       entity e;
+       if (this.team)
+       {
+               RandomSelection_Init();
+               IL_EACH(g_items, it.team == this.team,
                {
-                       RandomSelection_Init();
-                       IL_EACH(g_items, it.team == this.team,
+                       if (it.itemdef) // is a registered item
                        {
-                               if(it.itemdef) // is a registered item
-                               {
-                                       Item_Show(it, -1);
-                                       it.scheduledrespawntime = 0;
-                                       RandomSelection_AddEnt(it, it.cnt, 0);
-                               }
-                       });
-                       e = RandomSelection_chosen_ent;
-                       Item_Show(e, 1); // reset its state so it is visible (extra sendflags doesn't matter, this happens anyway)
-               }
-               else
-                       e = this;
-               Item_ScheduleRespawn(e);
+                               Item_Show(it, -1);
+                               it.scheduledrespawntime = 0;
+                               RandomSelection_AddEnt(it, it.cnt, 0);
+                       }
+               });
+               e = RandomSelection_chosen_ent;
+               Item_Show(e, 1); // reset its state so it is visible (extra sendflags doesn't matter, this happens anyway)
        }
+       else
+               e = this;
+       Item_ScheduleRespawn(e);
 }
 
 void Item_Reset(entity this)
@@ -962,16 +970,19 @@ void Item_Reset(entity this)
        Item_Show(this, !this.state);
        setorigin(this, this.origin);
 
-       if (this.classname != "droppedweapon")
+       if (Item_IsLoot(this))
        {
-               setthink(this, Item_Think);
-               this.nextthink = time;
-
-               if (this.waypointsprite_attached)
-                       WaypointSprite_Kill(this.waypointsprite_attached);
-
-               if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
-                       Item_ScheduleInitialRespawn(this);
+               return;
+       }
+       setthink(this, Item_Think);
+       this.nextthink = time;
+       if (this.waypointsprite_attached)
+       {
+               WaypointSprite_Kill(this.waypointsprite_attached);
+       }
+       if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
+       {
+               Item_ScheduleInitialRespawn(this);
        }
 }
 
@@ -1203,11 +1214,9 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                return;
        }
 
-       // is it a dropped weapon?
-       if (this.classname == "droppedweapon")
+       if (Item_IsLoot(this))
        {
                this.reset = SUB_Remove;
-               // it's a dropped weapon
                set_movetype(this, MOVETYPE_TOSS);
 
                // Savage: remove thrown items after a certain period of time ("garbage collection")
@@ -1217,7 +1226,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                this.takedamage = DAMAGE_YES;
                this.event_damage = Item_Damage;
 
-               if(this.strength_finished || this.invincible_finished || this.superweapons_finished)
+               if (Item_IsExpiring(this))
                {
                        // if item is worthless after a timer, have it expire then
                        this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
@@ -1332,7 +1341,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
        if(def.instanceOfWeaponPickup)
        {
-               if (this.classname != "droppedweapon") // if dropped, colormap is already set up nicely
+               if (!Item_IsLoot(this)) // if dropped, colormap is already set up nicely
                        this.colormap = 1024; // color shirt=0 pants=0 grey
                else
                        this.gravity = 1;
@@ -1368,6 +1377,13 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
 void StartItem(entity this, GameItem def)
 {
+    def = def.m_spawnfunc_hookreplace(def, this);
+    if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
+    {
+        delete(this);
+        return;
+    }
+    this.classname = def.m_canonical_spawnfunc;
     _StartItem(
        this,
        this.itemdef = def,
@@ -1424,114 +1440,9 @@ void setItemGroupCount()
        }
 }
 
-spawnfunc(item_rockets)
-{
-    StartItem(this, ITEM_Rockets);
-}
-
-spawnfunc(item_bullets)
-{
-       if(!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
-          (this.classname != "droppedweapon"))
-       {
-               weaponswapping = true;
-               spawnfunc_item_shells(this);
-               weaponswapping = false;
-               return;
-       }
-
-    StartItem(this, ITEM_Bullets);
-}
-
-spawnfunc(item_cells)
-{
-       StartItem(this, ITEM_Cells);
-}
-
-spawnfunc(item_plasma)
-{
-       StartItem(this, ITEM_Plasma);
-}
-
-spawnfunc(item_shells)
-{
-       if(!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
-          (this.classname != "droppedweapon"))
-       {
-               weaponswapping = true;
-               spawnfunc_item_bullets(this);
-               weaponswapping = false;
-               return;
-       }
-
-       StartItem(this, ITEM_Shells);
-}
-
-spawnfunc(item_armor_small)
-{
-       StartItem(this, ITEM_ArmorSmall);
-}
-
-spawnfunc(item_armor_medium)
-{
-       StartItem(this, ITEM_ArmorMedium);
-}
-
-spawnfunc(item_armor_big)
-{
-       StartItem(this, ITEM_ArmorBig);
-}
-
-spawnfunc(item_armor_mega)
-{
-       StartItem(this, ITEM_ArmorMega);
-}
-
-spawnfunc(item_health_small)
-{
-       StartItem(this, ITEM_HealthSmall);
-}
-
-spawnfunc(item_health_medium)
-{
-    StartItem(this, ITEM_HealthMedium);
-}
-
-spawnfunc(item_health_big)
-{
-       StartItem(this, ITEM_HealthBig);
-}
-
-spawnfunc(item_health_mega)
-{
-    StartItem(this, ITEM_HealthMega);
-}
-
-// 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_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)
-{
-       StartItem(this, ITEM_Strength);
-}
-
-spawnfunc(item_invincible)
-{
-       StartItem(this, ITEM_Shield);
-}
-
-// compatibility:
-spawnfunc(item_quad) { this.classname = "item_strength";spawnfunc_item_strength(this);}
-
 void target_items_use(entity this, entity actor, entity trigger)
 {
-       if(actor.classname == "droppedweapon")
+       if(Item_IsLoot(actor))
        {
                EXACTTRIGGER_TOUCH(this, trigger);
                delete(actor);
@@ -1546,7 +1457,7 @@ void target_items_use(entity this, entity actor, entity trigger)
                EXACTTRIGGER_TOUCH(this, trigger);
        }
 
-       IL_EACH(g_items, it.enemy == actor && it.classname == "droppedweapon",
+       IL_EACH(g_items, it.enemy == actor && Item_IsLoot(it),
        {
                delete(it);
        });
@@ -1668,31 +1579,6 @@ spawnfunc(target_items)
        }
 }
 
-spawnfunc(item_fuel)
-{
-       StartItem(this, ITEM_JetpackFuel);
-}
-
-spawnfunc(item_fuel_regen)
-{
-       if(start_items & ITEM_JetpackRegen.m_itemid)
-       {
-               spawnfunc_item_fuel(this);
-               return;
-       }
-       StartItem(this, ITEM_JetpackRegen);
-}
-
-spawnfunc(item_jetpack)
-{
-       if(start_items & ITEM_Jetpack.m_itemid)
-       {
-               spawnfunc_item_fuel(this);
-               return;
-       }
-       StartItem(this, ITEM_Jetpack);
-}
-
 float GiveWeapon(entity e, float wpn, float op, float val)
 {
        WepSet v0, v1;
index 80385971ab34d40a1ea17dbcc42e73714042aa9a..5ecbe548824bef246be9e2ccf45b5b95882df09d 100644 (file)
@@ -1,9 +1,5 @@
 #pragma once
 
-#ifdef SVQC
-#include <server/defs.qh>
-#endif
-
 const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
 
 // item networking
@@ -52,17 +48,10 @@ void ItemDrawSimple(entity this);
 
 #endif
 #ifdef SVQC
-spawnfunc(item_strength);
-spawnfunc(item_invincible);
-spawnfunc(item_armor_small);
-spawnfunc(item_shells);
-spawnfunc(item_bullets);
-spawnfunc(item_rockets);
 
 float autocvar_sv_simple_items;
 bool ItemSend(entity this, entity to, int sf);
 
-
 bool have_pickup_item(entity this);
 
 const float ITEM_RESPAWN_TICKS = 10;
index 8ded50d16b2da0d88a35c68eb333006786a15636..ddd62ae4e8f5f3ce5fc22b43d35086cc67d686ad 100644 (file)
@@ -94,11 +94,22 @@ void button_damage(entity this, entity inflictor, entity attacker, float damage,
        if(this.spawnflags & DOOR_NOSPLASH)
                if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
                        return;
-       this.health = this.health - damage;
-       if (this.health <= 0)
+       if (this.spawnflags & BUTTON_DONTACCUMULATEDMG)
        {
-               this.enemy = attacker;
-               button_fire(this);
+               if (this.health <= damage)
+               {
+                       this.enemy = attacker;
+                       button_fire(this);
+               }
+       }
+       else
+       {
+               this.health = this.health - damage;
+               if (this.health <= 0)
+               {
+                       this.enemy = attacker;
+                       button_fire(this);
+               }
        }
 }
 
index 6f70f09beec2219624baeca92e2cd7deaa104fb4..75a6006eb5e703e24b79362c9df7b0f39e328376 100644 (file)
@@ -1 +1,3 @@
 #pragma once
+
+const int BUTTON_DONTACCUMULATEDMG = 128;
index fc3cec863a26920d0547df1143e75af283631f88..3f5203806964b64988426406eef59eda544ec899 100644 (file)
@@ -55,11 +55,6 @@ spawnfunc(misc_teleporter_dest)
        spawnfunc_info_teleport_destination(this);
 }
 
-spawnfunc(target_teleporter)
-{
-       spawnfunc_info_teleport_destination(this);
-}
-
 #elif defined(CSQC)
 
 void teleport_dest_remove(entity this)
index 2ba6e7e3c704f8b283a28dd0e0bfaccee7abc767..373b755371f14fc7f3bfa66fc6a56ec006e9791f 100644 (file)
@@ -228,9 +228,10 @@ entity Simple_TeleportPlayer(entity teleporter, entity player)
 
 void teleport_findtarget(entity this)
 {
+       bool istrigger = (this.solid == SOLID_TRIGGER);
+
        int n = 0;
-       entity e;
-       for(e = NULL; (e = find(e, targetname, this.target)); )
+       for(entity e = NULL; (e = find(e, targetname, this.target)); )
        {
                ++n;
 #ifdef SVQC
@@ -256,7 +257,7 @@ void teleport_findtarget(entity this)
        else if(n == 1)
        {
                // exactly one dest - bots love that
-               this.enemy = find(e, targetname, this.target);
+               this.enemy = find(NULL, targetname, this.target);
        }
        else
        {
@@ -265,9 +266,11 @@ void teleport_findtarget(entity this)
        }
 
        // now enable touch
-       settouch(this, Teleport_Touch);
+       if(istrigger)
+               settouch(this, Teleport_Touch);
 #ifdef SVQC
-       trigger_teleport_link(this);
+       if(istrigger)
+               trigger_teleport_link(this);
 #endif
 }
 
index e7b309062848d0fe673b8d797f4039512f292372..4447ea21e25f0cf7928378fc97a462eb004c2b05 100644 (file)
@@ -13,7 +13,9 @@ void trigger_heal_touch(entity this, entity toucher)
                if (!IS_DEAD(toucher))
                if (toucher.triggerhealtime < time)
                {
-                       EXACTTRIGGER_TOUCH(this, toucher);
+                       bool is_trigger = !boolean(!this.nottargeted && this.targetname != "");
+                       if(is_trigger)
+                               EXACTTRIGGER_TOUCH(this, toucher);
                        toucher.triggerhealtime = time + 1;
 
                        if (toucher.health < this.max_health)
@@ -26,6 +28,11 @@ void trigger_heal_touch(entity this, entity toucher)
        }
 }
 
+void trigger_heal_use(entity this, entity actor, entity trigger)
+{
+       trigger_heal_touch(this, actor);
+}
+
 spawnfunc(trigger_heal)
 {
        this.active = ACTIVE_ACTIVE;
@@ -40,4 +47,17 @@ spawnfunc(trigger_heal)
                this.noise = "misc/mediumhealth.wav";
        precache_sound(this.noise);
 }
+
+spawnfunc(target_heal)
+{
+       this.active = ACTIVE_ACTIVE;
+       this.use = trigger_heal_use;
+       if (!this.health)
+               this.health = 10;
+       if (!this.max_health)
+               this.max_health = 200; //Max health topoff for field
+       if(this.noise == "")
+               this.noise = "misc/mediumhealth.wav";
+       precache_sound(this.noise);
+}
 #endif
index 533f65989e3f283a425a04ab8f713fe49a0cd618..999a615cb7c9ac9b835cc275482720e06cbd76ac 100644 (file)
@@ -35,9 +35,9 @@ vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
 
        torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
 
-       grav = PHYS_GRAVITY(other);
-       if(PHYS_ENTGRAVITY(other))
-               grav *= PHYS_ENTGRAVITY(other);
+       grav = PHYS_GRAVITY(tgt);
+       if(PHYS_ENTGRAVITY(tgt))
+               grav *= PHYS_ENTGRAVITY(tgt);
 
        zdist = torg.z - org.z;
        sdist = vlen(torg - org - zdist * '0 0 1');
index 794f4dc112d017a4e9759dbe84095d628662602e..a82034edcf017d5abad297540444f5111f62110d 100644 (file)
@@ -8,4 +8,6 @@ spawnfunc(trigger_relay)
        this.use = SUB_UseTargets;
        this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
 }
+
+spawnfunc(target_relay) { spawnfunc_trigger_relay(this); }
 #endif
index 5f545f01418488f4375c229715a06040e3fb2c87..0330ce8d8cc46b1e9065ff29065ff51f233dadfd 100644 (file)
@@ -12,52 +12,83 @@ void trigger_teleport_use(entity this, entity actor, entity trigger)
 }
 #endif
 
-void Teleport_Touch(entity this, entity toucher)
+bool Teleport_Active(entity this, entity player)
 {
        if (this.active != ACTIVE_ACTIVE)
-               return;
+               return false;
 
 #ifdef SVQC
-       if (!toucher.teleportable)
-               return;
+       if (!player.teleportable)
+               return false;
 
-       if(toucher.vehicle)
-       if(!toucher.vehicle.teleportable)
-               return;
+       if(player.vehicle)
+       if(!player.vehicle.teleportable)
+               return false;
 
-       if(IS_TURRET(toucher))
-               return;
+       if(IS_TURRET(player))
+               return false;
 #elif defined(CSQC)
-       if(!IS_PLAYER(toucher))
-               return;
+       if(!IS_PLAYER(player))
+               return false;
 #endif
 
-       if(IS_DEAD(toucher))
-               return;
+       if(IS_DEAD(player))
+               return false;
 
        if(this.team)
-               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
-                       return;
+               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, player)))
+                       return false;
 
-       EXACTTRIGGER_TOUCH(this, toucher);
+       return true;
+}
+
+void Teleport_Touch(entity this, entity toucher)
+{
+       entity player = toucher;
+
+       if(!Teleport_Active(this, player))
+               return;
+
+       EXACTTRIGGER_TOUCH(this, player);
 
 #ifdef SVQC
-       if(IS_PLAYER(toucher))
-               RemoveGrapplingHooks(toucher);
+       if(IS_PLAYER(player))
+               RemoveGrapplingHooks(player);
 #endif
 
        entity e;
-       e = Simple_TeleportPlayer(this, toucher);
+       e = Simple_TeleportPlayer(this, player);
 
 #ifdef SVQC
        string s = this.target; this.target = string_null;
-       SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for trigger too?
+       SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
        if (!this.target) this.target = s;
 
-       SUB_UseTargets(e, toucher, toucher);
+       SUB_UseTargets(e, player, player);
 #endif
 }
 
+#ifdef SVQC
+void target_teleport_use(entity this, entity actor, entity trigger)
+{
+       entity player = actor;
+
+       if(!Teleport_Active(this, player))
+               return;
+
+       if(IS_PLAYER(player))
+               RemoveGrapplingHooks(player);
+
+       entity e = Simple_TeleportPlayer(this, player);
+
+       string s = this.target; this.target = string_null;
+       SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
+       if (!this.target) this.target = s;
+
+       SUB_UseTargets(e, player, player);
+}
+#endif
+
 #ifdef SVQC
 float trigger_teleport_send(entity this, entity to, float sf)
 {
@@ -101,6 +132,25 @@ spawnfunc(trigger_teleport)
 
        IL_PUSH(g_teleporters, this);
 }
+
+spawnfunc(target_teleporter)
+{
+       if(this.target == "")
+       {
+               // actually a destination!
+               spawnfunc_info_teleport_destination(this);
+               return;
+       }
+
+       this.active = ACTIVE_ACTIVE;
+
+       this.use = target_teleport_use;
+
+       if(this.noise != "")
+               FOREACH_WORD(this.noise, true, precache_sound(it));
+
+       InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET);
+}
 #elif defined(CSQC)
 NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew)
 {
index 71d7c3db39f464b975e9258764fa08676984eae5..b5f542928f3fc4d869999c8b379a414143eb2a8e 100644 (file)
@@ -37,27 +37,13 @@ const int WS_INUSE  = 3;
 /** idle frame */
 const int WS_READY  = 4;
 
-#ifdef SVQC
-.int ammo_shells;
-.int ammo_nails;
-.int ammo_rockets;
-.int ammo_cells;
-.int ammo_plasma = _STAT(PLASMA);
-.int ammo_fuel = _STAT(FUEL);
-.int ammo_none;
-#else
-.int ammo_shells;
-.int ammo_nails;
-.int ammo_rockets;
-.int ammo_cells;
-.int ammo_plasma;
-.int ammo_fuel;
-.int ammo_none;
-#endif
-
 /** fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" */
 CLASS(Weapon, Object)
        ATTRIB(Weapon, m_id, int, 0);
+       /** the canonical spawnfunc name */
+    ATTRIB(Weapon, m_canonical_spawnfunc, string);
+    /** control what happens when this weapon is spawned */
+    METHOD(Weapon, m_spawnfunc_hookreplace, Weapon(Weapon this, entity e)) { return this; }
     /** A: WEPSET_id : WEPSET_... */
     ATTRIB(Weapon, weapons, WepSet, '0 0 0');
     /** M: ammotype  : main ammo type */
@@ -137,6 +123,18 @@ CLASS(Weapon, Object)
        }
 ENDCLASS(Weapon)
 
+#ifdef SVQC
+
+void weapon_defaultspawnfunc(entity this, Weapon e);
+#define SPAWNFUNC_WEAPON(name, weapon) \
+    spawnfunc(name) { weapon_defaultspawnfunc(this, weapon); }
+
+#else
+
+#define SPAWNFUNC_WEAPON(name, weapon)
+
+#endif
+
 #include <common/items/_mod.qh>
 CLASS(WeaponPickup, Pickup)
     ATTRIB(WeaponPickup, m_weapon, Weapon);
index 5b982219e7cc6708dd0ba994b0e5d2501be34797..23e3dbcb2bc8d235e2921f45ab8803570bd0ea35 100644 (file)
@@ -1,7 +1,6 @@
 #include "arc.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_arc) { weapon_defaultspawnfunc(this, WEP_ARC); }
 
 bool W_Arc_Beam_Send(entity this, entity to, int sf)
 {
index abd9933116b2d86af3506acd6013b3648f6796f9..4ec2d4edc98a1a59053f8a7ac8346c9f5a7bd52e 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Arc, Weapon)
+/* spawnfunc */ ATTRIB(Arc, m_canonical_spawnfunc, string, "weapon_arc");
 /* ammotype  */ ATTRIB(Arc, ammo_type, int, RESOURCE_CELLS);
 /* impulse   */ ATTRIB(Arc, impulse, int, 3);
 /* flags     */ ATTRIB(Arc, spawnflags, int, WEP_TYPE_HITSCAN);
@@ -74,6 +75,7 @@ CLASS(Arc, Weapon)
 ENDCLASS(Arc)
 REGISTER_WEAPON(ARC, arc, NEW(Arc));
 
+SPAWNFUNC_WEAPON(weapon_arc, WEP_ARC)
 
 #ifdef GAMEQC
 const float ARC_MAX_SEGMENTS = 20;
index ac1540cd51fc860038f1f04072b55c75a40e5656..2189c1db209d1d1d68b57949bdd8a5103183bb17 100644 (file)
@@ -1,8 +1,6 @@
 #include "blaster.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(this, WEP_BLASTER); }
-spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); }
 
 void W_Blaster_Touch(entity this, entity toucher)
 {
index 972dcd003f23118654a332337c16904e93ca190d..0a0e7c17d2f57c21cdbee6d3735f0576c566377e 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Blaster, Weapon)
+/* spawnfunc */ ATTRIB(Blaster, m_canonical_spawnfunc, string, "weapon_blaster");
 /* ammotype  */ //ATTRIB(Blaster, ammo_type, int, RESOURCE_NONE);
 /* impulse   */ ATTRIB(Blaster, impulse, int, 1);
 /* flags     */ ATTRIB(Blaster, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
@@ -44,10 +45,14 @@ CLASS(Blaster, Weapon)
 ENDCLASS(Blaster)
 REGISTER_WEAPON(BLASTER, blaster, NEW(Blaster));
 
+SPAWNFUNC_WEAPON(weapon_blaster, WEP_BLASTER)
+SPAWNFUNC_WEAPON(weapon_laser, WEP_BLASTER)
+
 #ifdef SVQC
 .float blaster_damage;
 .float blaster_edgedamage;
 .float blaster_radius;
 .float blaster_force;
 .float blaster_lifetime;
+
 #endif
index 2e2cb519af644ad83fa9133dc6f711227542b72f..246452fe62ff9cce932ec72a698033ef5d8839de 100644 (file)
@@ -1,7 +1,6 @@
 #include "crylink.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(this, WEP_CRYLINK); }
 
 void W_Crylink_CheckLinks(entity e)
 {
index e686cfa94cf87cbaf962ba52ecf77fb52a69e520..5ecef08d9f48bed31aa471cafefe95db1b158666 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Crylink, Weapon)
+/* spawnfunc */ ATTRIB(Crylink, m_canonical_spawnfunc, string, "weapon_crylink");
 /* ammotype  */ ATTRIB(Crylink, ammo_type, int, RESOURCE_CELLS);
 /* impulse   */ ATTRIB(Crylink, impulse, int, 6);
 /* flags     */ ATTRIB(Crylink, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_CANCLIMB | WEP_FLAG_NODUAL);
@@ -59,6 +60,8 @@ CLASS(Crylink, Weapon)
 ENDCLASS(Crylink)
 REGISTER_WEAPON(CRYLINK, crylink, NEW(Crylink));
 
+SPAWNFUNC_WEAPON(weapon_crylink, WEP_CRYLINK)
+
 #ifdef SVQC
 .float gravity;
 .float crylink_waitrelease;
index f4ef7ad32c08b320d139348908b42e7c95cb54bb..46db8358cef76b423c43e5b218e391d6ff876a9a 100644 (file)
@@ -1,8 +1,6 @@
 #include "devastator.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(this, WEP_DEVASTATOR); }
-spawnfunc(weapon_rocketlauncher) { spawnfunc_weapon_devastator(this); }
 
 .entity lastrocket;
 
index 9c419751173120e17ba80911ea9aca37e9b30026..0e8d8b2fbc8b271102ac17363b1435a957db1047 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Devastator, Weapon)
+/* spawnfunc */ ATTRIB(Devastator, m_canonical_spawnfunc, string, "weapon_devastator");
 /* ammotype  */ ATTRIB(Devastator, ammo_type, int, RESOURCE_ROCKETS);
 /* impulse   */ ATTRIB(Devastator, impulse, int, 9);
 /* flags     */ ATTRIB(Devastator, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL);
@@ -62,6 +63,9 @@ CLASS(Devastator, Weapon)
 ENDCLASS(Devastator)
 REGISTER_WEAPON(DEVASTATOR, devastator, NEW(Devastator));
 
+SPAWNFUNC_WEAPON(weapon_devastator, WEP_DEVASTATOR)
+SPAWNFUNC_WEAPON(weapon_rocketlauncher, WEP_DEVASTATOR)
+
 #ifdef SVQC
 .float rl_release;
 .float rl_detonate_later;
index 8b3946e46e43d77b673b7023c89b3ab4c5afbf6c..5aec7fef96367996a8a144268897a65cbfdbf5fa 100644 (file)
@@ -1,7 +1,6 @@
 #include "electro.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_electro) { weapon_defaultspawnfunc(this, WEP_ELECTRO); }
 
 void W_Electro_TriggerCombo(vector org, float rad, entity own)
 {
index 07c967c49bee20b1bb4d31b2f6c87801e3dac863..4018e5926c78ac03364173ce19883119b59d1503 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Electro, Weapon)
+/* spawnfunc */ ATTRIB(Electro, m_canonical_spawnfunc, string, "weapon_electro");
 /* ammotype  */ ATTRIB(Electro, ammo_type, int, RESOURCE_CELLS);
 /* impulse   */ ATTRIB(Electro, impulse, int, 5);
 /* flags     */ ATTRIB(Electro, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
@@ -66,6 +67,7 @@ CLASS(Electro, Weapon)
 ENDCLASS(Electro)
 REGISTER_WEAPON(ELECTRO, electro, NEW(Electro));
 
+SPAWNFUNC_WEAPON(weapon_electro, WEP_ELECTRO)
 
 #ifdef SVQC
 .float electro_count;
index 3f9cd4c4e18f6d731d2d7db54764b983ceb8ec0e..3f6830f33c271516446592883fed8ff4e08a6c06 100644 (file)
@@ -1,7 +1,6 @@
 #include "fireball.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(this, WEP_FIREBALL); }
 
 void W_Fireball_Explode(entity this, entity directhitentity)
 {
index e973d28845cee8ad52c995be9992a5ffd2da3667..4302c9e7973467d5843917f708cc805260db2cce 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Fireball, Weapon)
+/* spawnfunc */ ATTRIB(Fireball, m_canonical_spawnfunc, string, "weapon_fireball");
 /* ammotype  */ //ATTRIB(Fireball, ammo_type, int, RESOURCE_NONE);
 /* impulse   */ ATTRIB(Fireball, impulse, int, 9);
 /* flags     */ ATTRIB(Fireball, spawnflags, int, WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL);
@@ -53,6 +54,8 @@ CLASS(Fireball, Weapon)
 ENDCLASS(Fireball)
 REGISTER_WEAPON(FIREBALL, fireball, NEW(Fireball));
 
+SPAWNFUNC_WEAPON(weapon_fireball, WEP_FIREBALL)
+
 #ifdef SVQC
 .float bot_primary_fireballmooth; // whatever a mooth is
 .vector fireball_impactvec;
index be95d5dde2a3650367840715bf865565d1d69256..ff2e74539ce83faf9acb719b3faf4c26cd376bc7 100644 (file)
@@ -1,7 +1,6 @@
 #include "hagar.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); }
 
 // NO bounce protection, as bounces are limited!
 
index 24c700cc84d9412bf5362bd6d827656cda909d0f..924326fb3a0a65e211ef2a0eba363444e5f1b771 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Hagar, Weapon)
+/* spawnfunc */ ATTRIB(Hagar, m_canonical_spawnfunc, string, "weapon_hagar");
 /* ammotype  */ ATTRIB(Hagar, ammo_type, int, RESOURCE_ROCKETS);
 /* impulse   */ ATTRIB(Hagar, impulse, int, 8);
 /* flags     */ ATTRIB(Hagar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
@@ -56,3 +57,5 @@ CLASS(Hagar, Weapon)
 
 ENDCLASS(Hagar)
 REGISTER_WEAPON(HAGAR, hagar, NEW(Hagar));
+
+SPAWNFUNC_WEAPON(weapon_hagar, WEP_HAGAR)
index 2fb16d6e35f0dd9a1c60ffc96a4034c22ff86596..ae6c9a66372f4637ec28cfacb5890a82987f0e33 100644 (file)
@@ -1,7 +1,6 @@
 #include "hlac.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(this, WEP_HLAC); }
 
 void W_HLAC_Touch(entity this, entity toucher)
 {
index 4664e54d96776a7b02bc3ccdb7d34aa91ebc392c..d2bd427c2397bb90a1a69acb5c8be17382e39da0 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(HLAC, Weapon)
+/* spawnfunc */ ATTRIB(HLAC, m_canonical_spawnfunc, string, "weapon_hlac");
 /* ammotype  */ ATTRIB(HLAC, ammo_type, int, RESOURCE_CELLS);
 /* impulse   */ ATTRIB(HLAC, impulse, int, 6);
 /* flags     */ ATTRIB(HLAC, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
@@ -48,3 +49,5 @@ CLASS(HLAC, Weapon)
 
 ENDCLASS(HLAC)
 REGISTER_WEAPON(HLAC, hlac, NEW(HLAC));
+
+SPAWNFUNC_WEAPON(weapon_hlac, WEP_HLAC)
index d92e0caa8538b4c9ec4ccc5c0bc79a3fef41e7f4..4a21a516947ba0d18a95c178a513b95dbe0269fa 100644 (file)
@@ -2,8 +2,6 @@
 
 #ifdef SVQC
 
-spawnfunc(weapon_hook) { weapon_defaultspawnfunc(this, WEP_HOOK); }
-
 void W_Hook_ExplodeThink(entity this)
 {
        float dt, dmg_remaining_next, f;
index 4988323fda187803dab6218b45002d4bede7bf90..31424d421b14d68009a52e061c9e6dbf252d40e7 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Hook, Weapon)
+/* spawnfunc */ ATTRIB(Hook, m_canonical_spawnfunc, string, "weapon_hook");
 /* ammotype  */ ATTRIB(Hook, ammo_type, int, RESOURCE_FUEL);
 /* impulse   */ ATTRIB(Hook, impulse, int, 0);
 /* flags     */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
@@ -49,6 +50,8 @@ CLASS(Hook, Weapon)
 ENDCLASS(Hook)
 REGISTER_WEAPON(HOOK, hook, NEW(Hook));
 
+SPAWNFUNC_WEAPON(weapon_hook, WEP_HOOK)
+
 CLASS(OffhandHook, OffhandWeapon)
 #ifdef SVQC
     METHOD(OffhandHook, offhand_think, void(OffhandHook this, entity actor, bool key_pressed))
index 8d96c4b1013ca875a0d581408a335877c83c160a..80567c2955383932015ee9bb60eb6c5efac9738f 100644 (file)
@@ -2,17 +2,14 @@
 
 #ifdef SVQC
 
-spawnfunc(weapon_machinegun)
+METHOD(MachineGun, m_spawnfunc_hookreplace, Weapon(MachineGun this, entity e))
 {
-       if(autocvar_sv_q3acompat_machineshotgunswap)
-       if(this.classname != "droppedweapon")
+       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
        {
-               weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
-               return;
+               return WEP_SHOCKWAVE;
        }
-       weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
+       return this;
 }
-spawnfunc(weapon_uzi) { spawnfunc_weapon_machinegun(this); }
 
 void W_MachineGun_MuzzleFlash_Think(entity this)
 {
index 2f0974971ecc80112eb285e589801f88a074dc71..a7ede47a2105125d60a9a07778130c633720ce8e 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(MachineGun, Weapon)
+/* spawnfunc */ ATTRIB(MachineGun, m_canonical_spawnfunc, string, "weapon_machinegun");
 /* ammotype  */ ATTRIB(MachineGun, ammo_type, int, RESOURCE_BULLETS);
 /* impulse   */ ATTRIB(MachineGun, impulse, int, 3);
 /* flags     */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
@@ -54,3 +55,6 @@ CLASS(MachineGun, Weapon)
 
 ENDCLASS(MachineGun)
 REGISTER_WEAPON(MACHINEGUN, machinegun, NEW(MachineGun));
+
+SPAWNFUNC_WEAPON(weapon_machinegun, WEP_MACHINEGUN)
+SPAWNFUNC_WEAPON(weapon_uzi, WEP_MACHINEGUN)
index 99b00a814f20ffbdb4d3d62507ba6ccff96f2e80..575b76d72e12f4fbf9000331b3ed56a32cafd338 100644 (file)
@@ -1,7 +1,6 @@
 #include "minelayer.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(this, WEP_MINE_LAYER); }
 
 void W_MineLayer_Stick(entity this, entity to)
 {
index f113e6439e3bea5f42d569e36d86482ff9b9ffe2..f804aaf44e9c637520a33a2673b47bed06aba853 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(MineLayer, Weapon)
+/* spawnfunc */ ATTRIB(MineLayer, m_canonical_spawnfunc, string, "weapon_minelayer");
 /* ammotype  */ ATTRIB(MineLayer, ammo_type, int, RESOURCE_ROCKETS);
 /* impulse   */ ATTRIB(MineLayer, impulse, int, 4);
 /* flags     */ ATTRIB(MineLayer, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
@@ -53,6 +54,8 @@ CLASS(MineLayer, Weapon)
 ENDCLASS(MineLayer)
 REGISTER_WEAPON(MINE_LAYER, minelayer, NEW(MineLayer));
 
+SPAWNFUNC_WEAPON(weapon_minelayer, WEP_MINE_LAYER)
+
 #ifdef SVQC
 void W_MineLayer_Think(entity this);
 .float minelayer_detonate, mine_explodeanyway;
index 6a3d2e1250a63294e400a4a283e850fef651eb6e..6ada37cd675fd92db33ab02e849df8fcc364968a 100644 (file)
@@ -2,9 +2,6 @@
 
 #ifdef SVQC
 
-spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(this, WEP_MORTAR); }
-spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); }
-
 void W_Mortar_Grenade_Explode(entity this, entity directhitentity)
 {
        if(directhitentity.takedamage == DAMAGE_AIM)
index 4fc5ec9ad2995644c9a72c1f3d268be874f39edf..affec0dbcae952696866a9c6e08c84d15b7ba454 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Mortar, Weapon)
+/* spawnfunc */ ATTRIB(Mortar, m_canonical_spawnfunc, string, "weapon_mortar");
 /* ammotype  */ ATTRIB(Mortar, ammo_type, int, RESOURCE_ROCKETS);
 /* impulse   */ ATTRIB(Mortar, impulse, int, 4);
 /* flags     */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
@@ -53,6 +54,8 @@ CLASS(Mortar, Weapon)
 ENDCLASS(Mortar)
 REGISTER_WEAPON(MORTAR, mortar, NEW(Mortar));
 
+SPAWNFUNC_WEAPON(weapon_mortar, WEP_MORTAR)
+SPAWNFUNC_WEAPON(weapon_grenadelauncher, WEP_MORTAR)
 
 #ifdef SVQC
 .float gl_detonate_later;
index b4dab73cc951c2d8e93ba90c3024da215b799e00..9738914644ceb482b3936d07c734c4cef9eda899 100644 (file)
@@ -3,8 +3,6 @@
 #ifdef SVQC
 #include <common/triggers/trigger/jumppads.qh>
 
-spawnfunc(weapon_porto) { weapon_defaultspawnfunc(this, WEP_PORTO); }
-
 REGISTER_MUTATOR(porto_ticker, true);
 MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) {
        FOREACH_CLIENT(IS_PLAYER(it), it.porto_forbidden = max(0, it.porto_forbidden - 1));
index b46e479aa9f383292498a75e9672cda2013786b4..93b3a6e9f7da4d73251da4dd731ab1810390227c 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(PortoLaunch, Weapon)
+/* spawnfunc */ ATTRIB(PortoLaunch, m_canonical_spawnfunc, string, "weapon_porto");
 /* ammotype  */ ATTRIB(PortoLaunch, ammo_type, int, RESOURCE_NONE);
 /* impulse   */ ATTRIB(PortoLaunch, impulse, int, 0);
 /* flags     */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON | WEP_FLAG_NODUAL);
@@ -35,6 +36,8 @@ CLASS(PortoLaunch, Weapon)
 ENDCLASS(PortoLaunch)
 REGISTER_WEAPON(PORTO, porto, NEW(PortoLaunch));
 
+SPAWNFUNC_WEAPON(weapon_porto, WEP_PORTO)
+
 #ifdef SVQC
 .entity porto_current;
 .vector porto_v_angle; // holds "held" view angles
index 54784251281c0b488c4867123984548e060ae028..0e49171122634db1b0aaecce386dd1ccc984914d 100644 (file)
@@ -1,9 +1,6 @@
 #include "rifle.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(this, WEP_RIFLE); }
-spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); }
-spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); }
 
 void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, Sound pSound, entity actor)
 {
index b1c01b86ff861238a106b1d03a2b8dad77d8e55a..560354c0529b40b5e19e9a4ff389a01946dae6c6 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Rifle, Weapon)
+/* spawnfunc */ ATTRIB(Rifle, m_canonical_spawnfunc, string, "weapon_rifle");
 /* ammotype  */ ATTRIB(Rifle, ammo_type, int, RESOURCE_BULLETS);
 /* impulse   */ ATTRIB(Rifle, impulse, int, 7);
 /* flags     */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
@@ -47,6 +48,9 @@ CLASS(Rifle, Weapon)
 ENDCLASS(Rifle)
 REGISTER_WEAPON(RIFLE, rifle, NEW(Rifle));
 
+SPAWNFUNC_WEAPON(weapon_rifle, WEP_RIFLE)
+SPAWNFUNC_WEAPON(weapon_campingrifle, WEP_RIFLE)
+SPAWNFUNC_WEAPON(weapon_sniperrifle, WEP_RIFLE)
 
 #ifdef SVQC
 .float rifle_accumulator;
index b2df4f0efb54c53e5b951f9c874e3ce651b4b25e..5e3faeeab94a347424dda81601187a329f229f53 100644 (file)
@@ -1,7 +1,6 @@
 #include "seeker.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(this, WEP_SEEKER); }
 
 // ============================
 // Begin: Missile functions, these are general functions to be manipulated by other code
index 443d0843d01b3e1fea2bba4ba9d11a4cc182ba4d..e4e9fd535248f4b1f58bfe71e96866d9ac2f9306 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Seeker, Weapon)
+/* spawnfunc */ ATTRIB(Seeker, m_canonical_spawnfunc, string, "weapon_seeker");
 /* ammotype  */ ATTRIB(Seeker, ammo_type, int, RESOURCE_ROCKETS);
 /* impulse   */ ATTRIB(Seeker, impulse, int, 8);
 /* flags     */ ATTRIB(Seeker, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
@@ -82,6 +83,8 @@ CLASS(Seeker, Weapon)
 ENDCLASS(Seeker)
 REGISTER_WEAPON(SEEKER, seeker, NEW(Seeker));
 
+SPAWNFUNC_WEAPON(weapon_seeker, WEP_SEEKER)
+
 #ifdef SVQC
 .entity tag_target, wps_tag_tracker;
 .float tag_time;
index bc9e94767a45177e35b3dd788f283f3beeeb7c00..0ca4d528fe62ea550776a9d6ee1c9ad56dc6a526 100644 (file)
@@ -3,16 +3,14 @@
 REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE)
 
 #ifdef SVQC
-spawnfunc(weapon_shockwave)
+METHOD(Shockwave, m_spawnfunc_hookreplace, Weapon(Shockwave this, entity e))
 {
        //if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO
-       if(autocvar_sv_q3acompat_machineshotgunswap)
-       if(this.classname != "droppedweapon")
+       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
        {
-               weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
-               return;
+               return WEP_MACHINEGUN;
        }
-       weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
+       return this;
 }
 
 const float MAX_SHOCKWAVE_HITS = 10;
@@ -318,13 +316,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity)
        if(autocvar_g_antilag == 0 || noantilag)
                lag = 0; // only do hitscan, but no antilag
        if(lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag));
-               IL_EACH(g_monsters, it != actor,
-               {
-                       antilag_takeback(it, it, time - lag);
-               });
-       }
+               antilag_takeback_all(actor, lag);
 
        while(head)
        {
@@ -598,13 +590,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity)
        }
 
        if(lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it)));
-               IL_EACH(g_monsters, it != actor,
-               {
-                       antilag_restore(it, it);
-               });
-       }
+               antilag_restore_all(actor);
 }
 
 METHOD(Shockwave, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
index 89685376dad9a6fd52b6b61f966fdbe2d35ba7ac..ade2e9a85c46a08984c3adb698f80254323021a2 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Shockwave, Weapon)
+/* spawnfunc */ ATTRIB(Shockwave, m_canonical_spawnfunc, string, "weapon_shockwave");
 /* ammotype  */ //ATTRIB(Shockwave, ammo_type, int, RESOURCE_NONE);
 /* impulse   */ ATTRIB(Shockwave, impulse, int, 2);
 /* flags     */ ATTRIB(Shockwave, spawnflags, int, WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_TYPE_MELEE_SEC);
@@ -74,6 +75,7 @@ CLASS(Shockwave, Weapon)
 ENDCLASS(Shockwave)
 REGISTER_WEAPON(SHOCKWAVE, shockwave, NEW(Shockwave));
 
+SPAWNFUNC_WEAPON(weapon_shockwave, WEP_SHOCKWAVE)
 
 #ifdef CSQC
 void Net_ReadShockwaveParticle();
index e163df4bc61d1958c0ff203bbd8937005abaddef..9ffef64287ca8cfe7841bebc53c46a0232de05aa 100644 (file)
@@ -1,7 +1,6 @@
 #include "shotgun.qh"
 
 #ifdef SVQC
-spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); }
 
 void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary)
 {
index cd646a768f456aa2c74c9bdf8dda6f909ef8b305..e40b1d8a1f7f8aa07914fd2a4afabc325cff901d 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Shotgun, Weapon)
+/* spawnfunc */ ATTRIB(Shotgun, m_canonical_spawnfunc, string, "weapon_shotgun");
 /* ammotype  */ ATTRIB(Shotgun, ammo_type, int, RESOURCE_SHELLS);
 /* impulse   */ ATTRIB(Shotgun, impulse, int, 2);
 /* flags     */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_TYPE_MELEE_SEC);
@@ -52,3 +53,5 @@ CLASS(Shotgun, Weapon)
 
 ENDCLASS(Shotgun)
 REGISTER_WEAPON(SHOTGUN, shotgun, NEW(Shotgun));
+
+SPAWNFUNC_WEAPON(weapon_shotgun, WEP_SHOTGUN)
index a5ae87c36b392256205cc47dd79aa879c628ae7a..37c36964a2299655452f9cffb2e9397181aaaabe 100644 (file)
@@ -10,8 +10,6 @@
 .float tuba_lastnotes_cnt; // over
 .vector tuba_lastnotes[MAX_TUBANOTES];
 
-spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(this, WEP_TUBA); }
-
 bool W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
 {
        float i, j, mmin, mmax, nolength;
index 714a2b8b5853566a8175049433744f69dcdfc4c8..ffa1dd6e2dd5589e5623611655bce63a87bf4042 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Tuba, Weapon)
+/* spawnfunc */ ATTRIB(Tuba, m_canonical_spawnfunc, string, "weapon_tuba");
 /* impulse   */ ATTRIB(Tuba, impulse, int, 1);
 /* flags     */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL);
 /* rating    */ ATTRIB(Tuba, bot_pickupbasevalue, float, 2000);
@@ -40,6 +41,8 @@ CLASS(Tuba, Weapon)
 ENDCLASS(Tuba)
 REGISTER_WEAPON(TUBA, tuba, NEW(Tuba));
 
+SPAWNFUNC_WEAPON(weapon_tuba, WEP_TUBA)
+
 #ifdef CSQC
 entityclass(Tuba);
 class(Tuba) .int note;
index 73822b4c50fcbecad3a12f3189253ee4df03054c..4a9475a9ccebc212ebfc9ff36016c546865a1aef 100644 (file)
@@ -105,8 +105,6 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
 #endif
 
 #ifdef SVQC
-spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(this, WEP_VAPORIZER); }
-spawnfunc(weapon_minstanex) { spawnfunc_weapon_vaporizer(this); }
 
 void W_RocketMinsta_Explosion(entity actor, vector loc)
 {
index 0c5c19200a437d67a063ffe58fd43785557bcafc..ea9f8dd2ba60db6a8452b088e424e5ed0d976046 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Vaporizer, Weapon)
+/* spawnfunc */ ATTRIB(Vaporizer, m_canonical_spawnfunc, string, "weapon_vaporizer");
 /* ammotype  */ ATTRIB(Vaporizer, ammo_type, int, RESOURCE_CELLS);
 /* impulse   */ ATTRIB(Vaporizer, impulse, int, 7);
 /* flags     */ ATTRIB(Vaporizer, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN | WEP_FLAG_NODUAL);
@@ -50,6 +51,8 @@ CLASS(Vaporizer, Weapon)
 ENDCLASS(Vaporizer)
 REGISTER_WEAPON(VAPORIZER, vaporizer, NEW(Vaporizer));
 
+SPAWNFUNC_WEAPON(weapon_vaporizer, WEP_VAPORIZER)
+SPAWNFUNC_WEAPON(weapon_minstanex, WEP_VAPORIZER)
 
 #ifdef SVQC
 .float vaporizer_lasthit;
index 202780e29afc4d982400df3cd2ee3ad98fa43f5b..60557cbf8d42211602f985a6ba32da1c22551fa1 100644 (file)
@@ -71,8 +71,6 @@ NET_HANDLE(TE_CSQC_VORTEXBEAMPARTICLE, bool isNew)
 #endif
 
 #ifdef SVQC
-spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(this, WEP_VORTEX); }
-spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); }
 
 REGISTER_MUTATOR(vortex_charge, true);
 
index 5a41b90d8efede1e64457ac4001c09c86ea9b7b1..59152e2759f93b144585d9b8fcc6f394e6751627 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 CLASS(Vortex, Weapon)
+/* spawnfunc */ ATTRIB(Vortex, m_canonical_spawnfunc, string, "weapon_vortex");
 /* ammotype  */ ATTRIB(Vortex, ammo_type, int, RESOURCE_CELLS);
 /* impulse   */ ATTRIB(Vortex, impulse, int, 7);
 /* flags     */ ATTRIB(Vortex, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_NODUAL);
@@ -59,8 +60,9 @@ CLASS(Vortex, Weapon)
 ENDCLASS(Vortex)
 REGISTER_WEAPON(VORTEX, vortex, NEW(Vortex));
 
+SPAWNFUNC_WEAPON(weapon_vortex, WEP_VORTEX)
+SPAWNFUNC_WEAPON(weapon_nex, WEP_VORTEX)
 
 #ifdef SVQC
-
 .float vortex_lasthit;
 #endif
index 65b1a7f6416485d2777926c3c86e1aae006a9a02..82d3a50719356083ae96fbb18d691dfc2b9f83df 100644 (file)
@@ -819,12 +819,12 @@ bool WarpZoneLib_MoveOutOfSolid(entity e)
        vector m1 = e.maxs;
        e.mins = '0 0 0';
        e.maxs = '0 0 0';
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m0.x); e.mins.x = m0.x;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m1.x); e.maxs.x = m1.x;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m0.y); e.mins.y = m0.y;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m1.y); e.maxs.y = m1.y;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m0.z); e.mins.z = m0.z;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m1.z); e.maxs.z = m1.z;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m0.x); e.mins_x = m0.x;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m1.x); e.maxs_x = m1.x;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m0.y); e.mins_y = m0.y;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m1.y); e.maxs_y = m1.y;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m0.z); e.mins_z = m0.z;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m1.z); e.maxs_z = m1.z;
        setorigin(e, e.origin);
 
        tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
index 2f37eb7e2b665c260d6e5174797eaaec3d58cda4..baeb9a51bcbf127d14f8dc89acb36b6814ab5901 100644 (file)
@@ -198,6 +198,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
                        break;
        }
        me.encryptLabel.setText(me.encryptLabel, me.currentServerEncrypt);
+       setZonedTooltip(me.encryptLabel, _("Use the `crypto_aeslevel` cvar to change your preferences"), string_null);
 
        s = crypto_getidfp(me.currentServerCName);
        if (!s) { s = _("N/A"); }
index de7e01aa7a862d3f328d0c02a3fdd542e5238ad9..569301c5d65c369b60deb900f44be3bf1447c5bf 100644 (file)
@@ -14,6 +14,7 @@
 #include <server/impulse.qc>
 #include <server/ipban.qc>
 #include <server/item_key.qc>
+#include <server/items.qc>
 #include <server/mapvoting.qc>
 #include <server/matrix.qc>
 #include <server/miscfunctions.qc>
index 67f6aae4db511fd7cb1b91cb1cdb7412c7f017ea..2013fd6bb5db5c521737cf0985fc65c13a43846e 100644 (file)
@@ -14,6 +14,7 @@
 #include <server/impulse.qh>
 #include <server/ipban.qh>
 #include <server/item_key.qh>
+#include <server/items.qh>
 #include <server/mapvoting.qh>
 #include <server/matrix.qh>
 #include <server/miscfunctions.qh>
index 7173ae5970721388811a5f639476540732851b54..9327a203416598b10e37229d7909f3e34f0eb86d 100644 (file)
@@ -202,9 +202,10 @@ void anticheat_report_to_eventlog(entity this) {
 }
 
 void anticheat_report_to_playerstats(entity this) {
-       PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, "_time"), servertime - CS(this).anticheat_jointime);
+       PlayerStats_GameReport_Event_Player(this,
+               strcat(PLAYERSTATS_ANTICHEAT, "_time"), servertime - CS(this).anticheat_jointime);
 #define ANTICHEAT_REPORT_ONE(name, f, tmin, mi, ma) \
-       PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, name), f)
+       PlayerStats_GameReport_Event_Player(this, strcat(PLAYERSTATS_ANTICHEAT, name), f)
        ANTICHEATS(ANTICHEAT_REPORT_ONE);
 #undef ANTICHEAT_REPORT_ONE
 }
index 46c871876108005a0a69978681454bf1a9446f09..4062f7f660df2d15dfb01ea48bdf822a81599995 100644 (file)
@@ -119,3 +119,30 @@ void antilag_clear(entity e, entity store)
        }
        store.antilag_index = ANTILAG_MAX_ORIGINS - 1; // next one is 0
 }
+
+// TODO: use a single intrusive list across all antilagged entities
+void antilag_takeback_all(entity ignore, float lag)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && it != ignore, antilag_takeback(it, CS(it), time - lag));
+       IL_EACH(g_monsters, it != ignore,
+       {
+               antilag_takeback(it, it, time - lag);
+       });
+       IL_EACH(g_projectiles, it != ignore && it.classname == "nade",
+       {
+               antilag_takeback(it, it, time - lag);
+       });
+}
+
+void antilag_restore_all(entity ignore)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && it != ignore, antilag_restore(it, CS(it)));
+       IL_EACH(g_monsters, it != ignore,
+       {
+               antilag_restore(it, it);
+       });
+       IL_EACH(g_projectiles, it != ignore && it.classname == "nade",
+       {
+               antilag_restore(it, it);
+       });
+}
index 6cf392eed5ae1e8bd2bc125b3a19aa5b9e70d6dd..d57762ccd3899ef8588a560229b39e25ac89210e 100644 (file)
@@ -6,6 +6,9 @@ void antilag_takeback(entity e, entity store, float t);
 void antilag_restore(entity e, entity store);
 void antilag_clear(entity e, entity store);
 
+void antilag_takeback_all(entity ignore, float lag);
+void antilag_restore_all(entity ignore);
+
 .float antilag_debug;
 
 #define ANTILAG_LATENCY(e) min(0.4, CS(e).ping * 0.001)
index 4303aa9764ea3d9164380fe49023c410824786bf..b71d4459d1387469f9c1732fa906dda15617f965 100644 (file)
@@ -311,6 +311,7 @@ string autocvar_sv_jumpspeedcap_max;
 float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
 string autocvar_sv_jumpspeedcap_min;
 float autocvar_sv_jumpvelocity;
+float autocvar_sv_jumpvelocity_crouch;
 bool autocvar_sv_logscores_bots;
 bool autocvar_sv_logscores_console;
 bool autocvar_sv_logscores_file;
index d33aadfac8ad3567b1a1543acfd6f6e4e0e11892..6d0b3c6a1cae803294d4adb588868ab6da8dba94 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <server/defs.qh>
 #include <server/miscfunctions.qh>
+#include <server/items.qh>
 #include "havocbot.qh"
 
 #include "../cvars.qh"
@@ -81,7 +82,7 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float
                        continue;
 
                // Check if the item can be picked up safely
-               if(it.classname == "droppedweapon")
+               if(Item_IsLoot(it))
                {
                        if(!IS_ONGROUND(it))
                                continue;
index 45e71d5591389cdeaef0d000d44d240c16a707b1..d2fae8b50b1f60350d5d37f88b2de72bdada324e 100644 (file)
@@ -270,7 +270,7 @@ void PutObserverInServer(entity this)
        if (this.alivetime)
        {
                if (!warmup_stage)
-                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+                       PlayerStats_GameReport_Event_Player(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
                this.alivetime = 0;
        }
 
@@ -654,9 +654,11 @@ void PutPlayerInServer(entity this)
 
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
 
+       // player was spectator
        if (CS(this).killcount == FRAGS_SPECTATOR) {
                PlayerScore_Clear(this);
                CS(this).killcount = 0;
+               CS(this).startplaytime = time;
        }
 
        for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
@@ -873,19 +875,19 @@ Called when a client types 'kill' in the console
 .float clientkill_nexttime;
 void ClientKill_Now_TeamChange(entity this)
 {
-       if(CS(this).killindicator_teamchange == -1)
+       if(this.killindicator_teamchange == -1)
        {
                JoinBestTeam( this, false, true );
        }
-       else if(CS(this).killindicator_teamchange == -2)
+       else if(this.killindicator_teamchange == -2)
        {
                if(blockSpectators)
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
                PutObserverInServer(this);
        }
        else
-               SV_ChangeTeam(this, CS(this).killindicator_teamchange - 1);
-       CS(this).killindicator_teamchange = 0;
+               SV_ChangeTeam(this, this.killindicator_teamchange - 1);
+       this.killindicator_teamchange = 0;
 }
 
 void ClientKill_Now(entity this)
@@ -893,7 +895,7 @@ void ClientKill_Now(entity this)
        if(this.vehicle)
        {
            vehicles_exit(this.vehicle, VHEF_RELEASE);
-           if(!CS(this).killindicator_teamchange)
+           if(!this.killindicator_teamchange)
            {
             this.vehicle_health = -1;
             Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0');
@@ -905,7 +907,7 @@ void ClientKill_Now(entity this)
 
        this.killindicator = NULL;
 
-       if(CS(this).killindicator_teamchange)
+       if(this.killindicator_teamchange)
                ClientKill_Now_TeamChange(this);
 
        if (!IS_SPEC(this) && !IS_OBSERVER(this) && MUTATOR_CALLHOOK(ClientKill_Now, this) == false)
@@ -970,7 +972,7 @@ void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change,
        return;
     killtime = M_ARGV(1, float);
 
-       CS(this).killindicator_teamchange = targetteam;
+       this.killindicator_teamchange = targetteam;
 
     if(!this.killindicator)
        {
index 2282c09cbb1cafd54e3a667886430b98b554c342..42d7d9560b4e92359242e19428f242d146cbff0a 100644 (file)
@@ -73,7 +73,6 @@ CLASS(Client, Object)
 
     ATTRIB(Client, parm_idlesince, int, this.parm_idlesince);
     ATTRIB(Client, muted, bool, this.muted);
-    ATTRIB(Client, killindicator_teamchange, int, this.killindicator_teamchange);
     ATTRIB(Client, idlekick_lasttimeleft, float, this.idlekick_lasttimeleft);
     ATTRIB(Client, pm_frametime, float, this.pm_frametime);
     ATTRIB(Client, pressedkeys, int, this.pressedkeys);
@@ -86,6 +85,7 @@ CLASS(Client, Object)
     ATTRIB(Client, motd_actived_time, float, this.motd_actived_time);
     ATTRIB(Client, jointime, float, this.jointime);
     ATTRIB(Client, spectatortime, float, this.spectatortime);
+    ATTRIB(Client, startplaytime, float, this.startplaytime);
     ATTRIB(Client, version_nagtime, float, this.version_nagtime);
     ATTRIB(Client, netname_previous, string, this.netname_previous);
     ATTRIB(Client, allowed_timeouts, int, this.allowed_timeouts);
index 1ed78ad7c3ea01dac3f6470e7fcb8eca4359b018..1a826c6f142daf0164d83a7a27f59b37c09d9820 100644 (file)
@@ -430,7 +430,8 @@ void ReadyRestart_force()
        FOREACH_CLIENT(IS_PLAYER(it), {
                it.alivetime = 0;
                CS(it).killcount = 0;
-               PS_GR_P_ADDVAL(it, PLAYERSTATS_ALIVETIME, -PS_GR_P_ADDVAL(it, PLAYERSTATS_ALIVETIME, 0));
+               float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
+               PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
        });
 
        restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
index 126a0f6f6e7a98fc357917b8707f0c34ebf7dc8b..e830fe6c4fe8ed7caf27748935faa8d41cdd4967 100644 (file)
@@ -4,29 +4,15 @@
 #include <server/miscfunctions.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_WEAPON(weapon_nailgun, WEP_ELECTRO)
+SPAWNFUNC_WEAPON(weapon_supernailgun, WEP_HAGAR)
+SPAWNFUNC_WEAPON(weapon_supershotgun, WEP_MACHINEGUN)
 
-spawnfunc(item_spikes) {spawnfunc_item_bullets(this);}
+SPAWNFUNC_ITEM(item_spikes, ITEM_Bullets)
 //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
-
-
-
+SPAWNFUNC_ITEM(item_armor2, ITEM_ArmorMega)
+SPAWNFUNC_ITEM(item_armorInv, ITEM_ArmorMega) // TODO: make sure we actually want this
+spawnfunc(item_health) {if (this.spawnflags & 2) StartItem(this, ITEM_HealthMega);else StartItem(this, ITEM_HealthMedium);}
index df99f1570388c1a92bdcc6300d80b81027c83262..19f9ebf404426be0080aa8e389821860a431a54c 100644 (file)
@@ -1,15 +1,12 @@
 #include "quake2.qh"
 
-spawnfunc(item_armor_medium);
-
-spawnfunc(item_invincible);
-
+#include <common/items/_mod.qh>
 
 //***********************
 //QUAKE 2 ENTITIES - So people can play quake2 maps with the xonotic weapons
 //***********************
-spawnfunc(item_armor_jacket) {spawnfunc_item_armor_medium(this);}
+SPAWNFUNC_ITEM(item_armor_jacket, ITEM_ArmorMedium)
 
-spawnfunc(item_invulnerability) {spawnfunc_item_invincible(this);}
+SPAWNFUNC_ITEM(item_invulnerability, ITEM_Shield)
 
 // rest of the quake 2 entities are handled by q1 and q3 compat
index 2e2301d0bdec150704ae2b6f297beddbda5870a2..6a7f21aa674d319bad7ece6abeb908b28b764d60 100644 (file)
@@ -2,32 +2,11 @@
 
 #include <server/defs.qh>
 #include <server/miscfunctions.qh>
+#include <server/items.qh>
 #include <common/weapons/_all.qh>
 
-spawnfunc(weapon_crylink);
-spawnfunc(weapon_electro);
-spawnfunc(weapon_hagar);
-spawnfunc(weapon_hook);
-spawnfunc(weapon_machinegun);
-spawnfunc(weapon_vortex);
-spawnfunc(weapon_minelayer);
-
 spawnfunc(target_items);
 
-spawnfunc(item_bullets);
-spawnfunc(item_cells);
-spawnfunc(item_rockets);
-spawnfunc(item_shells);
-
-spawnfunc(item_strength);
-
-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
 //***********************
@@ -35,51 +14,51 @@ spawnfunc(item_health_mega);
 // 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);         }
+SPAWNFUNC_ITEM(ammo_shells, ITEM_Shells)
 
 // MG -> MG
-spawnfunc(ammo_bullets)        { spawnfunc_item_bullets(this);        }
+SPAWNFUNC_ITEM(ammo_bullets, ITEM_Bullets)
 
 // GL -> Mortar
-spawnfunc(ammo_grenades)       { spawnfunc_item_rockets(this);        }
+SPAWNFUNC_ITEM(ammo_grenades, ITEM_Rockets)
 
 // Mines -> Rockets
-spawnfunc(weapon_prox_launcher) { spawnfunc_weapon_minelayer(this);   }
-spawnfunc(ammo_mines)           { spawnfunc_item_rockets(this);       }
+SPAWNFUNC_WEAPON(weapon_prox_launcher, WEP_MINE_LAYER)
+SPAWNFUNC_ITEM(ammo_mines, ITEM_Rockets)
 
 // LG -> Lightning
-spawnfunc(weapon_lightning)    { spawnfunc_weapon_electro(this);      }
-spawnfunc(ammo_lightning)      { spawnfunc_item_cells(this);          }
+SPAWNFUNC_WEAPON(weapon_lightning, WEP_ELECTRO)
+SPAWNFUNC_ITEM(ammo_lightning, ITEM_Cells)
 
 // Plasma -> Hagar
-spawnfunc(weapon_plasmagun)    { spawnfunc_weapon_hagar(this);        }
-spawnfunc(ammo_cells)          { spawnfunc_item_rockets(this);        }
+SPAWNFUNC_WEAPON(weapon_plasmagun, WEP_HAGAR)
+SPAWNFUNC_ITEM(ammo_cells, ITEM_Rockets)
 
 // Rail -> Vortex
-spawnfunc(weapon_railgun)      { spawnfunc_weapon_vortex(this);       }
-spawnfunc(ammo_slugs)          { spawnfunc_item_cells(this);          }
+SPAWNFUNC_WEAPON(weapon_railgun, WEP_VORTEX)
+SPAWNFUNC_ITEM(ammo_slugs, ITEM_Cells)
 
 // BFG -> Crylink
-spawnfunc(weapon_bfg)          { spawnfunc_weapon_crylink(this);      }
-spawnfunc(ammo_bfg)            { spawnfunc_item_cells(this);          }
+SPAWNFUNC_WEAPON(weapon_bfg, WEP_CRYLINK)
+SPAWNFUNC_ITEM(ammo_bfg, ITEM_Cells)
 
 // grappling hook -> hook
-spawnfunc(weapon_grapplinghook) { spawnfunc_weapon_hook(this);        }
+SPAWNFUNC_WEAPON(weapon_grapplinghook, WEP_HOOK)
 
 // RL -> RL
-spawnfunc(ammo_rockets)        { spawnfunc_item_rockets(this);        }
+SPAWNFUNC_ITEM(ammo_rockets, ITEM_Rockets)
 
 // 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);     }
+SPAWNFUNC_ITEM(item_armor_body, ITEM_ArmorMega)
+SPAWNFUNC_ITEM(item_armor_combat, ITEM_ArmorBig)
+SPAWNFUNC_ITEM(item_armor_shard, ITEM_ArmorSmall)
+SPAWNFUNC_ITEM(item_enviro, ITEM_Shield)
 
 // medkit -> armor (we have no holdables)
-spawnfunc(holdable_medkit)        { spawnfunc_item_armor_mega(this);     }
+SPAWNFUNC_ITEM(holdable_medkit, ITEM_ArmorMega)
 
 // doubler -> strength
-spawnfunc(item_doubler)        { spawnfunc_item_strength(this); }
+SPAWNFUNC_ITEM(item_doubler, ITEM_Strength)
 
 .float wait;
 .float delay;
@@ -116,27 +95,31 @@ void target_give_init(entity this)
 {
        IL_EACH(g_items, it.targetname == this.target,
        {
-               if (it.classname == "weapon_rocketlauncher" || it.classname == "weapon_devastator") {
+               if (it.classname == "weapon_devastator") {
                        this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo);
                        this.netname = cons(this.netname, "devastator");
                }
-               else if (it.classname == "weapon_lightning") {
+               else if (it.classname == "weapon_vortex") {
+                       this.ammo_cells += it.count * WEP_CVAR_PRI(vortex, ammo); // WEAPONTODO
+                       this.netname = cons(this.netname, "vortex");
+               }
+               else if (it.classname == "weapon_electro") {
                        this.ammo_cells += it.count * WEP_CVAR_PRI(electro, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "electro");
                }
-               else if (it.classname == "weapon_plasmagun") {
+               else if (it.classname == "weapon_hagar") {
                        this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "hagar");
                }
-               else if (it.classname == "weapon_bfg") {
+               else if (it.classname == "weapon_crylink") {
                        this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo);
                        this.netname = cons(this.netname, "crylink");
                }
-               else if (it.classname == "weapon_grenadelauncher" || it.classname == "weapon_mortar") {
+               else if (it.classname == "weapon_mortar") {
                        this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "mortar");
                }
-               else if (it.classname == "item_armor_body")
+               else if (it.classname == "item_armor_mega")
                        this.armorvalue = 100;
                else if (it.classname == "item_health_mega")
                        this.health = 200;
index 6c69859fdfe0c6e748acc5b227227f49d5e9626c..d2577b46afb356fef97917bf577493f0e483fbaf 100644 (file)
@@ -2,65 +2,41 @@
 
 #include <server/defs.qh>
 #include <server/miscfunctions.qh>
+#include <server/items.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);
 
 //***********************
-//WORD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
+//WORLD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
 //***********************
 
 //spawnfunc(item_revival)     /* handled by buffs mutator */
 //spawnfunc(item_jumper)      /* handled by buffs mutator */
 
-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_WEAPON(weapon_punchy, WEP_ARC)
+SPAWNFUNC_WEAPON(weapon_nipper, WEP_MACHINEGUN)
+SPAWNFUNC_WEAPON(weapon_pumper, WEP_SHOTGUN)
+SPAWNFUNC_WEAPON(weapon_boaster, WEP_ELECTRO)
+SPAWNFUNC_WEAPON(weapon_splasher, WEP_VORTEX)
+SPAWNFUNC_WEAPON(weapon_bubbleg, WEP_HAGAR)
+SPAWNFUNC_WEAPON(weapon_balloony, WEP_MORTAR)
+SPAWNFUNC_WEAPON(weapon_betty, WEP_DEVASTATOR)
+SPAWNFUNC_WEAPON(weapon_imperius, WEP_CRYLINK)
+
+SPAWNFUNC_ITEM(ammo_pumper, ITEM_Shells)
+SPAWNFUNC_ITEM(ammo_nipper, ITEM_Bullets)
+SPAWNFUNC_ITEM(ammo_balloony, ITEM_Rockets)
+SPAWNFUNC_ITEM(ammo_bubbleg, ITEM_Rockets)
+SPAWNFUNC_ITEM(ammo_boaster, ITEM_Cells)
+SPAWNFUNC_ITEM(ammo_betty, ITEM_Rockets)
+SPAWNFUNC_ITEM(ammo_imperius, ITEM_Cells)
+
+SPAWNFUNC_ITEM(item_padpower, ITEM_Strength)
+SPAWNFUNC_ITEM(item_climber, ITEM_Shield)
 spawnfunc(item_speedy)         { spawnfunc_item_haste(this);                   }
 spawnfunc(item_visionless)     { spawnfunc_item_invis(this);                   }
-spawnfunc(item_armor_padshield)        { spawnfunc_item_armor_mega(this);      }
+SPAWNFUNC_ITEM(item_armor_padshield, ITEM_ArmorMega)
 
-spawnfunc(holdable_floater)            { spawnfunc_item_jetpack(this);         }
+SPAWNFUNC_ITEM(holdable_floater, ITEM_Jetpack)
index 4c3b1d221ea4ae73be7f0ff7cc7b7ed5d7b9000f..7ddea9f5ea8f4bd52b60dbf3997003d0e3fc2b44 100644 (file)
@@ -1,6 +1,5 @@
 #pragma once
 
-float warmup_limit;
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
 
@@ -139,7 +138,8 @@ void checkSpectatorBlock(entity this);
 
 float game_completion_ratio; // 0 at start, 1 near end
 .float winning;
-.float jointime; // time of joining
+.float jointime; // time of connecting
+.float startplaytime; // time of switching from spectator to player
 .float alivetime; // time of being alive
 .float motd_actived_time; // used for both motd and campaign_message
 
@@ -376,6 +376,8 @@ const float ACTIVE_TOGGLE   = 3;
 
 .float stat_respawn_time = _STAT(RESPAWN_TIME); // shows respawn time, and is negative when awaiting respawn
 
+.int killindicator_teamchange;
+
 void PlayerUseKey(entity this);
 
 USING(spawn_evalfunc_t, vector(entity this, entity player, entity spot, vector current));
index 02704629612f32eaec198bca03857447a7ba337f..c0ff6b3f4ebd6c1a833f45c3b2f880ff06f1f442 100644 (file)
@@ -47,15 +47,15 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype)
                else
                {
                        // teamkill
-                       GameRules_scoring_add(attacker, KILLS, -1); // or maybe add a teamkills field?
+                       GameRules_scoring_add(attacker, TEAMKILLS, 1);
                }
        }
        else
        {
                // regular frag
                GameRules_scoring_add(attacker, KILLS, 1);
-               if(targ.playerid)
-                       PS_GR_P_ADDVAL(attacker, sprintf("kills-%d", targ.playerid), 1);
+               if(!warmup_stage && targ.playerid)
+                       PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
        }
 
        GameRules_scoring_add(targ, DEATHS, 1);
@@ -167,60 +167,42 @@ void Obituary_SpecialDeath(
        string s1, string s2, string s3,
        float f1, float f2, float f3)
 {
-       if(DEATH_ISSPECIAL(deathtype))
+       if(!DEATH_ISSPECIAL(deathtype))
        {
-               entity deathent = Deathtypes_from(deathtype - DT_FIRST);
-               if (!deathent) { backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n"); return; }
+               backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
+               return;
+       }
 
-               if(g_cts && deathtype == DEATH_KILL.m_id)
-                       return; // TODO: somehow put this in CTS gamemode file!
+       entity deathent = Deathtypes_from(deathtype - DT_FIRST);
+       if (!deathent)
+       {
+               backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
+               return;
+       }
 
-               if(murder)
-               {
-                       if(deathent.death_msgmurder)
-                       {
-                               Send_Notification_WOCOVA(
-                                       NOTIF_ONE,
-                                       notif_target,
-                                       MSG_MULTI,
-                                       deathent.death_msgmurder,
-                                       s1, s2, s3, "",
-                                       f1, f2, f3, 0
-                               );
-                               Send_Notification_WOCOVA(
-                                       NOTIF_ALL_EXCEPT,
-                                       notif_target,
-                                       MSG_INFO,
-                                       deathent.death_msgmurder.nent_msginfo,
-                                       s1, s2, s3, "",
-                                       f1, f2, f3, 0
-                               );
-                       }
-               }
-               else
-               {
-                       if(deathent.death_msgself)
-                       {
-                               Send_Notification_WOCOVA(
-                                       NOTIF_ONE,
-                                       notif_target,
-                                       MSG_MULTI,
-                                       deathent.death_msgself,
-                                       s1, s2, s3, "",
-                                       f1, f2, f3, 0
-                               );
-                               Send_Notification_WOCOVA(
-                                       NOTIF_ALL_EXCEPT,
-                                       notif_target,
-                                       MSG_INFO,
-                                       deathent.death_msgself.nent_msginfo,
-                                       s1, s2, s3, "",
-                                       f1, f2, f3, 0
-                               );
-                       }
-               }
+       if(g_cts && deathtype == DEATH_KILL.m_id)
+               return; // TODO: somehow put this in CTS gamemode file!
+
+       Notification death_message = (murder) ? deathent.death_msgmurder : deathent.death_msgself;
+       if(death_message)
+       {
+               Send_Notification_WOCOVA(
+                       NOTIF_ONE,
+                       notif_target,
+                       MSG_MULTI,
+                       death_message,
+                       s1, s2, s3, "",
+                       f1, f2, f3, 0
+               );
+               Send_Notification_WOCOVA(
+                       NOTIF_ALL_EXCEPT,
+                       notif_target,
+                       MSG_INFO,
+                       death_message.nent_msginfo,
+                       s1, s2, s3, "",
+                       f1, f2, f3, 0
+               );
        }
-       else { backtrace("Obituary_SpecialDeath called without a special deathtype?\n"); return; }
 }
 
 float Obituary_WeaponDeath(
@@ -231,44 +213,43 @@ float Obituary_WeaponDeath(
        float f1, float f2)
 {
        Weapon death_weapon = DEATH_WEAPONOF(deathtype);
-       if (death_weapon != WEP_Null)
-       {
-               w_deathtype = deathtype;
-               Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
-               w_deathtype = false;
+       if (death_weapon == WEP_Null)
+               return false;
 
-               if (death_message)
-               {
-                       Send_Notification_WOCOVA(
-                               NOTIF_ONE,
-                               notif_target,
-                               MSG_MULTI,
-                               death_message,
-                               s1, s2, s3, "",
-                               f1, f2, 0, 0
-                       );
-                       // send the info part to everyone
-                       Send_Notification_WOCOVA(
-                               NOTIF_ALL_EXCEPT,
-                               notif_target,
-                               MSG_INFO,
-                               death_message.nent_msginfo,
-                               s1, s2, s3, "",
-                               f1, f2, 0, 0
-                       );
-               }
-               else
-               {
-                       LOG_TRACEF(
-                               "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %d!\n",
-                               deathtype,
-                               death_weapon
-                       );
-               }
+       w_deathtype = deathtype;
+       Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
+       w_deathtype = false;
 
-               return true;
+       if (death_message)
+       {
+               Send_Notification_WOCOVA(
+                       NOTIF_ONE,
+                       notif_target,
+                       MSG_MULTI,
+                       death_message,
+                       s1, s2, s3, "",
+                       f1, f2, 0, 0
+               );
+               // send the info part to everyone
+               Send_Notification_WOCOVA(
+                       NOTIF_ALL_EXCEPT,
+                       notif_target,
+                       MSG_INFO,
+                       death_message.nent_msginfo,
+                       s1, s2, s3, "",
+                       f1, f2, 0, 0
+               );
        }
-       return false;
+       else
+       {
+               LOG_TRACEF(
+                       "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %d!\n",
+                       deathtype,
+                       death_weapon
+               );
+       }
+
+       return true;
 }
 
 bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target)
@@ -379,11 +360,16 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
 
                        attacker.killsound += 1;
 
+                       // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
+                       // these 2 macros are spread over multiple files
                        #define SPREE_ITEM(counta,countb,center,normal,gentle) \
                                case counta: \
                                { \
                                        Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
-                                       PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
+                                       if (!warmup_stage)\
+                                       {\
+                                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
+                                       }\
                                        break; \
                                }
                        switch(CS(attacker).killcount)
@@ -393,12 +379,12 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
                        }
                        #undef SPREE_ITEM
 
-                       if(!checkrules_firstblood)
+                       if(!warmup_stage && !checkrules_firstblood)
                        {
                                checkrules_firstblood = true;
                                notif_firstblood = true; // modify the current messages so that they too show firstblood information
-                               PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
-                               PS_GR_P_ADDVAL(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
+                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
+                               PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
 
                                // tell spree_inf and spree_cen that this is a first-blood and first-victim event
                                kill_count_to_attacker = -1;
@@ -513,7 +499,10 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
                if(GameRules_scoring_add(targ, SCORE, 0) == -5)
                {
                        Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
-                       PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
+                       if (!warmup_stage)
+                       {
+                               PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
+                       }
                }
        }
 
index 713577fad17beb0c5d46932b7d845cea4b18cd7e..d9372e0aa561ea44f4d57850db09a656ef18e070 100644 (file)
@@ -50,14 +50,7 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
                source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 
        if (lag)
-       {
-               // take players back into the past
-               FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag));
-               IL_EACH(g_monsters, it != forent,
-               {
-                       antilag_takeback(it, it, time - lag);
-               });
-       }
+               antilag_takeback_all(forent, lag);
 
        // do the trace
        if(wz)
@@ -67,13 +60,7 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
 
        // restore players to current positions
        if (lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it)));
-               IL_EACH(g_monsters, it != forent,
-               {
-                       antilag_restore(it, it);
-               });
-       }
+               antilag_restore_all(forent);
 
        // restore shooter solid type
        if(source)
index ce25dfa522772b08e3f1dfb41dcc2a536a2f4902..2eda8584ef5d82eeb9f346cb131aa9a049a58488 100644 (file)
@@ -2093,6 +2093,10 @@ void EndFrame()
        {
                antilag_record(it, it, altime);
        });
+       IL_EACH(g_projectiles, it.classname == "nade",
+       {
+               antilag_record(it, it, altime);
+       });
        systems_update();
        IL_ENDFRAME();
 }
diff --git a/qcsrc/server/items.qc b/qcsrc/server/items.qc
new file mode 100644 (file)
index 0000000..29a8609
--- /dev/null
@@ -0,0 +1,114 @@
+#include "items.qh"
+
+/// \file
+/// \brief Source file that contains implementation of the functions related to
+/// game items.
+/// \copyright GNU GPLv2 or any later version.
+
+#include "g_subs.qh"
+#include <common/weapons/all.qh>
+
+.bool m_isloot; ///< Holds whether item is loot.
+/// \brief Holds whether strength, shield or superweapon timers expire while
+/// this item is on the ground.
+.bool m_isexpiring;
+
+entity Item_Create(string class_name, vector position, bool no_align)
+{
+       entity item = spawn();
+       item.classname = class_name;
+       item.spawnfunc_checked = true;
+       setorigin(item, position);
+       item.noalign = no_align;
+       Item_Initialize(item, class_name);
+       if (wasfreed(item))
+       {
+               return NULL;
+       }
+       return item;
+}
+
+void Item_Initialize(entity item, string class_name)
+{
+       FOREACH(Weapons, it.m_canonical_spawnfunc == class_name,
+       {
+               weapon_defaultspawnfunc(item, it);
+               return;
+       });
+       FOREACH(Items, it.m_canonical_spawnfunc == class_name,
+       {
+               StartItem(item, it);
+               return;
+       });
+       LOG_FATALF("Item_Initialize: Invalid classname: %s", class_name);
+}
+
+entity Item_CreateLoot(string class_name, vector position, vector vel,
+       float time_to_live)
+{
+       entity item = spawn();
+       if (!Item_InitializeLoot(item, class_name, position, vel, time_to_live))
+       {
+               return NULL;
+       }
+       return item;
+}
+
+bool Item_InitializeLoot(entity item, string class_name, vector position,
+       vector vel, float time_to_live)
+{
+       item.classname = class_name;
+       Item_SetLoot(item, true);
+       item.noalign = true;
+       setorigin(item, position);
+       item.pickup_anyway = true;
+       item.spawnfunc_checked = true;
+       Item_Initialize(item, class_name);
+       if (wasfreed(item))
+       {
+               return false;
+       }
+       item.gravity = 1;
+       item.velocity = vel;
+       SUB_SetFade(item, time + time_to_live, 1);
+       return true;
+}
+
+bool Item_IsLoot(entity item)
+{
+       return item.m_isloot;
+}
+
+void Item_SetLoot(entity item, bool loot)
+{
+       item.m_isloot = loot;
+}
+
+bool Item_IsExpiring(entity item)
+{
+       return item.m_isexpiring;
+}
+
+void Item_SetExpiring(entity item, bool expiring)
+{
+       item.m_isexpiring = expiring;
+}
+
+// Compatibility spawn functions
+
+// FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
+SPAWNFUNC_ITEM(item_armor1, ITEM_ArmorSmall)
+
+SPAWNFUNC_ITEM(item_armor25, ITEM_ArmorMega)
+
+SPAWNFUNC_ITEM(item_armor_large, ITEM_ArmorMega)
+
+SPAWNFUNC_ITEM(item_health1, ITEM_HealthSmall)
+
+SPAWNFUNC_ITEM(item_health25, ITEM_HealthMedium)
+
+SPAWNFUNC_ITEM(item_health_large, ITEM_HealthBig)
+
+SPAWNFUNC_ITEM(item_health100, ITEM_HealthMega)
+
+SPAWNFUNC_ITEM(item_quad, ITEM_Strength)
diff --git a/qcsrc/server/items.qh b/qcsrc/server/items.qh
new file mode 100644 (file)
index 0000000..af55eeb
--- /dev/null
@@ -0,0 +1,65 @@
+#pragma once
+
+/// \file
+/// \brief Header file that describes the functions related to game items.
+/// \copyright GNU GPLv2 or any later version.
+
+/// \brief Creates a new item.
+/// \param[in] class_name Class name of the item.
+/// \param[in] position Position of the item.
+/// \param[in] no_align True if item should be placed directly at specified
+/// position, false to let it drop to the ground.
+/// \return Item on success, NULL otherwise.
+entity Item_Create(string class_name, vector position, bool no_align);
+
+/// \brief Initializes the item according to classname.
+/// \param[in,out] item Item to initialize.
+/// \param[in] class_name Class name to use.
+/// \return No return.
+/// \nore This function is useful if you want to set some item properties before
+/// initialization.
+void Item_Initialize(entity item, string class_name);
+
+/// \brief Creates a loot item.
+/// \param[in] class_name Class name of the item.
+/// \param[in] position Position of the item.
+/// \param[in] velocity of the item.
+/// \param[in] time_to_live Amount of time after which the item will disappear.
+/// \return Item on success, NULL otherwise.
+entity Item_CreateLoot(string class_name, vector position, vector vel,
+       float time_to_live);
+
+/// \brief Initializes the loot item.
+/// \param[in] class_name Class name of the item.
+/// \param[in] position Position of the item.
+/// \param[in] velocity of the item.
+/// \param[in] time_to_live Amount of time after which the item will disappear.
+/// \return True on success, false otherwise.
+/// \nore This function is useful if you want to set some item properties before
+/// initialization.
+bool Item_InitializeLoot(entity item, string class_name, vector position,
+       vector vel, float time_to_live);
+
+/// \brief Returns whether the item is loot.
+/// \param[in] item Item to check.
+/// \return True if the item is loot, false otherwise.
+bool Item_IsLoot(entity item);
+
+/// \brief Sets the item loot status.
+/// \param[in,out] item Item to adjust.
+/// \param[in] loot Whether item is loot.
+/// \return No return.
+void Item_SetLoot(entity item, bool loot);
+
+/// \brief Returns whether the item is expiring (i.e. its strength, shield and
+/// superweapon timers expire while it is on the ground).
+/// \param[in] item Item to check.
+/// \return True if the item is expiring, false otherwise.
+bool Item_IsExpiring(entity item);
+
+/// \brief Sets the item expiring status (i.e. whether its strength, shield
+/// and superweapon timers expire while it is on the ground).
+/// \param[in,out] item Item to adjust.
+/// \param[in] expiring Whether item is expiring.
+/// \return No return.
+void Item_SetExpiring(entity item, bool expiring);
index ab837c8945b873c0982d13834693c4a1a0f1fe9e..ef69905bdcfbdcbc561ad7e782acb06923b2574e 100644 (file)
@@ -8,6 +8,7 @@
 #include "mutators/_mod.qh"
 #include "../common/t_items.qh"
 #include "resources.qh"
+#include "items.qh"
 #include "weapons/accuracy.qh"
 #include "weapons/csqcprojectile.qh"
 #include "weapons/selection.qh"
@@ -653,7 +654,7 @@ void readplayerstartcvars()
                        "g_random_start_shells"));
                SetResourceAmount(random_start_ammo, RESOURCE_BULLETS, cvar(
                        "g_random_start_bullets"));
-               SetResourceAmount(random_start_ammo, RESOURCE_ROCKETS, 
+               SetResourceAmount(random_start_ammo, RESOURCE_ROCKETS,
                        cvar("g_random_start_rockets"));
                SetResourceAmount(random_start_ammo, RESOURCE_CELLS, cvar(
                        "g_random_start_cells"));
@@ -1437,10 +1438,13 @@ bool isPushable(entity e)
                return false;
        if(e.iscreature)
                return true;
+       if (Item_IsLoot(e))
+       {
+               return true;
+       }
        switch(e.classname)
        {
                case "body":
-               case "droppedweapon":
                        return true;
                case "bullet": // antilagged bullets can't hit this either
                        return false;
index e0c4198cc7adbea3354f350544950a5cf8ac7397..6e3448066e5d49c7d43aac261bb8cf0a6a2f5148 100644 (file)
@@ -645,6 +645,13 @@ enum {
        MUT_ITEMTOUCH_PICKUP // return this flag to have the item "picked up" and taken even after mutator handled it
 };
 
+/** called after the item has been touched. */
+#define EV_ItemTouched(i, o) \
+    /** item */    i(entity, MUTATOR_ARGV_0_entity) \
+    /** toucher */ i(entity, MUTATOR_ARGV_1_entity) \
+    /**/
+MUTATOR_HOOKABLE(ItemTouched, EV_ItemTouched);
+
 /** Called when the amount of entity resources changes. Can be used to override
 resource limit. */
 #define EV_GetResourceLimit(i, o) \
index 159df8a7818edbf5348a6d9ab184fb1227dbaefe..919df49013dd5f53cc1c5c2f819cf11c2ee38d41 100644 (file)
@@ -266,7 +266,11 @@ MUTATOR_HOOKFUNCTION(ca, PlayerDies)
 
        ca_LastPlayerForTeam_Notify(frag_target);
        if (!allowed_to_spawn)
-               frag_target.respawn_flags =  RESPAWN_SILENT;
+       {
+               frag_target.respawn_flags = RESPAWN_SILENT;
+               // prevent unwanted sudden rejoin as spectator and move of spectator camera
+               frag_target.respawn_time = time + 2;
+       }
        if (!warmup_stage)
                eliminatedPlayers.SendFlags |= 1;
        if(IS_BOT_CLIENT(frag_target))
@@ -289,7 +293,7 @@ MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver)
 
        if (!IS_DEAD(player))
                ca_LastPlayerForTeam_Notify(player);
-       if (CS(player).killindicator_teamchange == -2) // player wants to spectate
+       if (player.killindicator_teamchange == -2) // player wants to spectate
                player.caplayer = 0;
        if (player.caplayer)
                player.frags = FRAGS_LMS_LOSER;
index 20f4b383d11296c1c2265dd5e65e9550762ea954..ca892c52ff538c59f2205bf25d1090ed48ddf1bc 100644 (file)
@@ -1,7 +1,7 @@
 #include "gamemode_cts.qh"
-#include <server/race.qh>
 
 #include <server/race.qh>
+#include <server/items.qh>
 
 float autocvar_g_cts_finish_kill_delay;
 bool autocvar_g_cts_selfdamage;
@@ -314,8 +314,10 @@ MUTATOR_HOOKFUNCTION(cts, FilterItem)
 {
        entity item = M_ARGV(0, entity);
 
-       if(item.classname == "droppedweapon")
+       if (Item_IsLoot(item))
+       {
                return true;
+       }
 }
 
 MUTATOR_HOOKFUNCTION(cts, Damage_Calculate)
index 4d6f70438583b0f805b40070754e303b1a3f4726..cd8fb390d4726744ee117bbad21fffe5a2fcaa62 100644 (file)
@@ -1,6 +1,6 @@
 #include "gamemode_lms.qh"
 
-#include <common/mutators/mutator/instagib/items.qc>
+#include <common/mutators/mutator/instagib/items.qh>
 #include <server/campaign.qh>
 #include <server/command/_mod.qh>
 
index d9226f3437e351bec049ea26b5747b333f2530ad..26fc9e659204d7bf6305a5d2483bf1b2b4ba9819 100644 (file)
@@ -517,7 +517,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
                if(this.alivetime)
                {
-                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+                       PlayerStats_GameReport_Event_Player(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
                        this.alivetime = 0;
                }
 
@@ -538,7 +538,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                {
                        delete(this.killindicator);
                        this.killindicator = NULL;
-                       if(CS(this).killindicator_teamchange)
+                       if(this.killindicator_teamchange)
                                defer_ClientKill_Now_TeamChange = true;
 
                        if(this.classname == "body")
@@ -558,6 +558,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                if(w != WEP_Null && accuracy_isgooddamage(attacker, this))
                        CS(attacker).accuracy.(accuracy_frags[w.m_id-1]) += 1;
 
+               this.respawn_time = 0;
                MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
                damage = M_ARGV(4, float);
                excess = max(0, damage - take - save);
@@ -586,6 +587,9 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body"))
                        return;
 
+               if (!this.respawn_time) // can be set in the mutator hook PlayerDies
+                       calculate_player_respawn_time(this);
+
                // when we get here, player actually dies
 
                Unfreeze(this); // remove any icy remains
@@ -621,9 +625,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
                STAT(MOVEVARS_SPECIALCOMMAND, this) = false; // sweet release
 
-               // when to allow respawn
-               calculate_player_respawn_time(this);
-
                this.death_time = time;
                if (random() < 0.5)
                        animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true);
index b25a65f1a7c127f348e897c392d4db2f00fb6734..c9948660efe1165c7ed8654dac1ba770e6763277 100644 (file)
@@ -96,7 +96,7 @@ void TeamScore_Spawn(float t, string name)
        PlayerStats_GameReport_AddTeam(t);
 }
 
-float TeamScore_AddToTeam(float t, float scorefield, float score)
+float TeamScore_AddToTeam(int t, float scorefield, float score)
 {
        entity s;
 
@@ -347,7 +347,7 @@ float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
        if(scores_label(scorefield) != "")
                s.SendFlags |= (2 ** (scorefield.m_id % 16));
        if(!warmup_stage)
-               PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
+               PlayerStats_GameReport_Event_Player(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
        s.(scores(scorefield)) += score;
        MUTATOR_CALLHOOK(AddedPlayerScore, scorefield, score, player);
        return s.(scores(scorefield));
@@ -908,10 +908,10 @@ void PlayerScore_PlayerStats(entity p)
 {
        entity s = CS(p).scorekeeper;
        FOREACH(Scores, true, {
-               if(s.(scores(it)) != 0)
-                       if(scores_label(it) != "")
-                               PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
-    });
+               if(s.(scores(it)) != 0 && scores_label(it) != "")
+                       PlayerStats_GameReport_Event_Player(s.owner,
+                               strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
+       });
 }
 
 void PlayerScore_TeamStats()
@@ -924,9 +924,9 @@ void PlayerScore_TeamStats()
                if(!sk)
                        continue;
                for(i = 0; i < MAX_TEAMSCORE; ++i)
-                       if(sk.(teamscores(i)) != 0)
-                               if(teamscores_label(i) != "")
-                                       // the +1 is important here!
-                                       PS_GR_T_ADDVAL(t+1, strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
+          &nbs