]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'martin-t/okc3' into martin-t/master
authorMartin Taibr <taibr.martin@gmail.com>
Sun, 3 Sep 2017 19:14:34 +0000 (21:14 +0200)
committerMartin Taibr <taibr.martin@gmail.com>
Sun, 3 Sep 2017 19:14:34 +0000 (21:14 +0200)
54 files changed:
.gitlab-ci.yml
CMakeLists.txt
defaultOverkill.cfg
mutators.cfg
qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc
qcsrc/common/mutators/mutator/hook/sv_hook.qc
qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc
qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc
qcsrc/common/mutators/mutator/midair/sv_midair.qc
qcsrc/common/mutators/mutator/multijump/multijump.qc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc
qcsrc/common/mutators/mutator/nix/sv_nix.qc
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc
qcsrc/common/mutators/mutator/pinata/sv_pinata.qc
qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc
qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc
qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
qcsrc/common/mutators/mutator/superspec/sv_superspec.qc
qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc
qcsrc/common/mutators/mutator/vampire/sv_vampire.qc
qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc
qcsrc/common/mutators/mutator/walljump/walljump.qc
qcsrc/common/physics/movetypes/movetypes.qh
qcsrc/common/physics/player.qh
qcsrc/common/t_items.qc
qcsrc/common/t_items.qh
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/projectiles.qh
qcsrc/dpdefs/post.qh
qcsrc/lib/_all.inc
qcsrc/lib/macro.qh
qcsrc/lib/misc.qh
qcsrc/lib/self.qh
qcsrc/lib/spawnfunc.qh
qcsrc/lib/stats.qh
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/autocvars.qh
qcsrc/server/client.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/mutators/events.qh
qcsrc/server/resources.qc [new file with mode: 0644]
qcsrc/server/resources.qh [new file with mode: 0644]
qcsrc/server/sv_main.qc
qcsrc/server/sv_main.qh
qcsrc/server/teamplay.qc
qcsrc/tools/compilationunits.sh

index 9305f527cd3abc9640d17bff880e021b854d7b17..596f61ad1dff4f98684a79992af9718aa69d7ebd 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=0a08daa9132d147f533776deda07643e
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index 2e5fc791f8585428c314a2ae9293bb011d371e3a..9a66f9fd2ef4985cf06e873e7435c6735b8d1f91 100644 (file)
@@ -8,9 +8,18 @@ add_custom_target(${all})
 set(checks qc-checks)
 add_custom_target(${checks})
 
-# depend on qcc
-if (TARGET gmqcc)
-    add_dependencies(${checks} gmqcc)
+if (gmqcc_BINARY_DIR)
+    set(compilerinfo "${gmqcc_BINARY_DIR}/gmqcc.h")
+    add_custom_command(
+            OUTPUT "${compilerinfo}"
+            DEPENDS "${gmqcc_BINARY_DIR}/gmqcc"
+            VERBATIM
+            COMMAND ${CMAKE_COMMAND} -E
+                md5sum "${gmqcc_BINARY_DIR}/gmqcc" > "${compilerinfo}"
+            )
+    add_custom_target(qcc ALL
+            DEPENDS "${compilerinfo}"
+            )
 endif ()
 
 add_dependencies(${checks} data-check-cvars)
@@ -45,6 +54,25 @@ add_custom_target(qc-whitespace
         VERBATIM COMMAND ./tools/whitespace.sh
         )
 
+function(prog name dir)
+    add_executable(${name} qcsrc/${dir}/progs.inc)
+    add_dependencies(${all} ${name})
+    add_dependencies(${name} ${checks})
+    add_dependencies(${name} qcc)
+    set_source_files_properties(qcsrc/${dir}/progs.inc PROPERTIES OBJECT_DEPENDS "${compilerinfo}")
+endfunction()
+
+function(set_prelude target prelude)
+    get_target_property(MY_PROJECT_SOURCES ${target} SOURCES)
+    foreach (source IN LISTS MY_PROJECT_SOURCES)
+        set_property(
+                SOURCE ${source}
+                APPEND PROPERTY COMPILE_FLAGS
+                "-include ${prelude}"
+        )
+    endforeach ()
+endfunction()
+
 include_directories(qcsrc)
 
 add_definitions(-DXONOTIC=1)
@@ -75,33 +103,16 @@ set_source_files_properties(
         HEADER_FILE_ONLY FALSE
 )
 
-add_executable(csprogs qcsrc/client/progs.inc)
-add_dependencies(${all} csprogs)
-add_dependencies(csprogs ${checks})
+prog(csprogs client)
 target_compile_definitions(csprogs PRIVATE -DGAMEQC -DCSQC)
+# set_prelude(csprogs "${PROJECT_SOURCE_DIR}/qcsrc/lib/_all.inc")
 
-add_executable(progs qcsrc/server/progs.inc)
-add_dependencies(${all} progs)
-add_dependencies(progs ${checks})
+prog(progs server)
 target_compile_definitions(progs PRIVATE -DGAMEQC -DSVQC)
 
-add_executable(menu qcsrc/menu/progs.inc)
-add_dependencies(${all} menu)
-add_dependencies(menu ${checks})
+prog(menu menu)
 target_compile_definitions(menu PRIVATE -DMENUQC)
 
-function(set_prelude target prelude)
-    get_target_property(MY_PROJECT_SOURCES target SOURCES)
-    foreach (source IN LISTS MY_PROJECT_SOURCES)
-        set_property(
-                SOURCE ${source}
-                APPEND PROPERTY COMPILE_FLAGS
-                "-include ${PROJECT_SOURCE_DIR}/${prelude}"
-        )
-    endforeach ()
-endfunction()
-# set_prelude(csprogs qcsrc/lib/_all.inc)
-
 function(copy prog)
     add_custom_command(TARGET ${prog} POST_BUILD
             COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE_DIR:${prog}>/${prog}.dat" "${prog}.dat"
index 2444301b9e7b34c75b8bacf3ebdd367615ec4939..f63f689f34bda0b1cd03bcb62a1f944cbac88a92 100644 (file)
@@ -28,7 +28,7 @@ set g_nades_nade_newton_style 2
 set g_dodging 1
 set sv_dodging_wall_dodging 1
 
-set g_spawn_near_teammate 1
+set g_spawn_near_teammate "!g_assault !g_freezetag"
 set g_spawn_near_teammate_ignore_spawnpoint 1
 set g_spawnshieldtime 0.5
 set g_respawn_delay_forced 2
index a1bf7716c783e350883f3a9b9af77f4f599ef9ed..583f3d5975672689f7aeb40348db5043b86b0fcb 100644 (file)
@@ -112,7 +112,7 @@ set g_rocket_flying 0 "set to 1 to enable rocket flying in all balance configs"
 //  spawn near teammate
 // =====================
 seta cl_spawn_near_teammate 1 "toggle for spawning near teammates (only effective if g_spawn_near_teammate_ignore_spawnpoint is 2)"
-set g_spawn_near_teammate 0 "if set, players prefer spawns near a team mate"
+set g_spawn_near_teammate 0 "players prefer spawns near a team mate"
 set g_spawn_near_teammate_distance 640 "max distance to consider a spawn to be near a team mate"
 set g_spawn_near_teammate_ignore_spawnpoint 0 "ignore spawnpoints and spawn right at team mates, if 2, clients can ignore this option"
 set g_spawn_near_teammate_ignore_spawnpoint_max 10 "if set, test at most this many of the available teammates"
index 61b0c06016923ad311c1d03e15a9adaf2852fe42..1164e0ade66efd9dcf05c924fa76ff109dcd0e65 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_bloodloss.qh"
 
-REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss"));
+float autocvar_g_bloodloss;
+REGISTER_MUTATOR(bloodloss, autocvar_g_bloodloss);
 
 .float bloodloss_timer;
 
@@ -9,7 +10,7 @@ MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink)
        entity player = M_ARGV(0, entity);
 
        if(IS_PLAYER(player))
-       if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player))
+       if(GetResourceAmount(player, RESOURCE_HEALTH) <= autocvar_g_bloodloss && !IS_DEAD(player))
        {
                PHYS_INPUT_BUTTON_CROUCH(player) = true;
 
@@ -28,7 +29,7 @@ MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump)
 {
        entity player = M_ARGV(0, entity);
 
-       if(player.health <= autocvar_g_bloodloss)
+       if(GetResourceAmount(player, RESOURCE_HEALTH) <= autocvar_g_bloodloss)
                return true;
 }
 
index 925525f395a80e672113ff19e6f5768e81ac1d17..e039a96b9501a9e99900884377b625fcab16861c 100644 (file)
@@ -424,12 +424,17 @@ void buff_Medic_Heal(entity this)
 {
        FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
        {
-               if(SAME_TEAM(it, this))
-               if(it.health < autocvar_g_balance_health_regenstable)
+               if (!SAME_TEAM(it, this))
                {
-                       Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
-                       it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable);
+                       continue;
                }
+               float hp = GetResourceAmount(it, RESOURCE_HEALTH);
+               if(hp >= autocvar_g_balance_health_regenstable)
+               {
+                       continue;
+               }
+               Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
+               SetResourceAmount(it, RESOURCE_HEALTH, bound(0, hp + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable));
        });
 }
 
@@ -460,11 +465,11 @@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
                frag_damage *= autocvar_g_buffs_speed_damage_take;
 
        if(frag_target.buffs & BUFF_MEDIC.m_itemid)
-       if((frag_target.health - frag_damage) <= 0)
+       if((GetResourceAmount(frag_target, RESOURCE_HEALTH) - frag_damage) <= 0)
        if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
        if(frag_attacker)
        if(random() <= autocvar_g_buffs_medic_survive_chance)
-               frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health);
+               frag_damage = max(5, GetResourceAmount(frag_target, RESOURCE_HEALTH) - autocvar_g_buffs_medic_survive_health);
 
        if(frag_target.buffs & BUFF_JUMP.m_itemid)
        if(frag_deathtype == DEATH_FALL.m_id)
@@ -537,9 +542,15 @@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
        if(frag_target.takedamage)
        if(DIFF_TEAM(frag_attacker, frag_target))
        {
-               frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
-               if(frag_target.armorvalue)
-                       frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max);
+               float amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal,
+                       GetResourceAmount(frag_target, RESOURCE_HEALTH));
+               GiveResourceWithLimit(frag_attacker, RESOURCE_HEALTH, amount, g_pickup_healthsmall_max);
+               if (frag_target.armorvalue)
+               {
+                       amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal,
+                               GetResourceAmount(frag_target, RESOURCE_ARMOR));
+                       GiveResourceWithLimit(frag_attacker, RESOURCE_ARMOR, amount, g_pickup_armorsmall_max);
+               }
        }
 
        M_ARGV(4, float) = frag_damage;
index 52fc52466d5f93ec8203f3b104ac2b72e295dba1..987645aaa0c719a5546ceeb03d47ee9b2901c804 100644 (file)
@@ -1,10 +1,11 @@
 #include "sv_campcheck.qh"
 
+string autocvar_g_campcheck;
 float autocvar_g_campcheck_damage;
 float autocvar_g_campcheck_distance;
 float autocvar_g_campcheck_interval;
 
-REGISTER_MUTATOR(campcheck, cvar("g_campcheck"));
+REGISTER_MUTATOR(campcheck, expr_evaluate(autocvar_g_campcheck));
 
 .float campcheck_nextcheck;
 .float campcheck_traveled_distance;
@@ -64,7 +65,7 @@ MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
                                if(player.vehicle)
                                        Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0');
                                else
