From: Martin Taibr Date: Sun, 3 Sep 2017 19:14:34 +0000 (+0200) Subject: Merge branch 'martin-t/okc3' into martin-t/master X-Git-Url: http://git.xonotic.org/?a=commitdiff_plain;h=a660fac7921ee07350df0c54f87e2ebf67c7da9f;hp=f817ef565e2a6e4ba98184e16ff34d6dabc615a7;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'martin-t/okc3' into martin-t/master --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9305f527c..596f61ad1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 '^:' diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e5fc791f..9a66f9fd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 "$/${prog}.dat" "${prog}.dat" diff --git a/defaultOverkill.cfg b/defaultOverkill.cfg index 2444301b9..f63f689f3 100644 --- a/defaultOverkill.cfg +++ b/defaultOverkill.cfg @@ -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 diff --git a/mutators.cfg b/mutators.cfg index a1bf7716c..583f3d597 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -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" diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc index 61b0c0601..1164e0ade 100644 --- a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc +++ b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc @@ -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; } diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index 925525f39..e039a96b9 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc index 52fc52466..987645aaa 100644 --- a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc +++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc index eb45d4baf..a1fe27a87 100644 --- a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc +++ b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qc b/qcsrc/common/mutators/mutator/hook/sv_hook.qc index 5dfdf4386..c39678119 100644 --- a/qcsrc/common/mutators/mutator/hook/sv_hook.qc +++ b/qcsrc/common/mutators/mutator/hook/sv_hook.qc @@ -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) diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc index fefad540b..1561dc10d 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc @@ -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 -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; } diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc index 23e0d0d85..e68c687bd 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc @@ -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) { diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc index a54292122..ac06a8f77 100644 --- a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc +++ b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc @@ -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) { diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qc b/qcsrc/common/mutators/mutator/midair/sv_midair.qc index 92bbacc00..54b3673c6 100644 --- a/qcsrc/common/mutators/mutator/midair/sv_midair.qc +++ b/qcsrc/common/mutators/mutator/midair/sv_midair.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qc b/qcsrc/common/mutators/mutator/multijump/multijump.qc index 47dcfd4af..081a1fdb6 100644 --- a/qcsrc/common/mutators/mutator/multijump/multijump.qc +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qc @@ -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 diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 18edd482d..92e16b48d 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -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 #include -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'; diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc index 288c2d5c8..af364995a 100644 --- a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc @@ -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 { diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc index 425b8c22b..4de24a589 100644 --- a/qcsrc/common/mutators/mutator/nix/sv_nix.qc +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc index d2a7308de..b47e58751 100644 --- a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc @@ -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 { diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc index 62781c920..38cd7474b 100644 --- a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc +++ b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc @@ -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 diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc index 53e4f49e6..1084ff778 100644 --- a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc +++ b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc @@ -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) { diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc index 9f0d8fbf0..d3c1922b9 100644 --- a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc +++ b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc @@ -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) { diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc index 93dc602f0..d121cf109 100644 --- a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc @@ -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 { diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc index 9ff364474..61c302c3e 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc @@ -2,6 +2,7 @@ #include +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); diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc index 4c99095b7..eb2008235 100644 --- a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc +++ b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc @@ -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) diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc index 3e6edb04b..a1b38fb6e 100644 --- a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc +++ b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc index 92c5943c3..199b4e202 100644 --- a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc @@ -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)); } } diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc index e2b0f57d7..ce9e27065 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/walljump/walljump.qc b/qcsrc/common/mutators/mutator/walljump/walljump.qc index b0d95ea29..c462a7e2b 100644 --- a/qcsrc/common/mutators/mutator/walljump/walljump.qc +++ b/qcsrc/common/mutators/mutator/walljump/walljump.qc @@ -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) diff --git a/qcsrc/common/physics/movetypes/movetypes.qh b/qcsrc/common/physics/movetypes/movetypes.qh index dbd765d98..85912ee1c 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qh +++ b/qcsrc/common/physics/movetypes/movetypes.qh @@ -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; diff --git a/qcsrc/common/physics/player.qh b/qcsrc/common/physics/player.qh index d5a8e605a..ae59381e5 100644 --- a/qcsrc/common/physics/player.qh +++ b/qcsrc/common/physics/player.qh @@ -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) diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index 6a6ce3faf..4fc2e3821 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -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; diff --git a/qcsrc/common/t_items.qh b/qcsrc/common/t_items.qh index f557e1030..fa78ff4b3 100644 --- a/qcsrc/common/t_items.qh +++ b/qcsrc/common/t_items.qh @@ -4,9 +4,6 @@ #include #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); diff --git a/qcsrc/common/weapons/all.qc b/qcsrc/common/weapons/all.qc index afbf79eb5..6605f00c2 100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@ -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 diff --git a/qcsrc/common/weapons/projectiles.qh b/qcsrc/common/weapons/projectiles.qh index 73cd00d6c..5a782b98f 100644 --- a/qcsrc/common/weapons/projectiles.qh +++ b/qcsrc/common/weapons/projectiles.qh @@ -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 diff --git a/qcsrc/dpdefs/post.qh b/qcsrc/dpdefs/post.qh index 16fd93450..70e5f3784 100644 --- a/qcsrc/dpdefs/post.qh +++ b/qcsrc/dpdefs/post.qh @@ -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 diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index 4da78f144..9d5820910 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -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(); diff --git a/qcsrc/lib/macro.qh b/qcsrc/lib/macro.qh index 1541b9997..f2ec6df4a 100644 --- a/qcsrc/lib/macro.qh +++ b/qcsrc/lib/macro.qh @@ -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) diff --git a/qcsrc/lib/misc.qh b/qcsrc/lib/misc.qh index 21e0c5239..6c29a4b88 100644 --- a/qcsrc/lib/misc.qh +++ b/qcsrc/lib/misc.qh @@ -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__ diff --git a/qcsrc/lib/self.qh b/qcsrc/lib/self.qh index f4c246f33..0a61cc003 100644 --- a/qcsrc/lib/self.qh +++ b/qcsrc/lib/self.qh @@ -1,5 +1,7 @@ #pragma once +#include "macro.qh" + // Transition from global 'self' to local 'this' // Step 1: auto oldself @@ -9,10 +11,10 @@ // 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) diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index e0605c938..e30e565fd 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -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) diff --git a/qcsrc/lib/stats.qh b/qcsrc/lib/stats.qh index 4642f7640..1100c474c 100644 --- a/qcsrc/lib/stats.qh +++ b/qcsrc/lib/stats.qh @@ -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) diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index 87a8d5689..99115fbdc 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index 2967c110c..3a8898670 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index f585a9a2c..fa8cee717 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -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; diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index f1d417d6b..22543766b 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -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); } diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index a34611cb5..a260fd005 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -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); diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 64aa03b50..c47952c5d 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -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) diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index bc09d7a8c..fd2e19878 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -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 index 000000000..edf4ff162 --- /dev/null +++ b/qcsrc/server/resources.qc @@ -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 index 000000000..ce8e1e8e5 --- /dev/null +++ b/qcsrc/server/resources.qh @@ -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); diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 51890765d..f0f18b826 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -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 + */ +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 - 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() diff --git a/qcsrc/server/sv_main.qh b/qcsrc/server/sv_main.qh index 6f70f09be..7f86d19c0 100644 --- a/qcsrc/server/sv_main.qh +++ b/qcsrc/server/sv_main.qh @@ -1 +1,3 @@ #pragma once + +bool expr_evaluate(string s); diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index e93a03201..4a9145f15 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -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 diff --git a/qcsrc/tools/compilationunits.sh b/qcsrc/tools/compilationunits.sh index e835fa671..e3d1eb013 100755 --- a/qcsrc/tools/compilationunits.sh +++ b/qcsrc/tools/compilationunits.sh @@ -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