-                                       Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0');
+                                       Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, GetResourceAmount(player, RESOURCE_HEALTH) + GetResourceAmount(player, RESOURCE_ARMOR) * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0');
                        }
                        player.campcheck_nextcheck = time + autocvar_g_campcheck_interval;
                        player.campcheck_traveled_distance = 0;
index eb45d4baf40a5950f81bdb466c0305cf616da70e..a1fe27a877c3e46e5e4dfe59a15239c583930f0a 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_cloaked.qh"
 
-REGISTER_MUTATOR(cloaked, cvar("g_cloaked"));
+string autocvar_g_cloaked;
+REGISTER_MUTATOR(cloaked, expr_evaluate(autocvar_g_cloaked));
 
 float autocvar_g_balance_cloaked_alpha;
 
index 5dfdf4386642933e5d98404e5be6914d785c2f0c..c3967811973a9ec009bbe17d120a0be38f86387d 100644 (file)
@@ -5,7 +5,7 @@
 #ifdef SVQC
 AUTOCVAR(g_grappling_hook_useammo, bool, false, "Use ammunition with the off-hand grappling hook");
 
-REGISTER_MUTATOR(hook, cvar("g_grappling_hook")) {
+REGISTER_MUTATOR(hook, expr_evaluate(cvar_string("g_grappling_hook"))) {
     MUTATOR_ONADD {
         g_grappling_hook = true;
         if(!autocvar_g_grappling_hook_useammo)
index fefad540b24c6b18cb76a1f1d22196023458ce9e..1561dc10db9a326acf932de70df86365e179bd4f 100644 (file)
@@ -1,5 +1,10 @@
 #include "sv_instagib.qh"
 
+bool autocvar_g_instagib_damagedbycontents = true;
+bool autocvar_g_instagib_blaster_keepdamage = false;
+bool autocvar_g_instagib_blaster_keepforce = false;
+bool autocvar_g_instagib_mirrordamage;
+bool autocvar_g_instagib_friendlypush = true;
 //int autocvar_g_instagib_ammo_drop;
 bool autocvar_g_instagib_ammo_convert_cells;
 bool autocvar_g_instagib_ammo_convert_rockets;
@@ -12,7 +17,7 @@ float autocvar_g_instagib_speed_highspeed;
 
 #include <common/items/_mod.qh>
 
-REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball);
+REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball);
 
 spawnfunc(item_minst_cells)
 {
@@ -55,7 +60,7 @@ void instagib_ammocheck(entity this)
 
        if(IS_DEAD(this) || game_stopped)
                instagib_stop_countdown(this);
-       else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE))
+       else if (GetResourceAmount(this, RESOURCE_CELLS) > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE))
                instagib_stop_countdown(this);
        else if(autocvar_g_rm && autocvar_g_rm_laser)
        {
@@ -67,53 +72,54 @@ void instagib_ammocheck(entity this)
        }
        else
        {
+               float hp = GetResourceAmount(this, RESOURCE_HEALTH);
                this.instagib_needammo = true;
-               if (this.health <= 5)
+               if (hp <= 5)
                {
                        Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED);
                }
-               else if (this.health <= 10)
+               else if (hp <= 10)
                {
                        Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1);
                }
-               else if (this.health <= 20)
+               else if (hp <= 20)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2);
                }
-               else if (this.health <= 30)
+               else if (hp <= 30)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3);
                }
-               else if (this.health <= 40)
+               else if (hp <= 40)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4);
                }
-               else if (this.health <= 50)
+               else if (hp <= 50)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5);
                }
-               else if (this.health <= 60)
+               else if (hp <= 60)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6);
                }
-               else if (this.health <= 70)
+               else if (hp <= 70)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7);
                }
-               else if (this.health <= 80)
+               else if (hp <= 80)
                {
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
                        Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8);
                }
-               else if (this.health <= 90)
+               else if (hp <= 90)
                {
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
                        Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
@@ -289,13 +295,15 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate)
                        if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker))
                                frag_force = '0 0 0';
 
-                       if(frag_target.armorvalue)
+                       float armor = GetResourceAmount(frag_target, RESOURCE_ARMOR);
+                       if(armor)
                        {
-                               frag_target.armorvalue -= 1;
+                               armor -= 1;
+                               SetResourceAmount(frag_target, RESOURCE_ARMOR, armor);
                                frag_damage = 0;
                                frag_target.damage_dealt += 1;
                                frag_attacker.damage_dealt += 1;
-                               Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
+                               Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, armor);
                        }
                }
 
@@ -312,7 +320,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate)
 
                                if(frag_target != frag_attacker)
                                {
-                                       if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); }
+                                       if(frag_damage <= 0 && GetResourceAmount(frag_target, RESOURCE_HEALTH) > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); }
                                        if(!autocvar_g_instagib_blaster_keepforce)
                                                frag_force = '0 0 0';
                                }
@@ -325,10 +333,12 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate)
        if(frag_mirrordamage > 0)
        {
                // just lose extra LIVES, don't kill the player for mirror damage
-               if(frag_attacker.armorvalue > 0)
+               float armor = GetResourceAmount(frag_attacker, RESOURCE_ARMOR);
+               if(armor > 0)
                {
-                       frag_attacker.armorvalue -= 1;
-                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue);
+                       armor -= 1;
+                       SetResourceAmount(frag_attacker, RESOURCE_ARMOR, armor);
+                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, armor);
                        frag_attacker.damage_dealt += frag_mirrordamage;
                }
                frag_mirrordamage = 0;
@@ -415,7 +425,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
 
        if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon")
        {
-               item.ammo_cells = autocvar_g_instagib_ammo_drop;
+               SetResourceAmount(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop);
                return false;
        }
 
@@ -428,10 +438,11 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
        if(item.flags & FL_POWERUP)
                return false;
 
-       if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
-               item.ammo_cells = autocvar_g_instagib_ammo_drop;
+       float cells = GetResourceAmount(item, RESOURCE_CELLS);
+       if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
+               SetResourceAmount(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop);
 
-       if(item.ammo_cells && !item.weapon)
+       if(cells && !item.weapon)
                return false;
 
        return true;
@@ -464,26 +475,27 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch)
        entity item = M_ARGV(0, entity);
        entity toucher = M_ARGV(1, entity);
 
-       if(item.ammo_cells)
+       if(GetResourceAmount(item, RESOURCE_CELLS))
        {
                // play some cool sounds ;)
+               float hp = GetResourceAmount(toucher, RESOURCE_HEALTH);
                if (IS_CLIENT(toucher))
                {
-                       if(toucher.health <= 5)
+                       if(hp <= 5)
                                Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
-                       else if(toucher.health < 50)
+                       else if(hp < 50)
                                Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
                }
 
-               if(toucher.health < 100)
-                       toucher.health = 100;
+               if(hp < 100)
+                       SetResourceAmount(toucher, RESOURCE_HEALTH, 100);
 
                return MUT_ITEMTOUCH_CONTINUE;
        }
 
        if(item.itemdef == ITEM_ExtraLife)
        {
-               toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives);
+               GiveResource(toucher, RESOURCE_ARMOR, autocvar_g_instagib_extralives);
                Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES);
                return MUT_ITEMTOUCH_PICKUP;
        }
index 23e0d0d85033ac05004679c72606a9f005eb9f8c..e68c687bdeb65058bac0c887c5f8aab8967bd560 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_invincibleproj.qh"
 
-REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles"));
+string autocvar_g_invincible_projectiles;
+REGISTER_MUTATOR(invincibleprojectiles, expr_evaluate(autocvar_g_invincible_projectiles));
 
 MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile)
 {
index a542921221a6a37bc7bddf4bb485f26fdcbd8d08..ac06a8f7747160259588667e74a20ba33878da9e 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_melee_only.qh"
 
-REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !cvar("g_overkill") && !g_nexball);
+string autocvar_g_melee_only;
+REGISTER_MUTATOR(melee_only, expr_evaluate(autocvar_g_melee_only) && !cvar("g_instagib") && !cvar("g_overkill") && !g_nexball);
 
 MUTATOR_HOOKFUNCTION(melee_only, SetStartItems, CBC_ORDER_LAST)
 {
index 92bbacc00663c2eb70d50dda92b913874eb2493d..54b3673c6e245331c69b72dc69a0bffaa3f5ad13 100644 (file)
@@ -1,8 +1,9 @@
 #include "sv_midair.qh"
 
+string autocvar_g_midair;
 float autocvar_g_midair_shieldtime;
 
-REGISTER_MUTATOR(midair, cvar("g_midair"));
+REGISTER_MUTATOR(midair, expr_evaluate(autocvar_g_midair));
 
 .float midair_shieldtime;
 
index 47dcfd4afd550230ab85fbff4872d03cdc6e13bd..081a1fdb6fd69d7767de22b0ebecd8c8445e93b4 100644 (file)
@@ -9,7 +9,7 @@
 
 
 #if defined(SVQC)
-REGISTER_MUTATOR(multijump, cvar("g_multijump"));
+REGISTER_MUTATOR(multijump, autocvar_g_multijump);
 #elif defined(CSQC)
 REGISTER_MUTATOR(multijump, true);
 #endif
index 18edd482d3c5b942092032ae2bf1371be5d2a2cf..92e16b48d22e04bc65a49f33b12dbdacbf17eee3 100644 (file)
@@ -81,7 +81,7 @@ MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile)
 
        entity nade_type = Nade_FromProjectile(proj.cnt);
        if (nade_type == NADE_TYPE_Null) return;
-       if(STAT(NADES_SMALL, NULL))
+       if(STAT(NADES_SMALL))
        {
                proj.mins = '-8 -8 -8';
                proj.maxs = '8 8 8';
@@ -150,7 +150,7 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan
 #include <common/monsters/sv_monsters.qh>
 #include <server/g_subs.qh>
 
-REGISTER_MUTATOR(nades, cvar("g_nades"));
+REGISTER_MUTATOR(nades, autocvar_g_nades);
 
 .float nade_time_primed;
 .float nade_lifetime;
@@ -431,7 +431,7 @@ void nade_ice_think(entity this)
 
        float current_freeze_time = this.ltime - time - 0.1;
 
-       FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && current_freeze_time > 0,
+       FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage && !IS_DEAD(it) && GetResourceAmount(it, RESOURCE_HEALTH) > 0 && current_freeze_time > 0,
        {
                if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(it, this.realowner) || it == this.realowner))
                if(!it.revival_time || ((time - it.revival_time) >= 1.5))
@@ -623,13 +623,15 @@ void nade_heal_touch(entity this, entity toucher)
                if ( health_factor > 0 )
                {
                        maxhealth = (IS_MONSTER(toucher)) ? toucher.max_health : g_pickup_healthmega_max;
-                       if ( toucher.health < maxhealth )
+                       float hp = GetResourceAmount(toucher, RESOURCE_HEALTH);
+                       if (hp < maxhealth)
                        {
-                               if ( this.nade_show_particles )
+                               if (this.nade_show_particles)
+                               {
                                        Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1);
-                               toucher.health = min(toucher.health+health_factor, maxhealth);
+                               }
+                               GiveResourceWithLimit(toucher, RESOURCE_HEALTH, health_factor, maxhealth);
                        }
-                       toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
                }
                else if ( health_factor < 0 )
                {
@@ -768,7 +770,7 @@ void nade_touch(entity this, entity toucher)
 
        if(autocvar_g_nades_pickup)
        if(time >= this.spawnshieldtime)
-       if(!toucher.nade && this.health == this.max_health) // no boosted shot pickups, thank you very much
+       if(!toucher.nade && GetResourceAmount(this, RESOURCE_HEALTH) == this.max_health) // no boosted shot pickups, thank you very much
        if(CanThrowNade(toucher)) // prevent some obvious things, like dead players
        if(IS_REAL_CLIENT(toucher)) // above checks for IS_PLAYER, don't need to do it here
        {
@@ -796,7 +798,7 @@ void nade_touch(entity this, entity toucher)
 
        //setsize(this, '-2 -2 -2', '2 2 2');
        //UpdateCSQCProjectile(this);
-       if(this.health == this.max_health)
+       if(GetResourceAmount(this, RESOURCE_HEALTH) == this.max_health)
        {
                spamsound(this, CH_SHOTS, SND_GRENADE_BOUNCE_RANDOM(), VOL_BASE, ATTEN_NORM);
                return;
@@ -860,19 +862,22 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i
        if(damage <= 0 || ((IS_ONGROUND(this)) && IS_PLAYER(attacker)))
                return;
 
-       if(this.health == this.max_health)
+       float hp = GetResourceAmount(this, RESOURCE_HEALTH);
+       if(hp == this.max_health)
        {
                sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
                this.nextthink = max(time + this.nade_lifetime, time);
                setthink(this, nade_beep);
        }
 
-       this.health -= damage;
+       hp -= damage;
+       SetResourceAmount(this, RESOURCE_HEALTH, hp);
+
 
        if ( this.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) )
                this.realowner = attacker;
 
-       if(this.health <= 0)
+       if(hp <= 0)
                W_PrepareExplosionByDamage(this, attacker, nade_boom);
        else
                nade_burn_spawn(this);
@@ -928,7 +933,7 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time)
 
        settouch(_nade, nade_touch);
        _nade.spawnshieldtime = time + 0.1; // prevent instantly picking up again
-       _nade.health = autocvar_g_nades_nade_health;
+       SetResourceAmount(_nade, RESOURCE_HEALTH, autocvar_g_nades_nade_health);
        _nade.max_health = _nade.health;
        _nade.takedamage = DAMAGE_AIM;
        _nade.event_damage = nade_damage;
@@ -1294,7 +1299,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
        if(n && STAT(FROZEN, player) == 3) // OK, there is at least one teammate reviving us
        {
                player.revive_progress = bound(0, player.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
-               player.health = max(1, player.revive_progress * start_health);
+               SetResourceAmount(player, RESOURCE_HEALTH, max(1, player.revive_progress * start_health));
 
                if(player.revive_progress >= 1)
                {
@@ -1411,7 +1416,7 @@ MUTATOR_HOOKFUNCTION(nades, Damage_Calculate)
        if(time - frag_inflictor.toss_time <= 0.1)
        {
                Unfreeze(frag_target);
-               frag_target.health = autocvar_g_freezetag_revive_nade_health;
+               SetResourceAmount(frag_target, RESOURCE_HEALTH, autocvar_g_freezetag_revive_nade_health);
                Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3);
                M_ARGV(4, float) = 0;
                M_ARGV(6, vector) = '0 0 0';
index 288c2d5c8363ea473d824edf867275700221b677..af364995a1e92bc13c42b287babb5e3471938441 100644 (file)
@@ -68,9 +68,11 @@ roflsound "New toys, new toys!" sound.
 
 */
 
+string autocvar_g_new_toys;
+
 bool nt_IsNewToy(int w);
 
-REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill"))
+REGISTER_MUTATOR(nt, expr_evaluate(autocvar_g_new_toys) && !cvar("g_instagib") && !cvar("g_overkill"))
 {
        MUTATOR_ONADD
        {
index 425b8c22b0e66f12e20115acbdc5a4cd6bdd0330..4de24a5898c8b214afb2351ec70b786ee9848434 100644 (file)
@@ -1,5 +1,6 @@
 #include "sv_nix.qh"
 
+string autocvar_g_nix;
 int autocvar_g_balance_nix_ammo_cells;
 int autocvar_g_balance_nix_ammo_plasma;
 int autocvar_g_balance_nix_ammo_fuel;
@@ -35,7 +36,7 @@ float nix_nextweapon;
 
 bool NIX_CanChooseWeapon(int wpn);
 
-REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"))
+REGISTER_MUTATOR(nix, expr_evaluate(autocvar_g_nix) && !cvar("g_instagib") && !cvar("g_overkill"))
 {
        MUTATOR_ONADD
        {
@@ -56,12 +57,12 @@ REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"
        {
                // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
                FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
-                       it.ammo_cells = start_ammo_cells;
-                       it.ammo_plasma = start_ammo_plasma;
-                       it.ammo_shells = start_ammo_shells;
-                       it.ammo_nails = start_ammo_nails;
-                       it.ammo_rockets = start_ammo_rockets;
-                       it.ammo_fuel = start_ammo_fuel;
+                       SetResourceAmount(it, RESOURCE_SHELLS, start_ammo_shells);
+                       SetResourceAmount(it, RESOURCE_BULLETS, start_ammo_nails);
+                       SetResourceAmount(it, RESOURCE_ROCKETS, start_ammo_rockets);
+                       SetResourceAmount(it, RESOURCE_CELLS, start_ammo_cells);
+                       SetResourceAmount(it, RESOURCE_PLASMA, start_ammo_plasma);
+                       SetResourceAmount(it, RESOURCE_FUEL, start_ammo_fuel);
                        it.weapons = start_weapons;
                        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                        {
@@ -133,30 +134,34 @@ void NIX_GiveCurrentWeapon(entity this)
 
        if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round!
        {
-               this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0;
-
+               SetResourceAmount(this, RESOURCE_SHELLS, 0);
+               SetResourceAmount(this, RESOURCE_BULLETS, 0);
+               SetResourceAmount(this, RESOURCE_ROCKETS, 0);
+               SetResourceAmount(this, RESOURCE_CELLS, 0);
+               SetResourceAmount(this, RESOURCE_PLASMA, 0);
+               SetResourceAmount(this, RESOURCE_FUEL, 0);
                if(this.items & IT_UNLIMITED_WEAPON_AMMO)
                {
                        switch(e.ammo_field)
                        {
-                               case ammo_shells:  this.ammo_shells  = autocvar_g_pickup_shells_max;  break;
-                               case ammo_nails:   this.ammo_nails   = autocvar_g_pickup_nails_max;   break;
-                               case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break;
-                               case ammo_cells:   this.ammo_cells   = autocvar_g_pickup_cells_max;   break;
-                               case ammo_plasma:  this.ammo_plasma  = autocvar_g_pickup_plasma_max;   break;
-                               case ammo_fuel:    this.ammo_fuel    = autocvar_g_pickup_fuel_max;    break;
+                               case ammo_shells:  SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_pickup_shells_max);  break;
+                               case ammo_nails:   SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_pickup_nails_max);   break;
+                               case ammo_rockets: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_pickup_rockets_max); break;
+                               case ammo_cells:   SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_pickup_cells_max);   break;
+                               case ammo_plasma:  SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_pickup_plasma_max);   break;
+                               case ammo_fuel:    SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_pickup_fuel_max);    break;
                        }
                }
                else
                {
                        switch(e.ammo_field)
                        {
-                               case ammo_shells:  this.ammo_shells  = autocvar_g_balance_nix_ammo_shells;  break;
-                               case ammo_nails:   this.ammo_nails   = autocvar_g_balance_nix_ammo_nails;   break;
-                               case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break;
-                               case ammo_cells:   this.ammo_cells   = autocvar_g_balance_nix_ammo_cells;   break;
-                               case ammo_plasma:  this.ammo_plasma  = autocvar_g_balance_nix_ammo_plasma;   break;
-                               case ammo_fuel:    this.ammo_fuel    = autocvar_g_balance_nix_ammo_fuel;    break;
+                               case ammo_shells:  SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammo_shells);  break;
+                               case ammo_nails:   SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammo_nails);   break;
+                               case ammo_rockets: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammo_rockets); break;
+                               case ammo_cells:   SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammo_cells);   break;
+                               case ammo_plasma:  SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammo_plasma);   break;
+                               case ammo_fuel:    SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammo_fuel);    break;
                        }
                }
 
@@ -204,12 +209,12 @@ void NIX_GiveCurrentWeapon(entity this)
        {
                switch(e.ammo_field)
                {
-                       case ammo_shells:  this.ammo_shells  += autocvar_g_balance_nix_ammoincr_shells;  break;
-                       case ammo_nails:   this.ammo_nails   += autocvar_g_balance_nix_ammoincr_nails;   break;
-                       case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break;
-                       case ammo_cells:   this.ammo_cells   += autocvar_g_balance_nix_ammoincr_cells;   break;
-                       case ammo_plasma:  this.ammo_plasma  += autocvar_g_balance_nix_ammoincr_plasma;   break;
-                       case ammo_fuel:    this.ammo_fuel    += autocvar_g_balance_nix_ammoincr_fuel;    break;
+                       case ammo_shells:  GiveResource(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammoincr_shells);  break;
+                       case ammo_nails:   GiveResource(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammoincr_nails);   break;
+                       case ammo_rockets: GiveResource(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammoincr_rockets); break;
+                       case ammo_cells:   GiveResource(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammoincr_cells);   break;
+                       case ammo_plasma:  GiveResource(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammoincr_plasma);   break;
+                       case ammo_fuel:    GiveResource(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammoincr_fuel);    break;
                }
 
                this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
index d2a7308dedf04267af31ea7b599a4abd447ca08f..b47e587511643e3076e172835052c16787d3079d 100644 (file)
@@ -3,6 +3,8 @@
 #include "hmg.qh"
 #include "rpc.qh"
 
+string autocvar_g_overkill;
+
 bool autocvar_g_overkill_powerups_replace;
 
 bool autocvar_g_overkill_itemwaypoints = true;
@@ -18,7 +20,7 @@ bool autocvar_g_overkill_filter_armormega;
 
 void ok_Initialize();
 
-REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
+REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
 {
        MUTATOR_ONADD
        {
index 62781c92073cfbd11278ea0313364bdac5bc0d8e..38cd7474b786c34343e4acf30009421d3fb8ec05 100644 (file)
@@ -4,7 +4,7 @@ int autocvar_g_physical_items;
 float autocvar_g_physical_items_damageforcescale;
 float autocvar_g_physical_items_reset;
 
-REGISTER_MUTATOR(physical_items, cvar("g_physical_items"))
+REGISTER_MUTATOR(physical_items, autocvar_g_physical_items)
 {
        // check if we have a physics engine
        MUTATOR_ONADD
index 53e4f49e60aadf08b13f9eb0880d3fe18711acd1..1084ff77895aa079e2dd9104ffe1159551f54db2 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_pinata.qh"
 
-REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill"));
+string autocvar_g_pinata;
+REGISTER_MUTATOR(pinata, expr_evaluate(autocvar_g_pinata) && !cvar("g_instagib") && !cvar("g_overkill"));
 
 MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
 {
index 9f0d8fbf0d5ec4907204169f4a7e53d17e338dbb..d3c1922b997c9437ab82ba9f9c27603d1ed14e1f 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_rocketflying.qh"
 
-REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying"));
+string autocvar_g_rocket_flying;
+REGISTER_MUTATOR(rocketflying, expr_evaluate(autocvar_g_rocket_flying));
 
 MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile)
 {
index 93dc602f0299df56c21597ea6cf966e951d2db78..d121cf1094685ac9263f55143b59da831cf37610 100644 (file)
@@ -1,5 +1,6 @@
 #include "sv_sandbox.qh"
 
+string autocvar_g_sandbox;
 int autocvar_g_sandbox_info;
 bool autocvar_g_sandbox_readonly;
 string autocvar_g_sandbox_storage_name;
@@ -18,7 +19,7 @@ float autocvar_g_sandbox_object_material_velocity_factor;
 float autosave_time;
 void sandbox_Database_Load();
 
-REGISTER_MUTATOR(sandbox, cvar("g_sandbox"))
+REGISTER_MUTATOR(sandbox, expr_evaluate(autocvar_g_sandbox))
 {
        MUTATOR_ONADD
        {
index 9ff3644748112eaad8c6dc1894487b52afe4dd21..61c302c3e7e0fa74bdd67d0a81a67ea00012efe1 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <lib/float.qh>
 
+string autocvar_g_spawn_near_teammate;
 float autocvar_g_spawn_near_teammate_distance;
 int autocvar_g_spawn_near_teammate_ignore_spawnpoint;
 int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max;
@@ -10,7 +11,7 @@ float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
 bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
 bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
 
-REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate"));
+REGISTER_MUTATOR(spawn_near_teammate, expr_evaluate(autocvar_g_spawn_near_teammate));
 
 .entity msnt_lookat;
 
@@ -20,6 +21,8 @@ REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate"));
 
 MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
 {
+       if (!teamplay) return;
+
        entity player = M_ARGV(0, entity);
        entity spawn_spot = M_ARGV(1, entity);
        vector spawn_score = M_ARGV(2, vector);
@@ -29,9 +32,6 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
 
        spawn_spot.msnt_lookat = NULL;
 
-       if(!teamplay)
-               return;
-
        RandomSelection_Init();
        FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), {
                if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance))
@@ -56,7 +56,8 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
 
 MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
 {
-       if(!teamplay) { return; }
+       if (!teamplay) return;
+
        entity player = M_ARGV(0, entity);
        entity spawn_spot = M_ARGV(1, entity);
 
index 4c99095b79df43f68e4daa402d97599cda72ebae..eb20082359ec1f25f92643bdd3f6a2b4624ebe17 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_superspec.qh"
 
-REGISTER_MUTATOR(superspec, cvar("g_superspectate"));
+string autocvar_g_superspectate;
+REGISTER_MUTATOR(superspec, expr_evaluate(autocvar_g_superspectate));
 
 #define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
 #define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false)
index 3e6edb04b356cf4b722504cbe2d5b04af8f494c8..a1b38fb6e750a2a44b6638189b6d18f3d89a3f0d 100644 (file)
@@ -1,11 +1,12 @@
 #include "sv_touchexplode.qh"
 
+string autocvar_g_touchexplode;
 float autocvar_g_touchexplode_radius;
 float autocvar_g_touchexplode_damage;
 float autocvar_g_touchexplode_edgedamage;
 float autocvar_g_touchexplode_force;
 
-REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode"));
+REGISTER_MUTATOR(touchexplode, expr_evaluate(autocvar_g_touchexplode));
 
 .float touchexplode_time;
 
index 92c5943c3d75455f7bc3d41b6f95184df7ba8555..199b4e202a7351a9b2d25bfb4364ec02119a259d 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_vampire.qh"
 
-REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib"));
+string autocvar_g_vampire;
+REGISTER_MUTATOR(vampire, expr_evaluate(autocvar_g_vampire) && !cvar("g_instagib"));
 
 MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
 {
@@ -12,7 +13,8 @@ MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
        if(frag_target != frag_attacker)
        if(!IS_DEAD(frag_target))
        {
-               GivePlayerHealth(frag_attacker, bound(0, damage_take, frag_target.health));
+               GiveResource(frag_attacker, RESOURCE_HEALTH,
+                       bound(0, damage_take, frag_target.health));
        }
 }
 
index e2b0f57d760e8cd13ba17925c0211809f31f8d3b..ce9e270654cb498281e01db990aa652e0ae8f8c1 100644 (file)
@@ -1,6 +1,7 @@
 #include "sv_vampirehook.qh"
 
-REGISTER_MUTATOR(vh, cvar("g_vampirehook"));
+string autocvar_g_vampirehook;
+REGISTER_MUTATOR(vh, expr_evaluate(autocvar_g_vampirehook));
 
 bool autocvar_g_vampirehook_teamheal;
 float autocvar_g_vampirehook_damage;
index b0d95ea29eb784570f48fb4621724dd930e0e292..c462a7e2b7c869398e733019a107f69049d16472 100644 (file)
@@ -4,7 +4,7 @@
 #ifdef CSQC
 REGISTER_MUTATOR(walljump, true);
 #elif defined(SVQC)
-REGISTER_MUTATOR(walljump, cvar("g_walljump"));
+REGISTER_MUTATOR(walljump, autocvar_g_walljump);
 #endif
 
 #define PHYS_WALLJUMP(s)                                               STAT(WALLJUMP, s)
index dbd765d983df928a9e6b262121cd9526b4813286..85912ee1c33f915ebdf59f907c616ef2f571c4f3 100644 (file)
@@ -13,18 +13,18 @@ const int WATERLEVEL_SUBMERGED = 3;
 #define SET_ONSLICK(s)                                         ((s).flags |= FL_ONSLICK)
 #define UNSET_ONSLICK(s)                                       ((s).flags &= ~FL_ONSLICK)
 
-#define GAMEPLAYFIX_DOWNTRACEONGROUND(s)    STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, NULL)
-#define GAMEPLAYFIX_EASIERWATERJUMP(s)      STAT(GAMEPLAYFIX_EASIERWATERJUMP, NULL)
-#define GAMEPLAYFIX_STEPDOWN(s)             STAT(GAMEPLAYFIX_STEPDOWN, NULL)
-#define GAMEPLAYFIX_STEPMULTIPLETIMES(s)    STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, NULL)
-#define GAMEPLAYFIX_UNSTICKPLAYERS(s)       STAT(GAMEPLAYFIX_UNSTICKPLAYERS, NULL)
-#define GAMEPLAYFIX_WATERTRANSITION(s)                 STAT(GAMEPLAYFIX_WATERTRANSITION, NULL)
-#define UPWARD_VELOCITY_CLEARS_ONGROUND(s)  STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, NULL)
-
-#define PHYS_STEPHEIGHT(s)                  STAT(MOVEVARS_STEPHEIGHT, NULL)
-#define PHYS_NOSTEP(s)                      STAT(NOSTEP, NULL)
-#define PHYS_JUMPSTEP(s)                    STAT(MOVEVARS_JUMPSTEP, NULL)
-#define PHYS_WALLFRICTION(s)                STAT(MOVEVARS_WALLFRICTION, NULL)
+#define GAMEPLAYFIX_DOWNTRACEONGROUND(s)    STAT(GAMEPLAYFIX_DOWNTRACEONGROUND)
+#define GAMEPLAYFIX_EASIERWATERJUMP(s)      STAT(GAMEPLAYFIX_EASIERWATERJUMP)
+#define GAMEPLAYFIX_STEPDOWN(s)             STAT(GAMEPLAYFIX_STEPDOWN)
+#define GAMEPLAYFIX_STEPMULTIPLETIMES(s)    STAT(GAMEPLAYFIX_STEPMULTIPLETIMES)
+#define GAMEPLAYFIX_UNSTICKPLAYERS(s)       STAT(GAMEPLAYFIX_UNSTICKPLAYERS)
+#define GAMEPLAYFIX_WATERTRANSITION(s)                 STAT(GAMEPLAYFIX_WATERTRANSITION)
+#define UPWARD_VELOCITY_CLEARS_ONGROUND(s)  STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND)
+
+#define PHYS_STEPHEIGHT(s)                  STAT(MOVEVARS_STEPHEIGHT)
+#define PHYS_NOSTEP(s)                      STAT(NOSTEP)
+#define PHYS_JUMPSTEP(s)                    STAT(MOVEVARS_JUMPSTEP)
+#define PHYS_WALLFRICTION(s)                STAT(MOVEVARS_WALLFRICTION)
 
 #ifdef CSQC
 .float bouncestop;
index d5a8e605af8a5b1721fa4d478b35d7de88727cc7..ae59381e5c11394ce93abcf441cd5c453be4d8a6 100644 (file)
@@ -80,7 +80,7 @@ bool IsFlying(entity a);
 #define PHYS_JETPACK_MAXSPEED_UP(s)         STAT(JETPACK_MAXSPEED_UP, s)
 #define PHYS_JETPACK_REVERSE_THRUST(s)         STAT(JETPACK_REVERSE_THRUST, s)
 
-#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, NULL)
+#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS)
 #define PHYS_JUMPVELOCITY(s)                STAT(MOVEVARS_JUMPVELOCITY, s)
 
 #define PHYS_MAXAIRSPEED(s)                 STAT(MOVEVARS_MAXAIRSPEED, s)
@@ -97,7 +97,7 @@ bool IsFlying(entity a);
 #define PHYS_WARSOWBUNNY_TOPSPEED(s)        STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, s)
 #define PHYS_WARSOWBUNNY_TURNACCEL(s)       STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, s)
 
-#define PHYS_SLICK_APPLYGRAVITY(s)             STAT(SLICK_APPLYGRAVITY, NULL)
+#define PHYS_SLICK_APPLYGRAVITY(s)             STAT(SLICK_APPLYGRAVITY)
 
 #define PHYS_INPUT_BUTTON_ATCK(s)           PHYS_INPUT_BUTTON_BUTTON1(s)
 #define PHYS_INPUT_BUTTON_JUMP(s)           PHYS_INPUT_BUTTON_BUTTON2(s)
index 6a6ce3fafdccd004a36a7c438b820b8423414040..4fc2e38219c144e2a5801129419847e2444c4808 100644 (file)
@@ -682,146 +682,29 @@ void Item_ScheduleInitialRespawn(entity e)
        Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : ITEM_RESPAWNTIME_INITIAL(e)));
 }
 
-void GivePlayerResource(entity player, .float resource_type, float amount)
+float Item_GiveAmmoTo(entity item, entity player, int resource_type, float ammomax)
 {
+       float amount = GetResourceAmount(item, resource_type);
        if (amount == 0)
        {
-               return;
-       }
-       switch (resource_type)
-       {
-               case health:
-               {
-                       // Ugly hack. We do not check if health goes beyond hard limit since
-                       // currently it is done in player_regen. We need to bring back this
-                       // check when other code is ported to this function.
-                       player.health = bound(player.health, player.health + amount,
-                               autocvar_g_balance_health_limit);
-                       // Correct code:
-                       //player.health = bound(player.health, player.health + amount,
-                       //      min(autocvar_g_balance_health_limit,
-                       //      RESOURCE_AMOUNT_HARD_LIMIT));
-                       player.pauserothealth_finished = max(player.pauserothealth_finished,
-                               time + autocvar_g_balance_pause_health_rot);
-                       return;
-               }
-               case armorvalue:
-               {
-                       // Ugly hack. We do not check if armor goes beyond hard limit since
-                       // currently it is done in player_regen. We need to bring back this
-                       // check when other code is ported to this function.
-                       player.armorvalue = bound(player.armorvalue, player.armorvalue +
-                               amount, autocvar_g_balance_armor_limit);
-                       // Correct code:
-                       //player.armorvalue = bound(player.armorvalue, player.armorvalue +
-                       //      amount, min(autocvar_g_balance_armor_limit,
-                       //      RESOURCE_AMOUNT_HARD_LIMIT));
-                       player.pauserotarmor_finished = max(player.pauserotarmor_finished,
-                               time + autocvar_g_balance_pause_armor_rot);
-                       return;
-               }
-               case ammo_shells:
-               case ammo_nails:
-               case ammo_rockets:
-               case ammo_cells:
-               case ammo_plasma:
-               {
-                       GivePlayerAmmo(player, resource_type, amount);
-                       return;
-               }
-               case ammo_fuel:
-               {
-                       player.ammo_fuel = bound(player.ammo_fuel, player.ammo_fuel +
-                               amount, min(g_pickup_fuel_max, RESOURCE_AMOUNT_HARD_LIMIT));
-                       player.pauserotfuel_finished = max(player.pauserotfuel_finished,
-                               time + autocvar_g_balance_pause_fuel_rot);
-                       return;
-               }
-       }
-}
-
-void GivePlayerHealth(entity player, float amount)
-{
-       GivePlayerResource(player, health, amount);
-}
-
-void GivePlayerArmor(entity player, float amount)
-{
-       GivePlayerResource(player, armorvalue, amount);
-}
-
-void GivePlayerAmmo(entity player, .float ammotype, float amount)
-{
-       if (amount == 0)
-       {
-               return;
-       }
-       float maxvalue = RESOURCE_AMOUNT_HARD_LIMIT;
-       switch (ammotype)
-       {
-               case ammo_shells:
-               {
-                       maxvalue = g_pickup_shells_max;
-                       break;
-               }
-               case ammo_cells:
-               {
-                       maxvalue = g_pickup_cells_max;
-                       break;
-               }
-               case ammo_rockets:
-               {
-                       maxvalue = g_pickup_rockets_max;
-                       break;
-               }
-               case ammo_plasma:
-               {
-                       maxvalue = g_pickup_plasma_max;
-                       break;
-               }
-               case ammo_nails:
-               {
-                       maxvalue = g_pickup_nails_max;
-                       break;
-               }
-       }
-       player.(ammotype) = min(player.(ammotype) + amount,
-               min(maxvalue, RESOURCE_AMOUNT_HARD_LIMIT));
-}
-
-void GivePlayerFuel(entity player, float amount)
-{
-       GivePlayerResource(player, ammo_fuel, amount);
-}
-
-float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax)
-{
-       if (!item.(ammotype))
                return false;
-
+       }
+       float player_amount = GetResourceAmount(player, resource_type);
        if (item.spawnshieldtime)
        {
-               if ((player.(ammotype) < ammomax) || item.pickup_anyway > 0)
+               if ((player_amount >= ammomax) && (item.pickup_anyway <= 0))
                {
-                       float amount = item.(ammotype);
-                       if ((player.(ammotype) + amount) > ammomax)
-                       {
-                               amount = ammomax - player.(ammotype);
-                       }
-                       GivePlayerResource(player, ammotype, amount);
-                       return true;
+                       return false;
                }
+               GiveResourceWithLimit(player, resource_type, amount, ammomax);
+               return true;
        }
-       else if(g_weapon_stay == 2)
+       if (g_weapon_stay != 2)
        {
-               float mi = min(item.(ammotype), ammomax);
-               if (player.(ammotype) < mi)
-               {
-                       GivePlayerResource(player, ammotype, mi - player.(ammotype));
-               }
-               return true;
+               return false;
        }
-       return false;
+       GiveResourceWithLimit(player, resource_type, amount, min(amount, ammomax));
+       return true;
 }
 
 float Item_GiveTo(entity item, entity player)
@@ -850,14 +733,14 @@ float Item_GiveTo(entity item, entity player)
                        }
                }
        }
-       pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health);
-       pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_shells, g_pickup_shells_max);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_nails, g_pickup_nails_max);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_rockets, g_pickup_rockets_max);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, g_pickup_cells_max);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_plasma, g_pickup_plasma_max);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_HEALTH, item.max_health);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_ARMOR, item.max_armorvalue);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_SHELLS, g_pickup_shells_max);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_BULLETS, g_pickup_nails_max);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_ROCKETS, g_pickup_rockets_max);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_CELLS, g_pickup_cells_max);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_PLASMA, g_pickup_plasma_max);
+       pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_FUEL, g_pickup_fuel_max);
        if (item.itemdef.instanceOfWeaponPickup)
        {
                WepSet w;
index f557e10308cc0d961df222bcb9a021dc4fd33399..fa78ff4b334ea9ede118f7817c81f02de9c0387d 100644 (file)
@@ -4,9 +4,6 @@
 #include <server/defs.qh>
 #endif
 
-/// \brief Unconditional maximum amount of resources the player can have.
-const int RESOURCE_AMOUNT_HARD_LIMIT = 999;
-
 const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
 
 // item networking
@@ -87,39 +84,7 @@ void Item_ScheduleRespawn(entity e);
 
 void Item_ScheduleInitialRespawn(entity e);
 
-/// \brief Gives player a resource such as health, armor or ammo.
-/// \param[in,out] player Player to give resource to.
-/// \param[in] resource_type Type of the resource.
-/// \param[in] amount Amount of resource to give.
-/// \return No return.
-void GivePlayerResource(entity player, .float resource_type, float amount);
-
-/// \brief Gives health to the player.
-/// \param[in,out] player Player to give health to.
-/// \param[in] amount Amount of health to give.
-/// \return No return.
-void GivePlayerHealth(entity player, float amount);
-
-/// \brief Gives armor to the player.
-/// \param[in,out] player Player to give armor to.
-/// \param[in] amount Amount of armor to give.
-/// \return No return.
-void GivePlayerArmor(entity player, float amount);
-
-/// \brief Gives ammo of the specified type to the player.
-/// \param[in,out] player Player to give ammo to.
-/// \param[in] type Ammo type property.
-/// \param[in] amount Amount of ammo to give.
-/// \return No return.
-void GivePlayerAmmo(entity player, .float ammotype, float amount);
-
-/// \brief Gives fuel to the player.
-/// \param[in,out] player Player to give fuel to.
-/// \param[in] amount Amount of fuel to give.
-/// \return No return.
-void GivePlayerFuel(entity player, float amount);
-
-float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax);
+float Item_GiveAmmoTo(entity item, entity player, int resource_type, float ammomax);
 
 float Item_GiveTo(entity item, entity player);
 
index afbf79eb54777d0e2852366e9d44956ff69c3966..6605f00c26d2a2e48378f519f9a9fc86c314a7cb 100644 (file)
@@ -530,7 +530,9 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim)
        int compressed_shotorg = compressShotOrigin(this.movedir);
        // make them match perfectly
 #ifdef SVQC
-       this.movedir = decompressShotOrigin(this.owner.stat_shotorg = compressed_shotorg);
+    // null during init
+    if (this.owner) this.owner.stat_shotorg = compressed_shotorg;
+       this.movedir = decompressShotOrigin(compressed_shotorg);
 #else
        this.movedir = decompressShotOrigin(compressed_shotorg);
 #endif
index 73cd00d6c1c3e9eea514afa31e9c00afea76609a..5a782b98f9e462210ae5de73ec15102b340507b5 100644 (file)
@@ -41,3 +41,5 @@ const int PROJECTILE_ARC_BOLT = 35;
 // projectile IDs 40-50 reserved
 
 const int PROJECTILE_RPC = 60;
+
+// projectile IDs 70-100 reserved
index 16fd93450332e44fa0952d7003f6c50fb1fc6b03..70e5f378422af7850a91ec65cf8857d6c61ae0d8 100644 (file)
@@ -11,8 +11,8 @@
 #undef setcolor
 
 #ifdef MENUQC
-       #define NULL (0, null_entity)
+       #define NULL (RVALUE, null_entity)
        #define world NULL
 #else
-       #define NULL (0, world)
+       #define NULL (RVALUE, world)
 #endif
index 4da78f1444eb27b397d926442c7a153012adf01e..9d582091039faa12ac2028efeb9022357be3a631 100644 (file)
@@ -182,7 +182,17 @@ void make_safe_for_remove(entity this);
        #define SV_Shutdown _SV_Shutdown
 
        void _StartFrame();
-       void StartFrame() { if (_StartFrame) _StartFrame(); }
+       bool _StartFrame_init;
+       void spawnfunc_worldspawn(entity);
+       void StartFrame() {
+               if (!_StartFrame_init) {
+                       _StartFrame_init = true;
+                       float oldtime = time; time = 1;
+                       __spawnfunc_expecting = 2; NULL.__spawnfunc_constructor(NULL);
+                       time = oldtime;
+        }
+        if (_StartFrame) _StartFrame();
+       }
        #define StartFrame _StartFrame
 
        void _SetNewParms();
index 1541b9997cff5efb80c96fa1a3844c00b6dcc1ba..f2ec6df4a4206e8797f08ecb529fa0faa6adbcb3 100644 (file)
@@ -9,6 +9,9 @@
     #define MACRO_END } while (0)
 #endif
 
+/** Marker for use in (RVALUE, (expr)) */
+#define RVALUE 0
+
 #define _CAT(a, b) a ## b
 #define CAT(a, b) _CAT(a, b)
 
index 21e0c5239f59aa7399533f5c35aca8c0e0c2487b..6c29a4b88dd100f74ded51a0fe91ad82179c7d25 100644 (file)
@@ -10,6 +10,7 @@
 
        #include "p99.qh"
        #define OVERLOAD(F, ...) P99_IF_EMPTY(__VA_ARGS__)(P99_PASTE2(F, _00)())(P99_PASTE3(F, _, P00_NARG(__VA_ARGS__))(__VA_ARGS__))
+       /** for use within a macro */
        #define OVERLOAD_(F, ...) P99_IF_EMPTY(__VA_ARGS__)(P99_PASTE2(F, _00)())(P99_PASTE3(F, _, P00_NARG(__VA_ARGS__))(__VA_ARGS__))
 #else
        #define EVAL(...) __VA_ARGS__
index f4c246f33058997803e1291f243286cfce99362d..0a61cc003dfdc9046acf24dc7b33b057115732e8 100644 (file)
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "macro.qh"
+
 // Transition from global 'self' to local 'this'
 
 // Step 1: auto oldself
 
 // Step 2: const self
 #if 1
-    #define self (0, self)
+    #define self (RVALUE, self)
     [[alias("self")]] entity __self;
     #define setself(s) (__self = s)
-    #define WITHSELF(value, block) WITH(entity, __self, value, (0, block))
+    #define WITHSELF(value, block) WITH(entity, __self, value, (RVALUE, block))
 #endif
 
 // Step 3: propagate SELFPARAM()
@@ -32,7 +34,7 @@
 // Step 5: this should work
 #if 1
     #undef self
-    #define self (0, this)
+    #define self (RVALUE, this)
 #endif
 
 // Step 6: remove SELFPARAM, add parameters
@@ -56,11 +58,11 @@ noref entity _selftemp;
 #define SELFWRAP_SET(T, e, f) \
     (_selftemp = (e), _selftemp.__##T = ((f) ? T##_self : func_null), _selftemp.self##T = (f))
 #define SELFWRAP_GET(T, e) \
-    (0, (e).self##T)
+    (RVALUE, (e).self##T)
 #define _SELFWRAP_SET(T, e, f) \
     ((e).__##T = (f))
 #define _SELFWRAP_GET(T, e) \
-    (0, (e).__##T)
+    (RVALUE, (e).__##T)
 
 SELFWRAP(think, void, (), (entity this), (this))
 #define setthink(e, f) SELFWRAP_SET(think, e, f)
index e0605c93840599e8f928f359ce1f6b02cccd7016..e30e565fd3259a6916d36d5c230d7165bae4dd1c 100644 (file)
@@ -27,34 +27,71 @@ noref bool require_spawnfunc_prefix;
        #define _spawnfunc_check(fld) \
                if (fieldname == #fld) continue;
 
-       noref bool __spawnfunc_expecting;
+       noref int __spawnfunc_expecting;
        noref entity __spawnfunc_expect;
        noref bool __spawnfunc_unreachable_workaround = true;
 
+    .void(entity) __spawnfunc_constructor;
+    noref IntrusiveList g_spawn_queue;
+
+    #define SPAWNFUNC_INTERNAL_FIELDS(X) \
+        X(string, classname, "spawnfunc") \
+        X(string, targetname, string_null) \
+        /**/
+
+    #define X(T, fld, def) .T fld, __spawnfunc_##fld;
+    SPAWNFUNC_INTERNAL_FIELDS(X)
+    #undef X
+
+    void __spawnfunc_defer(entity prototype, void(entity) constructor)
+    {
+        IL_PUSH(g_spawn_queue, prototype);
+        #define X(T, fld, def) { prototype.__spawnfunc_##fld = prototype.fld; prototype.fld = def; }
+        SPAWNFUNC_INTERNAL_FIELDS(X);
+        #undef X
+        prototype.__spawnfunc_constructor = constructor;
+    }
+
+    noref IntrusiveList g_map_entities;
+    #define __spawnfunc_spawn_all() MACRO_BEGIN \
+        g_map_entities = IL_NEW(); \
+        IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
+    MACRO_END
+
+    void __spawnfunc_spawn(entity prototype)
+    {
+        entity e = new(clone);
+        copyentity(prototype, e);
+        IL_PUSH(g_map_entities, e);
+        #define X(T, fld, def) { e.fld = e.__spawnfunc_##fld; e.__spawnfunc_##fld = def; }
+        SPAWNFUNC_INTERNAL_FIELDS(X);
+        #undef X
+        e.__spawnfunc_constructor(e);
+    }
+
        #define spawnfunc_1(id) spawnfunc_2(id, FIELDS_UNION)
        #define spawnfunc_2(id, whitelist) \
                void __spawnfunc_##id(entity this); \
                [[accumulate]] void spawnfunc_##id(entity this) \
                { \
-                       if (__spawnfunc_expecting) \
-                       { \
+                   bool dospawn = true; \
+                   if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = false; } \
+                       else if (__spawnfunc_expecting) { \
                                /* engine call */ \
+                if (!g_spawn_queue) { g_spawn_queue = IL_NEW(); } \
                                __spawnfunc_expecting = false; \
                                this = __spawnfunc_expect; \
                                __spawnfunc_expect = NULL; \
-                       } \
-                       else \
-                       { \
+                dospawn = false; \
+                       } else { \
+                           /* userland call */ \
                                assert(this); \
                        } \
-                       if (!this.sourceLoc) \
-                       { \
+                       if (!this.sourceLoc) { \
                                this.sourceLoc = __FILE__ ":" STR(__LINE__); \
                        } \
-                       if (!this.spawnfunc_checked) \
-                       { \
-                               for (int i = 0, n = numentityfields(); i < n; ++i) \
-                               { \
+                       if (!this.spawnfunc_checked) { \
+                               for (int i = 0, n = numentityfields(); i < n; ++i) { \
                                        string value = getentityfieldstring(i, this); \
                                        string fieldname = entityfieldname(i); \
                                        whitelist(_spawnfunc_checktypes) \
@@ -65,8 +102,15 @@ noref bool require_spawnfunc_prefix;
                                        LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), #id, fieldname, value); \
                                } \
                                this.spawnfunc_checked = true; \
+                               if (this) { \
+                    /* not worldspawn, delay spawn */ \
+                    __spawnfunc_defer(this, __spawnfunc_##id); \
+                } else { \
+                    /* world might not be "worldspawn" */ \
+                    this.__spawnfunc_constructor = __spawnfunc_##id; \
+                } \
                        } \
-                       __spawnfunc_##id(this); \
+                       if (dospawn) { __spawnfunc_##id(this); } \
                        if (__spawnfunc_unreachable_workaround) return; \
                } \
                void __spawnfunc_##id(entity this)
index 4642f76403c3124b6cdc30fb9b3f923263394548..1100c474cb9eb49074cd85d08431fd0f0ba61012 100644 (file)
@@ -33,8 +33,8 @@ int g_magic_stats_hole = 0;
        void stats_get() {}
        #define STAT(...) EVAL_STAT(OVERLOAD(STAT, __VA_ARGS__))
        #define EVAL_STAT(...) __VA_ARGS__
-    #define STAT_1(id) STAT_2(id, NULL)
-       #define STAT_2(id, cl) (0, _STAT(id))
+    #define STAT_1(id) (RVALUE, _STAT(id))
+       #define STAT_2(id, cl) STAT_1(id)
 
        #define getstat_int(id) getstati(id, 0, 24)
        #define getstat_bool(id) boolean(getstati(id))
@@ -61,9 +61,14 @@ int g_magic_stats_hole = 0;
                }
        #define REGISTER_STAT_3(x, T, expr) REGISTER_STAT_2(x, T)
 #elif defined(SVQC)
+    /** Internal use only */
+    entity STATS;
        /** Add all registered stats, access with `STAT(ID, player)` or `.type stat = _STAT(ID); player.stat` */
        void stats_add() {}
-       #define STAT(id, cl) (cl)._STAT(id)
+       #define STAT(...) EVAL_STAT(OVERLOAD_(STAT, __VA_ARGS__))
+    #define EVAL_STAT(...) __VA_ARGS__
+    #define STAT_1(id) (RVALUE, STAT_2(id, STATS))
+       #define STAT_2(id, cl) (cl)._STAT(id)
 
        #define addstat_int(id, fld) addstat(id, AS_INT, fld)
        #define addstat_bool(id, fld) addstat(id, AS_INT, fld)
@@ -83,9 +88,10 @@ int g_magic_stats_hole = 0;
        const int AS_FLOAT = 8;
 
        .int __stat_null;
-       /** Prevent engine stats being sent */
-       STATIC_INIT(stats_clear)
+       STATIC_INIT(stats)
        {
+           STATS = new(stats);
+           // Prevent engine stats being sent
                int r = STATS_ENGINE_RESERVE;
                for (int i = 0, n = 256 - r; i < n; ++i) {
                        #define X(_, name, id) if (i == id) continue;
@@ -111,10 +117,11 @@ int g_magic_stats_hole = 0;
                        addstat_##T(STAT_##id.m_id, fld); \
                }
        void GlobalStats_update(entity this) {}
+    /** TODO: do we want the global copy to update? */
     #define REGISTER_STAT_3(id, T, expr) \
        REGISTER_STAT_2(id, T); \
        [[accumulate]] void GlobalStats_update(entity this) { STAT(id, this) = (expr); } \
-       STATIC_INIT(worldstat_##id) { entity this = NULL; STAT(id, this) = (expr); }
+       STATIC_INIT(worldstat_##id) { entity this = STATS; STAT(id, this) = (expr); }
 #else
        #define REGISTER_STAT_2(id, type)
     #define REGISTER_STAT_3(id, T, expr)
index 87a8d56892d69577c7d07d2cbe9f9ced6cc5162e..99115fbdc4fcaa48d2b3b33f8aca6458d530d718 100644 (file)
@@ -20,6 +20,7 @@
 #include <server/playerdemo.qc>
 #include <server/portals.qc>
 #include <server/race.qc>
+#include <server/resources.qc>
 #include <server/round_handler.qc>
 #include <server/scores.qc>
 #include <server/scores_rules.qc>
index 2967c110ce9a5a58db86abcf00a996c27b53172b..3a8898670354511f40a1e515d25aeae0d5b28068 100644 (file)
@@ -20,6 +20,7 @@
 #include <server/playerdemo.qh>
 #include <server/portals.qh>
 #include <server/race.qh>
+#include <server/resources.qh>
 #include <server/round_handler.qh>
 #include <server/scores.qh>
 #include <server/scores_rules.qh>
index f585a9a2c33db3eafb7b72168b2b3d8a50de8afd..fa8cee7172c6d96b73e630b417079f4d9f55211d 100644 (file)
@@ -165,12 +165,7 @@ int autocvar_g_maxplayers;
 float autocvar_g_maxplayers_spectator_blocktime;
 float autocvar_g_maxpushtime;
 float autocvar_g_maxspeed;
-#define autocvar_g_instagib cvar("g_instagib")
-bool autocvar_g_instagib_damagedbycontents = true;
-bool autocvar_g_instagib_blaster_keepdamage = false;
-bool autocvar_g_instagib_blaster_keepforce = false;
-bool autocvar_g_instagib_mirrordamage;
-bool autocvar_g_instagib_friendlypush = true;
+bool autocvar_g_instagib;
 #define autocvar_g_mirrordamage cvar("g_mirrordamage")
 #define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
 bool autocvar_g_mirrordamage_onlyweapons;
@@ -416,7 +411,6 @@ float autocvar_g_monsters_armor_blockpercent;
 float autocvar_g_monsters_healthbars;
 bool autocvar_g_monsters_lineofsight = true;
 //bool autocvar_g_monsters_ignoretraces = true;
-#define autocvar_g_bloodloss cvar("g_bloodloss")
 bool autocvar_g_nades;
 bool autocvar_g_nades_override_dropweapon = true;
 vector autocvar_g_nades_throw_offset;
index f1d417d6bace6987d6f1a72984f6f77260b7db0f..22543766be9af89c136c5725490481e9643c626c 100644 (file)
@@ -12,6 +12,7 @@
 #include "teamplay.qh"
 #include "playerdemo.qh"
 #include "spawnpoints.qh"
+#include "resources.qh"
 #include "g_damage.qh"
 #include "g_hook.qh"
 #include "command/common.qh"
@@ -1628,8 +1629,8 @@ void player_regen(entity this)
                float mina, maxa, limith, limita;
                maxa = autocvar_g_balance_armor_rotstable;
                mina = autocvar_g_balance_armor_regenstable;
-               limith = autocvar_g_balance_health_limit;
-               limita = autocvar_g_balance_armor_limit;
+               limith = GetResourceLimit(this, RESOURCE_HEALTH);
+               limita = GetResourceLimit(this, RESOURCE_ARMOR);
 
                regen_health_rotstable = regen_health_rotstable * max_mod;
                regen_health_stable = regen_health_stable * max_mod;
@@ -1656,7 +1657,7 @@ void player_regen(entity this)
 
                maxf = autocvar_g_balance_fuel_rotstable;
                minf = autocvar_g_balance_fuel_regenstable;
-               limitf = autocvar_g_balance_fuel_limit;
+               limitf = GetResourceLimit(this, RESOURCE_FUEL);
 
                this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf);
        }
index a34611cb50ca783ddacc0c448f95b39f5070f93d..a260fd00558afc502eb3ba9d97069ddc3df9f5de 100644 (file)
@@ -9,6 +9,7 @@
 #include "../common/state.qh"
 #include "../common/physics/player.qh"
 #include "../common/t_items.qh"
+#include "resources.qh"
 #include "../common/vehicles/all.qh"
 #include "../common/items/_mod.qh"
 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
@@ -275,7 +276,7 @@ bool frag_centermessage_override(entity attacker, entity targ, int deathtype, in
        if(deathtype == DEATH_FIRE.m_id)
        {
                Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping));
-               Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker.netname, kill_count_to_target, attacker.health, attacker.armorvalue, (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping));
+               Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker.netname, kill_count_to_target, GetResourceAmount(attacker, RESOURCE_HEALTH), GetResourceAmount(attacker, RESOURCE_ARMOR), (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping));
                return true;
        }
 
@@ -427,8 +428,8 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
                                        CHOICE_TYPEFRAGGED,
                                        attacker.netname,
                                        kill_count_to_target,
-                                       attacker.health,
-                                       attacker.armorvalue,
+                                       GetResourceAmount(attacker, RESOURCE_HEALTH),
+                                       GetResourceAmount(attacker, RESOURCE_ARMOR),
                                        (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
                                );
                        }
@@ -450,8 +451,8 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
                                        CHOICE_FRAGGED,
                                        attacker.netname,
                                        kill_count_to_target,
-                                       attacker.health,
-                                       attacker.armorvalue,
+                                       GetResourceAmount(attacker, RESOURCE_HEALTH),
+                                       GetResourceAmount(attacker, RESOURCE_ARMOR),
                                        (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
                                );
                        }
@@ -543,7 +544,7 @@ void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypo
 
        STAT(FROZEN, targ) = frozen_type;
        targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
-       targ.health = ((frozen_type == 3) ? targ_maxhealth : 1);
+       SetResourceAmount(targ, RESOURCE_HEALTH, ((frozen_type == 3) ? targ_maxhealth : 1));
        targ.revive_speed = freeze_time;
        if(targ.bot_attack)
                IL_REMOVE(g_bot_targets, targ);
@@ -588,7 +589,7 @@ void Unfreeze (entity targ)
 
        if(STAT(FROZEN, targ) && STAT(FROZEN, targ) != 3) // only reset health if target was frozen
        {
-               targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health);
+               SetResourceAmount(targ, RESOURCE_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health));
                targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
        }
 
@@ -652,9 +653,9 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d
                // These are ALWAYS lethal
                // No damage modification here
                // Instead, prepare the victim for his death...
-               targ.armorvalue = 0;
+               SetResourceAmount(targ, RESOURCE_ARMOR, 0);
                targ.spawnshieldtime = 0;
-               targ.health = 0.9; // this is < 1
+               SetResourceAmount(targ, RESOURCE_HEALTH, 0.9); // this is < 1
                targ.flags -= targ.flags & FL_GODMODE;
                damage = 100000;
        }
@@ -754,7 +755,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d
                        if(damage >= autocvar_g_frozen_revive_falldamage)
                        {
                                Unfreeze(targ);
-                               targ.health = autocvar_g_frozen_revive_falldamage_health;
+                               SetResourceAmount(targ, RESOURCE_HEALTH, autocvar_g_frozen_revive_falldamage_health);
                                Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3);
                                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
                                Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
index 64aa03b5026d5b62f0e24df6fc731389a8b637e9..c47952c5d99f0c83231c5916c6f47e0fcfb13183 100644 (file)
@@ -521,7 +521,7 @@ void detect_maptype()
                o.y += random() * (world.maxs.y - world.mins.y);
                o.z += random() * (world.maxs.z - world.mins.z);
 
-               tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 32768', MOVE_WORLDONLY, NULL);
+               tracebox(o, STAT(PL_MIN), STAT(PL_MAX), o - '0 0 32768', MOVE_WORLDONLY, NULL);
                if(trace_fraction == 1)
                        continue;
 
@@ -936,6 +936,7 @@ spawnfunc(worldspawn)
        WinningConditionHelper(this); // set worldstatus
 
        world_initialized = 1;
+       __spawnfunc_spawn_all();
 }
 
 spawnfunc(light)
index bc09d7a8cd9d92d3da6340807e8a09fd13b384cc..fd2e19878f54a78f7c03423865f4285ca94213f8 100644 (file)
@@ -620,6 +620,38 @@ enum {
        MUT_ITEMTOUCH_PICKUP // return this flag to have the item "picked up" and taken even after mutator handled it
 };
 
+/** Called when the amount of entity resources changes. Can be used to override
+resource limit. */
+#define EV_GetResourceLimit(i, o) \
+       /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \
+       /** resource type */  i(int, MUTATOR_ARGV_1_int) \
+       /** limit */          i(float, MUTATOR_ARGV_2_float) \
+       /**/                  o(float, MUTATOR_ARGV_2_float) \
+       /**/
+MUTATOR_HOOKABLE(GetResourceLimit, EV_GetResourceLimit);
+
+/** Called when the amount of resource of an entity changes. See RESOURCE_*
+constants for resource types. Return true to forbid the change. */
+#define EV_SetResourceAmount(i, o) \
+       /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \
+       /** resource type */  i(int, MUTATOR_ARGV_1_int) \
+       /**/                  o(int, MUTATOR_ARGV_1_int) \
+       /** amount */         i(float, MUTATOR_ARGV_2_float) \
+       /**/                  o(float, MUTATOR_ARGV_2_float) \
+       /**/
+MUTATOR_HOOKABLE(SetResourceAmount, EV_SetResourceAmount);
+
+/** Called when entity is being given some resource. See RESOURCE_* constants
+for resource types. Return true to forbid giving. */
+#define EV_GiveResource(i, o) \
+       /** receiver */      i(entity, MUTATOR_ARGV_0_entity) \
+       /** resource type */ i(int, MUTATOR_ARGV_1_int) \
+       /**/                 o(int, MUTATOR_ARGV_1_int) \
+       /** amount */        i(float, MUTATOR_ARGV_2_float) \
+       /**/                 o(float, MUTATOR_ARGV_2_float) \
+       /**/
+MUTATOR_HOOKABLE(GiveResource, EV_GiveResource);
+
 /** called at when a player connect */
 #define EV_ClientConnect(i, o) \
     /** player */ i(entity, MUTATOR_ARGV_0_entity) \
diff --git a/qcsrc/server/resources.qc b/qcsrc/server/resources.qc
new file mode 100644 (file)
index 0000000..edf4ff1
--- /dev/null
@@ -0,0 +1,190 @@
+#include "resources.qh"
+/// \file
+/// \brief Source file that contains implementation of the resource system.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+#include "autocvars.qh"
+#include "miscfunctions.qh"
+
+float GetResourceLimit(entity e, int resource_type)
+{
+       float limit;
+       switch (resource_type)
+       {
+               case RESOURCE_HEALTH:
+               {
+                       limit = autocvar_g_balance_health_limit;
+                       break;
+               }
+               case RESOURCE_ARMOR:
+               {
+                       limit = autocvar_g_balance_armor_limit;
+                       break;
+               }
+               case RESOURCE_SHELLS:
+               {
+                       limit = g_pickup_shells_max;
+                       break;
+               }
+               case RESOURCE_BULLETS:
+               {
+                       limit = g_pickup_nails_max;
+                       break;
+               }
+               case RESOURCE_ROCKETS:
+               {
+                       limit = g_pickup_rockets_max;
+                       break;
+               }
+               case RESOURCE_CELLS:
+               {
+                       limit = g_pickup_cells_max;
+                       break;
+               }
+               case RESOURCE_PLASMA:
+               {
+                       limit = g_pickup_plasma_max;
+                       break;
+               }
+               case RESOURCE_FUEL:
+               {
+                       limit = autocvar_g_balance_fuel_limit;
+                       break;
+               }
+               default:
+               {
+                       error("GetResourceLimit: Invalid resource type.");
+                       return 0;
+               }
+       }
+       MUTATOR_CALLHOOK(GetResourceLimit, e, resource_type, limit);
+       limit = M_ARGV(2, float);
+       if (limit > RESOURCE_AMOUNT_HARD_LIMIT)
+       {
+               limit = RESOURCE_AMOUNT_HARD_LIMIT;
+       }
+       return limit;
+}
+
+float GetResourceAmount(entity e, int resource_type)
+{
+       .float resource_field = GetResourceField(resource_type);
+       return e.(resource_field);
+}
+
+void SetResourceAmount(entity e, int resource_type, float amount)
+{
+       bool forbid = MUTATOR_CALLHOOK(SetResourceAmount, e, resource_type, amount);
+       if (forbid)
+       {
+               return;
+       }
+       resource_type = M_ARGV(1, int);
+       amount = M_ARGV(2, float);
+       .float resource_field = GetResourceField(resource_type);
+       if (e.(resource_field) == amount)
+       {
+               return;
+       }
+       float max_amount = GetResourceLimit(e, resource_type);
+       if (amount > max_amount)
+       {
+               amount = max_amount;
+       }
+       e.(resource_field) = amount;
+}
+
+void GiveResource(entity receiver, int resource_type, float amount)
+{
+       if (amount == 0)
+       {
+               return;
+       }
+       bool forbid = MUTATOR_CALLHOOK(GiveResource, receiver, resource_type,
+               amount);
+       if (forbid)
+       {
+               return;
+       }
+       resource_type = M_ARGV(1, int);
+       amount = M_ARGV(2, float);
+       if (amount <= 0)
+       {
+               return;
+       }
+       SetResourceAmount(receiver, resource_type,
+               GetResourceAmount(receiver, resource_type) + amount);
+       switch (resource_type)
+       {
+               case RESOURCE_HEALTH:
+               {
+                       receiver.pauserothealth_finished =
+                               max(receiver.pauserothealth_finished, time +
+                               autocvar_g_balance_pause_health_rot);
+                       return;
+               }
+               case RESOURCE_ARMOR:
+               {
+                       receiver.pauserotarmor_finished =
+                               max(receiver.pauserotarmor_finished, time +
+                               autocvar_g_balance_pause_armor_rot);
+                       return;
+               }
+               case RESOURCE_FUEL:
+               {
+                       receiver.pauserotfuel_finished = max(receiver.pauserotfuel_finished,
+                               time + autocvar_g_balance_pause_fuel_rot);
+                       return;
+               }
+       }
+}
+
+void GiveResourceWithLimit(entity receiver, int resource_type, float amount,
+       float limit)
+{
+       if (amount == 0)
+       {
+               return;
+       }
+       float current_amount = GetResourceAmount(receiver, resource_type);
+       if (current_amount + amount > limit)
+       {
+               amount = limit - current_amount;
+       }
+       GiveResource(receiver, resource_type, amount);
+}
+
+int GetResourceType(.float resource_field)
+{
+       switch (resource_field)
+       {
+               case health: { return RESOURCE_HEALTH; }
+               case armorvalue: { return RESOURCE_ARMOR; }
+               case ammo_shells: { return RESOURCE_SHELLS; }
+               case ammo_nails: { return RESOURCE_BULLETS; }
+               case ammo_rockets: { return RESOURCE_ROCKETS; }
+               case ammo_cells: { return RESOURCE_CELLS; }
+               case ammo_plasma: { return RESOURCE_PLASMA; }
+               case ammo_fuel: { return RESOURCE_FUEL; }
+       }
+       error("GetResourceType: Invalid field.");
+       return 0;
+}
+
+.float GetResourceField(int resource_type)
+{
+       switch (resource_type)
+       {
+               case RESOURCE_HEALTH: { return health; }
+               case RESOURCE_ARMOR: { return armorvalue; }
+               case RESOURCE_SHELLS: { return ammo_shells; }
+               case RESOURCE_BULLETS: { return ammo_nails; }
+               case RESOURCE_ROCKETS: { return ammo_rockets; }
+               case RESOURCE_CELLS: { return ammo_cells; }
+               case RESOURCE_PLASMA: { return ammo_plasma; }
+               case RESOURCE_FUEL: { return ammo_fuel; }
+       }
+       error("GetResourceField: Invalid resource type.");
+       return health;
+}
diff --git a/qcsrc/server/resources.qh b/qcsrc/server/resources.qh
new file mode 100644 (file)
index 0000000..ce8e1e8
--- /dev/null
@@ -0,0 +1,70 @@
+#pragma once
+/// \file
+/// \brief Header file that describes the resource system.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+/// \brief Unconditional maximum amount of resources the entity can have.
+const int RESOURCE_AMOUNT_HARD_LIMIT = 999;
+
+/// \brief Describes the available resource types.
+enum
+{
+       RESOURCE_HEALTH = 1, ///< Health.
+       RESOURCE_ARMOR, ///< Armor.
+       RESOURCE_SHELLS, ///< Shells (used by shotgun).
+       RESOURCE_BULLETS, ///< Bullets (used by machinegun and rifle)
+       RESOURCE_ROCKETS, ///< Rockets (used by mortar, hagar, devastator, etc).
+       RESOURCE_CELLS, ///< Cells (used by electro, crylink, vortex, etc)
+       RESOURCE_PLASMA, ///< Plasma (unused).
+       RESOURCE_FUEL ///< Fuel (used by jetpack).
+};
+
+// ============================ Public API ====================================
+
+/// \brief Returns the maximum amount of the given resource.
+/// \param[in] e Entity to check.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \return Maximum amount of the given resource.
+float GetResourceLimit(entity e, int resource_type);
+
+/// \brief Returns the current amount of resource the given entity has.
+/// \param[in] e Entity to check.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \return Current amount of resource the given entity has.
+float GetResourceAmount(entity e, int resource_type);
+
+/// \brief Sets the current amount of resource the given entity will have.
+/// \param[in,out] e Entity to adjust.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \param[in] amount Amount of resource to set.
+/// \return No return.
+void SetResourceAmount(entity e, int resource_type, float amount);
+
+/// \brief Gives an entity some resource.
+/// \param[in,out] receiver Entity to give resource to.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \param[in] amount Amount of resource to give.
+/// \return No return.
+void GiveResource(entity receiver, int resource_type, float amount);
+
+/// \brief Gives an entity some resource but not more than a limit.
+/// \param[in,out] receiver Entity to give resource to.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \param[in] amount Amount of resource to give.
+/// \param[in] limit Limit of resources to give.
+/// \return No return.
+void GiveResourceWithLimit(entity receiver, int resource_type, float amount,
+       float limit);
+
+// ===================== Legacy and/or internal API ===========================
+
+/// \brief Converts an entity field to resource type.
+/// \param[in] resource_field Entity field to convert.
+/// \return Resource type (a RESOURCE_* constant).
+int GetResourceType(.float resource_field);
+
+/// \brief Converts resource type (a RESOURCE_* constant) to entity field.
+/// \param[in] resource_type Type of the resource.
+/// \return Entity field for that resource.
+.float GetResourceField(int resource_type);
index 51890765d6b5a5c66d07fb279f1aaa3d16cec618..f0f18b826bdae4d4409a8559f87dd2587c63e75d 100644 (file)
@@ -245,6 +245,77 @@ void StartFrame()
 .string gametypefilter;
 .string cvarfilter;
 bool DoesQ3ARemoveThisEntity(entity this);
+
+/**
+ * Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ...
+ * +: all must match. this is the default
+ * -: one must NOT match
+ *
+ * var>x
+ * var<x
+ * var>=x
+ * var<=x
+ * var==x
+ * var!=x
+ * var===x
+ * var!==x
+ */
+bool expr_evaluate(string s)
+{
+    bool ret = false;
+    if (str2chr(s, 0) == '+') {
+        s = substring(s, 1, -1);
+    } else if (str2chr(s, 0) == '-') {
+        ret = true;
+        s = substring(s, 1, -1);
+    }
+    bool expr_fail = false;
+    for (int i = 0, n = tokenize_console(s); i < n; ++i) {
+        int o;
+        string k, v;
+        s = argv(i);
+        #define X(expr) \
+            if (expr) { \
+                continue; \
+            } else { \
+                expr_fail = true; \
+                break; \
+            }
+        #define BINOP(op, len, expr) \
+            if ((o = strstrofs(s, op, 0)) >= 0) { \
+                k = substring(s, 0, o); \
+                v = substring(s, o + len, -1); \
+                X(expr); \
+            }
+        BINOP(">=", 2, cvar(k) >= stof(v));
+        BINOP("<=", 2, cvar(k) <= stof(v));
+        BINOP(">",  1, cvar(k) >  stof(v));
+        BINOP("<",  1, cvar(k) <  stof(v));
+        BINOP("==", 2, cvar(k) == stof(v));
+        BINOP("!=", 2, cvar(k) != stof(v));
+        BINOP("===", 3, cvar_string(k) == v);
+        BINOP("!==", 3, cvar_string(k) != v);
+        {
+            k = s;
+            bool b = true;
+            if (str2chr(k, 0) == '!') {
+                k = substring(s, 1, -1);
+                b = false;
+            }
+            float f = stof(k);
+            bool isnum = ftos(f) == k;
+            X(boolean(isnum ? f : cvar(k)) == b);
+        }
+        #undef BINOP
+        #undef X
+    }
+    if (!expr_fail) {
+        ret = !ret;
+    }
+    // now ret is true if we want to keep the item, and false if we want to get rid of it
+    return ret;
+}
+
 void SV_OnEntityPreSpawnFunction(entity this)
 {
        __spawnfunc_expecting = true;
@@ -253,160 +324,44 @@ void SV_OnEntityPreSpawnFunction(entity this)
        if (this.gametypefilter != "")
        if (!isGametypeInFilter(MapInfo_LoadedGametype, teamplay, have_team_spawns, this.gametypefilter))
        {
-               delete(this);
-               __spawnfunc_expecting = false;
-               return;
+               goto cleanup;
        }
-       if(this.cvarfilter != "")
-       {
-               float n, i, o, inv;
-               string s, k, v;
-               inv = 0;
-
-               s = this.cvarfilter;
-               if(substring(s, 0, 1) == "+")
-               {
-                       s = substring(s, 1, -1);
-               }
-               else if(substring(s, 0, 1) == "-")
-               {
-                       inv = 1;
-                       s = substring(s, 1, -1);
-               }
-
-               n = tokenize_console(s);
-               for(i = 0; i < n; ++i)
-               {
-                       s = argv(i);
-                       // syntax:
-                       // var>x
-                       // var<x
-                       // var>=x
-                       // var<=x
-                       // var==x
-                       // var!=x
-                       // var===x
-                       // var!==x
-                       if((o = strstrofs(s, ">=", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+2, -1);
-                               if(cvar(k) < stof(v))
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, "<=", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+2, -1);
-                               if(cvar(k) > stof(v))
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, ">", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+1, -1);
-                               if(cvar(k) <= stof(v))
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, "<", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+1, -1);
-                               if(cvar(k) >= stof(v))
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, "==", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+2, -1);
-                               if(cvar(k) != stof(v))
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, "!=", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+2, -1);
-                               if(cvar(k) == stof(v))
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, "===", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+2, -1);
-                               if(cvar_string(k) != v)
-                                       goto cvar_fail;
-                       }
-                       else if((o = strstrofs(s, "!==", 0)) >= 0)
-                       {
-                               k = substring(s, 0, o);
-                               v = substring(s, o+2, -1);
-                               if(cvar_string(k) == v)
-                                       goto cvar_fail;
-                       }
-                       else if(substring(s, 0, 1) == "!")
-                       {
-                               k = substring(s, 1, -1);
-                               if(cvar(k))
-                                       goto cvar_fail;
-                       }
-                       else
-                       {
-                               k = s;
-                               if (!cvar(k))
-                                       goto cvar_fail;
-                       }
-               }
-               inv = !inv;
-LABEL(cvar_fail)
-               // now inv is 1 if we want to keep the item, and 0 if we want to get rid of it
-               if (!inv)
-               {
-                       //print("cvarfilter fail\n");
-                       delete(this);
-                       __spawnfunc_expecting = false;
-                       return;
-               }
+       if (this.cvarfilter != "" && !expr_evaluate(this.cvarfilter)) {
+        goto cleanup;
        }
 
-       if(DoesQ3ARemoveThisEntity(this))
-       {
-               delete(this);
-               __spawnfunc_expecting = false;
-               return;
+       if (DoesQ3ARemoveThisEntity(this)) {
+               goto cleanup;
        }
 
        set_movetype(this, this.movetype);
 
-       if(this.monster_attack)
+       if (this.monster_attack) {
                IL_PUSH(g_monster_targets, this);
+    }
 
        // support special -1 and -2 angle from radiant
-       if (this.angles == '0 -1 0')
+       if (this.angles == '0 -1 0') {
                this.angles = '-90 0 0';
-       else if (this.angles == '0 -2 0')
+       } else if (this.angles == '0 -2 0') {
                this.angles = '+90 0 0';
-
-       if(this.originjitter.x != 0)
-               this.origin_x = this.origin.x + (random() * 2 - 1) * this.originjitter.x;
-       if(this.originjitter.y != 0)
-               this.origin_y = this.origin.y + (random() * 2 - 1) * this.originjitter.y;
-       if(this.originjitter.z != 0)
-               this.origin_z = this.origin.z + (random() * 2 - 1) * this.originjitter.z;
-       if(this.anglesjitter.x != 0)
-               this.angles_x = this.angles.x + (random() * 2 - 1) * this.anglesjitter.x;
-       if(this.anglesjitter.y != 0)
-               this.angles_y = this.angles.y + (random() * 2 - 1) * this.anglesjitter.y;
-       if(this.anglesjitter.z != 0)
-               this.angles_z = this.angles.z + (random() * 2 - 1) * this.anglesjitter.z;
-       if(this.anglejitter != 0)
-               this.angles_y = this.angles.y + (random() * 2 - 1) * this.anglejitter;
-
-       if(MUTATOR_CALLHOOK(OnEntityPreSpawn, this))
-       {
-               delete(this);
-               __spawnfunc_expecting = false;
-               return;
+    }
+
+    #define X(out, in) MACRO_BEGIN \
+        if (in != 0) { out = out + (random() * 2 - 1) * in; } \
+    MACRO_END
+    X(this.origin.x, this.originjitter.x); X(this.origin.y, this.originjitter.y); X(this.origin.z, this.originjitter.z);
+    X(this.angles.x, this.anglesjitter.x); X(this.angles.y, this.anglesjitter.y); X(this.angles.z, this.anglesjitter.z);
+    X(this.angles.y, this.anglejitter);
+    #undef X
+
+       if (MUTATOR_CALLHOOK(OnEntityPreSpawn, this)) {
+               goto cleanup;
        }
+       return;
+LABEL(cleanup)
+    builtin_remove(this);
+    __spawnfunc_expecting = false;
 }
 
 void WarpZone_PostInitialize_Callback()
index 6f70f09beec2219624baeca92e2cd7deaa104fb4..7f86d19c01a47bc83cfd2870c0f0c170a0e93b99 100644 (file)
@@ -1 +1,3 @@
 #pragma once
+
+bool expr_evaluate(string s);
index e93a03201a9ddf4233b229126b69d15bca035de6..4a9145f150262b57c343bab137a7ae5127b31e36 100644 (file)
@@ -43,8 +43,14 @@ void InitGameplayMode()
 
        // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
        get_mi_min_max(1);
-       world.mins = mi_min;
-       world.maxs = mi_max;
+       // assign reflectively to avoid "assignment to world" warning
+       int done = 0; for (int i = 0, n = numentityfields(); i < n; ++i) {
+           string k = entityfieldname(i); vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
+           if (v) {
+            putentityfieldstring(i, world, sprintf("%d %d %d", v));
+            if (++done == 2) break;
+        }
+       }
        // currently, NetRadiant's limit is 131072 qu for each side
        // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
        // set the distance according to map size but don't go over the limit to avoid issues with float precision
index e835fa67135e5145c0c8bd97ace8512c05492b4f..e3d1eb01368df1c714c55d7264eda99e89ff8bbd 100755 (executable)
@@ -74,6 +74,12 @@ function check() {
     done
 }
 
-check client
-check server
-check menu
+if [ ${#@} -eq 0 ]; then
+    check client
+    check server
+    check menu
+else
+    for var in ${@}; do
+        check ${var}
+    done
+fi