From: terencehill Date: Wed, 20 Jun 2018 10:07:20 +0000 (+0200) Subject: Merge branch 'master' into terencehill/bot_ai X-Git-Tag: xonotic-v0.8.5~1923^2~28 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=4096ab0591cbd7fac803e022375cd3c221511d8b;hp=88a5c43608df68db1ca68cb8135cd2f057994c51 Merge branch 'master' into terencehill/bot_ai # Conflicts: # qcsrc/common/gamemodes/gamemode/ctf/ctf.qc --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e50392ca6a..8243f0d9ba 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=033546d32426e6409458fb39d0130f56 + - EXPECT=de6a7d95ce65fb6c66558a93a9fb994f - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg | tee /dev/stderr | grep '^:' diff --git a/.tx/merge-base b/.tx/merge-base index 9745f61361..ca7140deb4 100644 --- a/.tx/merge-base +++ b/.tx/merge-base @@ -1 +1 @@ -Sun Jun 3 07:24:16 CEST 2018 +Wed Jun 20 07:24:25 CEST 2018 diff --git a/_hud_descriptions.cfg b/_hud_descriptions.cfg index 9a1654f83a..30e5a8bee9 100644 --- a/_hud_descriptions.cfg +++ b/_hud_descriptions.cfg @@ -62,7 +62,7 @@ seta hud_panel_weapons_label "" "1 = show number of weapon, 2 = show bound key o seta hud_panel_weapons_label_scale "" "scale of the weapon text label" seta hud_panel_weapons_accuracy "" "show accuracy color as the weapon icon background; colors can be configured with accuracy_color* cvars" seta hud_panel_weapons_ammo "" "show ammo as a status bar" -seta hud_panel_weapons_onlyowned "" "show only owned weapons" +seta hud_panel_weapons_onlyowned "" "show only owned weapons, set it to 2 to show only the held weapon" seta hud_panel_weapons_noncurrent_alpha "" "alpha of noncurrent weapons" seta hud_panel_weapons_noncurrent_scale "" "scale of noncurrent weapons, relative to the current weapon" seta hud_panel_weapons_selection_radius "" "number of weapons that get partially highlighted on each side of the currently selected weapon" diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg index 9ed423c1c9..d2ff12f6bc 100644 --- a/bal-wep-mario.cfg +++ b/bal-wep-mario.cfg @@ -256,13 +256,13 @@ set g_balance_crylink_primary_spread 0.08 set g_balance_crylink_reload_ammo 0 set g_balance_crylink_reload_time 2 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_damage 8 -set g_balance_crylink_secondary_edgedamage 4 -set g_balance_crylink_secondary_force -200 +set g_balance_crylink_secondary_damage 50 +set g_balance_crylink_secondary_edgedamage 15 +set g_balance_crylink_secondary_force -400 set g_balance_crylink_secondary_joindelay 0 set g_balance_crylink_secondary_joinexplode 0 set g_balance_crylink_secondary_joinexplode_damage 0 @@ -275,11 +275,11 @@ set g_balance_crylink_secondary_middle_fadetime 5 set g_balance_crylink_secondary_middle_lifetime 5 set g_balance_crylink_secondary_other_fadetime 5 set g_balance_crylink_secondary_other_lifetime 5 -set g_balance_crylink_secondary_radius 100 -set g_balance_crylink_secondary_refire 0.7 -set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_radius 70 +set g_balance_crylink_secondary_refire 0.8 +set g_balance_crylink_secondary_shots 1 set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spread 0 set g_balance_crylink_secondary_spreadtype 1 set g_balance_crylink_switchdelay_drop 0.2 set g_balance_crylink_switchdelay_raise 0.2 @@ -459,7 +459,7 @@ set g_balance_vaporizer_switchdelay_raise 0.2 set g_balance_vaporizer_weaponreplace "" set g_balance_vaporizer_weaponstart 0 set g_balance_vaporizer_weaponstartoverride -1 -set g_balance_vaporizer_weaponthrowable 0 +set g_balance_vaporizer_weaponthrowable 1 // }}} // {{{ #13: Grappling Hook set g_balance_hook_primary_ammo 5 @@ -753,7 +753,7 @@ set g_balance_arc_beam_heat 0 set g_balance_arc_burst_heat 5 set g_balance_arc_beam_maxangle 10 set g_balance_arc_beam_nonplayerdamage 80 -set g_balance_arc_beam_range 1500 +set g_balance_arc_beam_range 1250 set g_balance_arc_beam_refire 0.25 set g_balance_arc_beam_returnspeed 8 set g_balance_arc_beam_tightness 0.5 diff --git a/commands.cfg b/commands.cfg index 0e765b8a13..b2edf84788 100644 --- a/commands.cfg +++ b/commands.cfg @@ -212,7 +212,6 @@ alias lockteams "qc_cmd_sv lockteams ${* ?}" // Disabl alias make_mapinfo "qc_cmd_sv make_mapinfo ${* ?}" // Automatically rebuild mapinfo files alias moveplayer "qc_cmd_sv moveplayer ${* ?}" // Change the team/status of a player alias nospectators "qc_cmd_sv nospectators ${* ?}" // Automatically remove spectators from a match -alias playerdemo "qc_cmd_sv playerdemo ${* ?}" // Control the ability to save demos of players alias printstats "qc_cmd_sv printstats ${* ?}" // Dump eventlog player stats and other score information alias radarmap "qc_cmd_sv radarmap ${* ?}" // Generate a radar image of the map alias reducematchtime "qc_cmd_sv reducematchtime ${* ?}" // Decrease the timelimit value incrementally diff --git a/common.ko.po b/common.ko.po index 0407c66068..4d65b27123 100644 --- a/common.ko.po +++ b/common.ko.po @@ -4,9 +4,10 @@ # # Translators: # Jisoo Lim , 2017 -# Kyf Lee (coughingmouse) , 2016 -# Kyf Lee (coughingmouse) , 2016-2017 -# Kyf Lee (coughingmouse) , 2016-2017 +# Kyf Lee , 2016 +# Kyf Lee , 2016-2017 +# Kyf Lee , 2016-2017 +# Kyf Lee , 2016 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" diff --git a/gamemodes-server.cfg b/gamemodes-server.cfg index 7319cba4e8..41b5fc5dbe 100644 --- a/gamemodes-server.cfg +++ b/gamemodes-server.cfg @@ -478,7 +478,7 @@ seta g_nexball_tackling 1 "Allow ball theft?" set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it" set g_onslaught_point_limit 1 "Onslaught point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" set g_onslaught_warmup 5 -set g_onslaught_round_timelimit 280 +set g_onslaught_round_timelimit 500 set g_onslaught_teleport_radius 200 "Allows teleporting from a control point to another" set g_onslaught_teleport_wait 5 "Time before player can teleport again" set g_onslaught_spawn_choose 1 "Allow players to choose the control point to be spawned at" diff --git a/mutators.cfg b/mutators.cfg index a4c8144fc2..afa1782498 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -294,7 +294,7 @@ set g_campcheck_distance 1800 // ========== set g_new_toys 0 "Mutator 'New Toys': enable extra fun guns" set g_new_toys_autoreplace 2 "0: never replace, 1: always auto replace guns by available new toys, 2: randomly auto replace guns by available new toys" -set g_new_toys_use_pickupsound 1 "play the 'new toys, new toys!' roflsound when picking up a new toys weapon" +set g_new_toys_use_pickupsound 0 "play the 'new toys, new toys!' roflsound when picking up a new toys weapon" // ======= diff --git a/qcsrc/client/_mod.inc b/qcsrc/client/_mod.inc index aa20961a2b..ab9184b9b9 100644 --- a/qcsrc/client/_mod.inc +++ b/qcsrc/client/_mod.inc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/client/_mod.qh b/qcsrc/client/_mod.qh index ecfa4ee1a6..971cc01de6 100644 --- a/qcsrc/client/_mod.qh +++ b/qcsrc/client/_mod.qh @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index bcbe724469..0195db43a4 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -129,6 +129,9 @@ void Announcer_Gamestart() void Announcer_Time() { + if(intermission) + return; + float timeleft; if(warmup_stage) { diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index b6b33c3b17..8eb3ca1dcb 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -344,7 +344,7 @@ float autocvar_hud_panel_weapons_complainbubble_padding; float autocvar_hud_panel_weapons_complainbubble_time; int autocvar_hud_panel_weapons_label; float autocvar_hud_panel_weapons_label_scale = 0.5; -bool autocvar_hud_panel_weapons_onlyowned; +int autocvar_hud_panel_weapons_onlyowned; float autocvar_hud_panel_weapons_noncurrent_alpha = 1; float autocvar_hud_panel_weapons_noncurrent_scale = 1; float autocvar_hud_panel_weapons_selection_radius = 0; diff --git a/qcsrc/client/hud/hud.qh b/qcsrc/client/hud/hud.qh index 950dee17ad..68d1e6bffa 100644 --- a/qcsrc/client/hud/hud.qh +++ b/qcsrc/client/hud/hud.qh @@ -86,8 +86,8 @@ const float BORDER_MULTIPLIER = 4; float scoreboard_bottom; int weapon_accuracy[Weapons_MAX]; -int complain_weapon; -float complain_weapon_type; +entity complain_weapon; +int complain_weapon_type; float complain_weapon_time; PlayerScoreField ps_primary, ps_secondary; diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc index f0ec01c9e7..bd94520d4e 100644 --- a/qcsrc/client/hud/panel/radar.qc +++ b/qcsrc/client/hud/panel/radar.qc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -352,7 +353,7 @@ void HUD_Radar() IL_EACH(g_radaricons, it.teamradar_icon, { if ( hud_panel_radar_mouse ) - if ( it.health >= 0 ) + if ( GetResourceAmount(it, RESOURCE_HEALTH) >= 0 ) if ( it.team == myteam + 1 || gametype == MAPINFO_TYPE_RACE || !teamplay ) { vector coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(it.origin)); diff --git a/qcsrc/client/hud/panel/weapons.qc b/qcsrc/client/hud/panel/weapons.qc index 4506f69a0c..5d75c7dd2a 100644 --- a/qcsrc/client/hud/panel/weapons.qc +++ b/qcsrc/client/hud/panel/weapons.qc @@ -108,7 +108,14 @@ void HUD_Weapons() } if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime) - complain_weapon = 0; + complain_weapon = NULL; + + entity wepent = viewmodels[0]; // TODO: unhardcode + + if (wepent.switchweapon == WEP_Null) + panel_switchweapon = NULL; + else if (!panel_switchweapon) + panel_switchweapon = wepent.switchweapon; if(autocvar__hud_configure) { @@ -159,10 +166,18 @@ void HUD_Weapons() // do we own this weapon? weapon_count = 0; - for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) - if((weapons_stat & WepSet_FromWeapon(weaponorder[i])) || (weaponorder[i].m_id == complain_weapon)) - ++weapon_count; - + if (autocvar_hud_panel_weapons_onlyowned >= 2) // only current + { + for (i = 0; i <= WEP_LAST-WEP_FIRST; ++i) + if (weaponorder[i] == panel_switchweapon || weaponorder[i] == complain_weapon) + ++weapon_count; + } + else + { + for (i = 0; i <= WEP_LAST-WEP_FIRST; ++i) + if ((weapons_stat & WepSet_FromWeapon(weaponorder[i])) || weaponorder[i] == complain_weapon) + ++weapon_count; + } // might as well commit suicide now, no reason to live ;) if (weapon_count == 0) @@ -373,13 +388,6 @@ void HUD_Weapons() switch_speed = frametime * autocvar_hud_panel_weapons_selection_speed; vector radius_size = weapon_size * (autocvar_hud_panel_weapons_selection_radius + 1); - entity wepent = viewmodels[0]; // TODO: unhardcode - - if(wepent.switchweapon == WEP_Null) - panel_switchweapon = NULL; - else if(!panel_switchweapon) - panel_switchweapon = wepent.switchweapon; - // draw background behind currently selected weapon // do it earlier to make sure bg is drawn behind every weapon icons while it's moving if(panel_switchweapon) @@ -395,10 +403,18 @@ void HUD_Weapons() if(!it || weapon_id < 0) { continue; } // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon - if(autocvar_hud_panel_weapons_onlyowned) + if (autocvar_hud_panel_weapons_onlyowned) { - if (!((weapons_stat & WepSet_FromWeapon(it)) || (it.m_id == complain_weapon))) - continue; + if (autocvar_hud_panel_weapons_onlyowned >= 2) // only current + { + if (!(it == panel_switchweapon || it == complain_weapon)) + continue; + } + else + { + if (!((weapons_stat & WepSet_FromWeapon(it)) || (it == complain_weapon))) + continue; + } } else { @@ -518,7 +534,7 @@ void HUD_Weapons() } // draw the complain message - if(it.m_id == complain_weapon) + if(it == complain_weapon) { if(fadetime) a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1)); diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 3de8cefeb6..218df18a47 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -1216,7 +1216,8 @@ NET_HANDLE(TE_CSQC_PINGPLREPORT, bool isNew) NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) { - complain_weapon = ReadByte(); + int weapon_id = ReadByte(); + complain_weapon = Weapons_from(weapon_id); complain_weapon_type = ReadByte(); return = true; @@ -1225,9 +1226,9 @@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) switch(complain_weapon_type) { - case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, complain_weapon); break; - case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, complain_weapon); break; - default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, complain_weapon); break; + case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, weapon_id); break; + case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, weapon_id); break; + default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, weapon_id); break; } } diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index a95acd5739..69c3fa3d2b 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -114,7 +114,6 @@ const int MAX_SPECTATORS = 7; int spectatorlist[MAX_SPECTATORS]; int framecount; -.float health; float GetSpeedUnitFactor(int speed_unit); string GetSpeedUnit(int speed_unit); diff --git a/qcsrc/client/miscfunctions.qh b/qcsrc/client/miscfunctions.qh index f23a3976b5..0143d1a013 100644 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@ -34,7 +34,7 @@ float PreviewExists(string name); vector Rotate(vector v, float a); -#define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : ((s).health <= 0)) +#define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : (GetResourceAmount((s), RESOURCE_HEALTH) <= 0)) // decolorizes and team colors the player name when needed diff --git a/qcsrc/client/resources.qc b/qcsrc/client/resources.qc new file mode 100644 index 0000000000..285ebad639 --- /dev/null +++ b/qcsrc/client/resources.qc @@ -0,0 +1,87 @@ +#include "resources.qh" +#include + +/// \file +/// \brief Source file that contains implementation of the resource system. +/// \copyright GNU GPLv2 or any later version. + +float GetResourceAmount(entity e, int resource_type) +{ + .float resource_field = GetResourceField(resource_type); + return e.(resource_field); +} + +bool SetResourceAmountExplicit(entity e, int resource_type, float amount) +{ + .float resource_field = GetResourceField(resource_type); + if (e.(resource_field) != amount) + { + e.(resource_field) = amount; + return true; + } + return false; +} + +void SetResourceAmount(entity e, int resource_type, float amount) +{ + SetResourceAmountExplicit(e, resource_type, amount); +} + +void TakeResource(entity receiver, int resource_type, float amount) +{ + if (amount == 0) + { + return; + } + SetResourceAmount(receiver, resource_type, + GetResourceAmount(receiver, resource_type) - amount); +} + +void TakeResourceWithLimit(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; + } + TakeResource(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/client/resources.qh b/qcsrc/client/resources.qh new file mode 100644 index 0000000000..3aaa8aab59 --- /dev/null +++ b/qcsrc/client/resources.qh @@ -0,0 +1,61 @@ +#pragma once + +/// \file +/// \brief Header file that describes the resource system. +/// \copyright GNU GPLv2 or any later version. + +#include + +// ============================ Public API ==================================== + +/// \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 resource amount of an entity without calling any hooks. +/// \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 Boolean for whether the ammo amount was changed +bool SetResourceAmountExplicit(entity e, int resource_type, float amount); + +/// \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 Takes an entity some resource. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \return No return. +void TakeResource(entity receiver, int resource_type, float amount); + +/// \brief Takes an entity some resource but not less than a limit. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \param[in] limit Limit of resources to take. +/// \return No return. +void TakeResourceWithLimit(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); + +/// \brief Legacy fields for the resources. To be removed. +.float health; +.float armorvalue; diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 8a7d225bff..7c1ece5a3d 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -2,6 +2,7 @@ #include "autocvars.qh" #include "miscfunctions.qh" +#include "resources.qh" #include "hud/_mod.qh" #include @@ -157,10 +158,10 @@ void Draw_ShowNames(entity this) this.healthvalue / autocvar_hud_panel_healtharmor_maxhealth, false, 1, '1 0 0', a, DRAWFLAG_NORMAL); } - if (this.armorvalue > 0) + if (GetResourceAmount(this, RESOURCE_ARMOR) > 0) { HUD_Panel_DrawProgressBar(pos + eX * 0.5 * mySize.x, sz, "nametag_statusbar", - this.armorvalue / autocvar_hud_panel_healtharmor_maxarmor, false, 0, '0 1 0', a, + GetResourceAmount(this, RESOURCE_ARMOR) / autocvar_hud_panel_healtharmor_maxarmor, false, 0, '0 1 0', a, DRAWFLAG_NORMAL); } } @@ -193,13 +194,13 @@ void Draw_ShowNames_All() if (entcs.m_entcs_private) { it.healthvalue = entcs.healthvalue; - it.armorvalue = entcs.armorvalue; + SetResourceAmountExplicit(it, RESOURCE_ARMOR, GetResourceAmount(entcs, RESOURCE_ARMOR)); it.sameteam = true; } else { it.healthvalue = 0; - it.armorvalue = 0; + SetResourceAmountExplicit(it, RESOURCE_ARMOR, 0); it.sameteam = false; } bool dead = entcs_IsDead(i) || entcs_IsSpectating(i); diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index d036bd75df..4d355fb2a1 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -2057,7 +2057,7 @@ void CSQC_UpdateView(entity this, float w, float h) IL_EACH(g_drawables, it.draw, it.draw(it)); - addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); + addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); // TODO: .health is used in cl_deathfade (a feature we have turned off currently) renderscene(); // now switch to 2D drawing mode by calling a 2D drawing function diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh index 983b073b40..41c5d3017e 100644 --- a/qcsrc/common/debug.qh +++ b/qcsrc/common/debug.qh @@ -1,5 +1,9 @@ #pragma once +#ifdef CSQC +#include +#endif + // This includes some functions useful for debugging. // Some more bot-specific ones are in server/pathlib/debug.qc. @@ -403,7 +407,7 @@ CLASS(DebugText3d, Object) CONSTRUCT(DebugText3d); this.origin = pos; this.message = strzone(msg); - this.health = align; + SetResourceAmount(this, RESOURCE_HEALTH, align); this.hit_time = time; this.fade_rate = fade_rate_; this.velocity = vel; @@ -425,7 +429,7 @@ CLASS(DebugText3d, Object) int size = 8; vector screen_pos = project_3d_to_2d(this.origin) + since_created * this.velocity; - float align = this.health; + float align = GetResourceAmount(this, RESOURCE_HEALTH); if (align > 0) screen_pos.x -= stringwidth(this.message, true, size * '1 1 0') * min(1, align); if (screen_pos.z < 0) return; // behind camera diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index bbca691add..86acdc1540 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -1,5 +1,9 @@ #include "ent_cs.qh" #include +#include +#ifdef SVQC +#include +#endif REGISTRY(EntCSProps, BITS(16) - 1) #define EntCSProps_from(i) _EntCSProps_from(i, NULL) @@ -34,6 +38,26 @@ STATIC_INIT(RegisterEntCSProps_renumber) { FOREACH(EntCSProps, true, it.m_id = i } #endif +#ifdef SVQC +#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, svsend, clreceive) \ + bool id##_check(entity ent, entity player) { return (GetResourceAmount(ent, checkprop) != GetResourceAmount(player, checkprop)); } \ + void id##_set(entity ent, entity player) { SetResourceAmountExplicit(ent, checkprop, GetResourceAmount(player, checkprop)); } \ + void id##_send(int chan, entity ent) { LAMBDA(svsend); } \ + REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \ + this.m_public = ispublic; \ + this.m_check = id##_check; \ + this.m_set = id##_set; \ + this.m_send = id##_send; \ + } +#elif defined(CSQC) +#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, svsend, clreceive) \ + void id##_receive(entity ent) { LAMBDA(clreceive); } \ + REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \ + this.m_public = ispublic; \ + this.m_receive = id##_receive; \ + } +#endif + #define ENTCS_SET_NORMAL(var, x) MACRO_BEGIN \ var = x; \ MACRO_END @@ -53,13 +77,13 @@ ENTCS_PROP(ANGLES, false, angles_y, ENTCS_SET_NORMAL, { WriteByte(chan, ent.angles.y / 360 * 256); }, { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; ent.angles = v; }) -ENTCS_PROP(HEALTH, false, health, ENTCS_SET_NORMAL, - { WriteByte(chan, bound(0, ent.health / 10, 255)); /* FIXME: use a better scale? */ }, +ENTCS_PROP_RESOURCE(HEALTH, false, RESOURCE_HEALTH, ENTCS_SET_NORMAL, + { WriteByte(chan, bound(0, GetResourceAmount(ent, RESOURCE_HEALTH) / 10, 255)); /* FIXME: use a better scale? */ }, { ent.healthvalue = ReadByte() * 10; }) -ENTCS_PROP(ARMOR, false, armorvalue, ENTCS_SET_NORMAL, - { WriteByte(chan, bound(0, ent.armorvalue / 10, 255)); /* FIXME: use a better scale? */ }, - { ent.armorvalue = ReadByte() * 10; }) +ENTCS_PROP_RESOURCE(ARMOR, false, RESOURCE_ARMOR, ENTCS_SET_NORMAL, + { WriteByte(chan, bound(0, GetResourceAmount(ent, RESOURCE_ARMOR) / 10, 255)); /* FIXME: use a better scale? */ }, + { SetResourceAmountExplicit(ent, RESOURCE_ARMOR, ReadByte() * 10); }) ENTCS_PROP(NAME, true, netname, ENTCS_SET_MUTABLE_STRING, { WriteString(chan, ent.netname); }, diff --git a/qcsrc/common/gamemodes/gamemode/assault/assault.qc b/qcsrc/common/gamemodes/gamemode/assault/assault.qc index 1a9fff10cb..82af283d63 100644 --- a/qcsrc/common/gamemodes/gamemode/assault/assault.qc +++ b/qcsrc/common/gamemodes/gamemode/assault/assault.qc @@ -19,7 +19,7 @@ STATIC_INIT(g_assault) void assault_objective_use(entity this, entity actor, entity trigger) { // activate objective - this.health = 100; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); //print("^2Activated objective ", this.targetname, "=", etos(this), "\n"); //print("Activator is ", actor.classname, "\n"); @@ -31,7 +31,7 @@ void assault_objective_use(entity this, entity actor, entity trigger) vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current) { - if(this.health < 0 || this.health >= ASSAULT_VALUE_INACTIVE) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 0 || GetResourceAmount(this, RESOURCE_HEALTH) >= ASSAULT_VALUE_INACTIVE) return '-1 0 0'; return current; } @@ -40,7 +40,7 @@ vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, // and when a new round starts void assault_objective_reset(entity this) { - this.health = ASSAULT_VALUE_INACTIVE; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ASSAULT_VALUE_INACTIVE); } // decrease the health of targeted objectives @@ -61,18 +61,18 @@ void assault_objective_decrease_use(entity this, entity actor, entity trigger) else return; // already activated! cannot activate again! - if(this.enemy.health < ASSAULT_VALUE_INACTIVE) + if(GetResourceAmount(this.enemy, RESOURCE_HEALTH) < ASSAULT_VALUE_INACTIVE) { - if(this.enemy.health - this.dmg > 0.5) + if(GetResourceAmount(this.enemy, RESOURCE_HEALTH) - this.dmg > 0.5) { GameRules_scoring_add_team(actor, SCORE, this.dmg); - this.enemy.health = this.enemy.health - this.dmg; + TakeResource(this.enemy, RESOURCE_HEALTH, this.dmg); } else { - GameRules_scoring_add_team(actor, SCORE, this.enemy.health); + GameRules_scoring_add_team(actor, SCORE, GetResourceAmount(this.enemy, RESOURCE_HEALTH)); GameRules_scoring_add_team(actor, ASSAULT_OBJECTIVES, 1); - this.enemy.health = -1; + SetResourceAmountExplicit(this.enemy, RESOURCE_HEALTH, -1); if(this.enemy.message) FOREACH_CLIENT(IS_PLAYER(it), { centerprint(it, this.enemy.message); }); @@ -99,7 +99,7 @@ void assault_setenemytoobjective(entity this) bool assault_decreaser_sprite_visible(entity this, entity player, entity view) { - if(this.assault_decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE) + if(GetResourceAmount(this.assault_decreaser.enemy, RESOURCE_HEALTH) >= ASSAULT_VALUE_INACTIVE) return false; return true; @@ -127,7 +127,7 @@ void target_objective_decrease_activate(entity this) { WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultDestroy, WP_AssaultDestroy); WaypointSprite_UpdateMaxHealth(spr, it.max_health); - WaypointSprite_UpdateHealth(spr, it.health); + WaypointSprite_UpdateHealth(spr, GetResourceAmount(it, RESOURCE_HEALTH)); it.sprite = spr; } else @@ -176,7 +176,7 @@ void assault_roundstart_use_this(entity this) void assault_wall_think(entity this) { - if(this.enemy.health < 0) + if(GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 0) { this.model = ""; this.solid = SOLID_NOT; @@ -323,7 +323,7 @@ spawnfunc(target_objective_decrease) this.dmg = 101; this.use = assault_objective_decrease_use; - this.health = ASSAULT_VALUE_INACTIVE; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ASSAULT_VALUE_INACTIVE); this.max_health = ASSAULT_VALUE_INACTIVE; this.enemy = NULL; @@ -331,6 +331,21 @@ spawnfunc(target_objective_decrease) } // destructible walls that can be used to trigger target_objective_decrease +bool destructible_heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + if(targ.sprite) + { + WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH)); + } + func_breakable_colormod(targ); + return true; +} + spawnfunc(func_breakable); spawnfunc(func_assault_destructible) { @@ -338,6 +353,7 @@ spawnfunc(func_assault_destructible) this.spawnflags = 3; this.classname = "func_assault_destructible"; + this.event_heal = destructible_heal; IL_PUSH(g_assault_destructibles, this); if(assault_attacker_team == NUM_TEAM_1) @@ -395,7 +411,7 @@ void havocbot_goalrating_ast_targets(entity this, float ratingscale) entity destr = it; IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target, { - if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE) + if(GetResourceAmount(it.enemy, RESOURCE_HEALTH) > 0 && GetResourceAmount(it.enemy, RESOURCE_HEALTH) < ASSAULT_VALUE_INACTIVE) { found = true; break; diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/clanarena.qc b/qcsrc/common/gamemodes/gamemode/clanarena/clanarena.qc index 561129c7db..f4aea4357d 100644 --- a/qcsrc/common/gamemodes/gamemode/clanarena/clanarena.qc +++ b/qcsrc/common/gamemodes/gamemode/clanarena/clanarena.qc @@ -96,7 +96,7 @@ float CA_CheckWinner() void CA_RoundStart() { - allowed_to_spawn = boolean(warmup_stage); + allowed_to_spawn = boolean(warmup_stage); } bool CA_CheckTeams() @@ -164,7 +164,7 @@ entity CA_SpectateNext(entity player, entity start) MUTATOR_HOOKFUNCTION(ca, PlayerSpawn) { - entity player = M_ARGV(0, entity); + entity player = M_ARGV(0, entity); player.caplayer = 1; if (!warmup_stage) @@ -220,7 +220,7 @@ MUTATOR_HOOKFUNCTION(ca, reset_map_players) MUTATOR_HOOKFUNCTION(ca, ClientConnect) { - entity player = M_ARGV(0, entity); + entity player = M_ARGV(0, entity); TRANSMUTE(Observer, player); return true; @@ -253,8 +253,7 @@ entity ca_LastPlayerForTeam(entity this) void ca_LastPlayerForTeam_Notify(entity this) { - if (round_handler_IsActive()) - if (round_handler_IsRoundStarted()) + if (!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) { entity pl = ca_LastPlayerForTeam(this); if (pl) @@ -275,15 +274,17 @@ MUTATOR_HOOKFUNCTION(ca, PlayerDies) } frag_target.respawn_flags |= RESPAWN_FORCE; if (!warmup_stage) + { eliminatedPlayers.SendFlags |= 1; - if(IS_BOT_CLIENT(frag_target)) - bot_clear(frag_target); + if (IS_BOT_CLIENT(frag_target)) + bot_clear(frag_target); + } return true; } MUTATOR_HOOKFUNCTION(ca, ClientDisconnect) { - entity player = M_ARGV(0, entity); + entity player = M_ARGV(0, entity); if (player.caplayer == 1) ca_LastPlayerForTeam_Notify(player); @@ -292,7 +293,7 @@ MUTATOR_HOOKFUNCTION(ca, ClientDisconnect) MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver) { - entity player = M_ARGV(0, entity); + entity player = M_ARGV(0, entity); if (!IS_DEAD(player)) ca_LastPlayerForTeam_Notify(player); @@ -352,7 +353,7 @@ MUTATOR_HOOKFUNCTION(ca, Damage_Calculate) MUTATOR_HOOKFUNCTION(ca, FilterItem) { - entity item = M_ARGV(0, entity); + entity item = M_ARGV(0, entity); if (autocvar_g_powerups <= 0) if (item.flags & FL_POWERUP) @@ -396,8 +397,8 @@ MUTATOR_HOOKFUNCTION(ca, Scores_CountFragsRemaining) MUTATOR_HOOKFUNCTION(ca, SpectateSet) { - entity client = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); + entity client = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); if (!autocvar_g_ca_spectate_enemies && client.caplayer) if (DIFF_TEAM(targ, client)) @@ -406,7 +407,7 @@ MUTATOR_HOOKFUNCTION(ca, SpectateSet) MUTATOR_HOOKFUNCTION(ca, SpectateNext) { - entity client = M_ARGV(0, entity); + entity client = M_ARGV(0, entity); if (!autocvar_g_ca_spectate_enemies && client.caplayer) { @@ -418,9 +419,9 @@ MUTATOR_HOOKFUNCTION(ca, SpectateNext) MUTATOR_HOOKFUNCTION(ca, SpectatePrev) { - entity client = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); - entity first = M_ARGV(2, entity); + entity client = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); + entity first = M_ARGV(2, entity); if (!autocvar_g_ca_spectate_enemies && client.caplayer) { @@ -453,7 +454,7 @@ MUTATOR_HOOKFUNCTION(ca, Bot_FixCount, CBC_ORDER_EXCLUSIVE) MUTATOR_HOOKFUNCTION(ca, ClientCommand_Spectate) { - entity player = M_ARGV(0, entity); + entity player = M_ARGV(0, entity); if (player.caplayer) { diff --git a/qcsrc/common/gamemodes/gamemode/ctf/ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/ctf.qc index 92f64080e6..25412be2a6 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/ctf.qc +++ b/qcsrc/common/gamemodes/gamemode/ctf/ctf.qc @@ -147,7 +147,7 @@ void ctf_FlagcarrierWaypoints(entity player) { WaypointSprite_Spawn(WP_FlagCarrier, 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, player.team, player, wps_flagcarrier, true, RADARICON_FLAG); WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2); - WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); + WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(GetResourceAmount(player, RESOURCE_HEALTH), GetResourceAmount(player, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team)); if(player.flagcarried && CTF_SAMETEAM(player, player.flagcarried)) @@ -343,7 +343,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype) set_movetype(flag, MOVETYPE_TOSS); flag.takedamage = DAMAGE_YES; flag.angles = '0 0 0'; - flag.health = flag.max_flag_health; + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); flag.ctf_droptime = time; flag.ctf_dropper = player; flag.ctf_status = FLAG_DROPPED; @@ -366,7 +366,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype) if(autocvar_g_ctf_flag_return_time || (autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health)) { WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health); - WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); + WaypointSprite_UpdateHealth(flag.wps_flagdropped, GetResourceAmount(flag, RESOURCE_HEALTH)); } player.throw_antispam = time + autocvar_g_ctf_pass_wait; @@ -680,7 +680,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) switch(pickuptype) { case PICKUP_BASE: flag.ctf_pickuptime = time; break; // used for timing runs - case PICKUP_DROPPED: flag.health = flag.max_flag_health; break; // reset health/return timelimit + case PICKUP_DROPPED: SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); break; // reset health/return timelimit default: break; } @@ -762,9 +762,9 @@ void ctf_CheckFlagReturn(entity flag, int returntype) { if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING)) { - if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); } + if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, GetResourceAmount(flag, RESOURCE_HEALTH)); } - if((flag.health <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time)) + if((GetResourceAmount(flag, RESOURCE_HEALTH) <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time)) { switch(returntype) { @@ -875,7 +875,7 @@ void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage this.ctf_flagdamaged_byworld = true; else { - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); ctf_CheckFlagReturn(this, RETURN_NEEDKILL); } return; @@ -883,7 +883,7 @@ void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage if(autocvar_g_ctf_flag_return_damage) { // reduce health and check if it should be returned - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); ctf_CheckFlagReturn(this, RETURN_DAMAGE); return; } @@ -946,20 +946,20 @@ void ctf_FlagThink(entity this) { if((vdist(this.origin - this.ctf_spawnorigin, <=, autocvar_g_ctf_flag_return_dropped)) || (autocvar_g_ctf_flag_return_dropped == -1)) { - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); ctf_CheckFlagReturn(this, RETURN_DROPPED); return; } } if(this.ctf_flagdamaged_byworld) { - this.health -= ((this.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE); + TakeResource(this, RESOURCE_HEALTH, ((this.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE)); ctf_CheckFlagReturn(this, RETURN_NEEDKILL); return; } else if(autocvar_g_ctf_flag_return_time) { - this.health -= ((this.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE); + TakeResource(this, RESOURCE_HEALTH, ((this.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE)); ctf_CheckFlagReturn(this, RETURN_TIMEOUT); return; } @@ -970,7 +970,7 @@ void ctf_FlagThink(entity this) { if(this.speedrunning && ctf_captimerecord && (time >= this.ctf_pickuptime + ctf_captimerecord)) { - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); ctf_CheckFlagReturn(this, RETURN_SPEEDRUN); CS(this.owner).impulse = CHIMPULSE_SPEEDRUN.impulse; // move the player back to the waypoint they set @@ -1039,7 +1039,7 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) { if(!autocvar_g_ctf_flag_return_damage_delay) { - flag.health = 0; + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, 0); ctf_CheckFlagReturn(flag, RETURN_NEEDKILL); } if(!flag.ctf_flagdamaged_byworld) { return; } @@ -1163,7 +1163,7 @@ void ctf_RespawnFlag(entity flag) set_movetype(flag, ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS)); flag.takedamage = DAMAGE_NO; - flag.health = flag.max_flag_health; + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); flag.solid = SOLID_TRIGGER; flag.velocity = '0 0 0'; flag.angles = flag.mangle; @@ -1250,7 +1250,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e flag.takedamage = DAMAGE_NO; flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale; flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100); - flag.health = flag.max_flag_health; + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); flag.event_damage = ctf_FlagDamage; flag.pushable = true; flag.teleportable = TELEPORT_NORMAL; @@ -2167,7 +2167,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink) // update the health of the flag carrier waypointsprite if(player.wps_flagcarrier) - WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); + WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(GetResourceAmount(player, RESOURCE_HEALTH), GetResourceAmount(player, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); } MUTATOR_HOOKFUNCTION(ctf, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc @@ -2195,7 +2195,7 @@ MUTATOR_HOOKFUNCTION(ctf, Damage_Calculate) // for changing damage and force val } else if(frag_target.flagcarried && !IS_DEAD(frag_target) && CTF_DIFFTEAM(frag_target, frag_attacker)) // if the target is a flagcarrier { - if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id))) + if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(GetResourceAmount(frag_target, RESOURCE_HEALTH), GetResourceAmount(frag_target, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id))) if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time) { frag_target.wps_helpme_time = time; diff --git a/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh b/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh index 74a3993e35..2f9643b4a4 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh +++ b/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh @@ -51,7 +51,8 @@ const float VEHICLE_FLAG_SCALE = 1.0; // waypoint colors #define WPCOLOR_ENEMYFC(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') -#define WPCOLOR_FLAGCARRIER(t) (WP_FlagCarrier.m_color) +#define WPCOLOR_FLAGCARRIER(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') +//#define WPCOLOR_FLAGCARRIER(t) (WP_FlagCarrier.m_color) #define WPCOLOR_DROPPEDFLAG(t) ((t) ? ('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5 : '1 1 1') // sounds diff --git a/qcsrc/common/gamemodes/gamemode/cts/cts.qc b/qcsrc/common/gamemodes/gamemode/cts/cts.qc index 12319c26cd..6190289347 100644 --- a/qcsrc/common/gamemodes/gamemode/cts/cts.qc +++ b/qcsrc/common/gamemodes/gamemode/cts/cts.qc @@ -74,7 +74,7 @@ void CTS_ClientKill(entity e) // silent version of ClientKill, used when player setthink(e.killindicator, KillIndicator_Think); e.killindicator.nextthink = time + (e.lip) * 0.05; e.killindicator.cnt = ceil(autocvar_g_cts_finish_kill_delay); - e.killindicator.health = 1; // this is used to indicate that it should be silent + e.killindicator.count = 1; // this is used to indicate that it should be silent e.lip = 0; } @@ -386,7 +386,7 @@ MUTATOR_HOOKFUNCTION(cts, ClientKill) M_ARGV(1, float) = 0; // kill delay - if(player.killindicator && player.killindicator.health == 1) // player.killindicator.health == 1 means that the kill indicator was spawned by CTS_ClientKill + if(player.killindicator && player.killindicator.count == 1) // player.killindicator.count == 1 means that the kill indicator was spawned by CTS_ClientKill { delete(player.killindicator); player.killindicator = NULL; diff --git a/qcsrc/common/gamemodes/gamemode/domination/domination.qc b/qcsrc/common/gamemodes/gamemode/domination/domination.qc index 067d5c0bfe..7575f94647 100644 --- a/qcsrc/common/gamemodes/gamemode/domination/domination.qc +++ b/qcsrc/common/gamemodes/gamemode/domination/domination.qc @@ -188,9 +188,9 @@ void dompointthink(entity this) void dompointtouch(entity this, entity toucher) { - if (!IS_PLAYER(toucher)) + if(!IS_PLAYER(toucher)) return; - if (toucher.health < 1) + if(GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) return; if(round_handler_IsActive() && !round_handler_IsRoundStarted()) diff --git a/qcsrc/common/gamemodes/gamemode/freezetag/freezetag.qc b/qcsrc/common/gamemodes/gamemode/freezetag/freezetag.qc index 50e3a815f4..9205e89754 100644 --- a/qcsrc/common/gamemodes/gamemode/freezetag/freezetag.qc +++ b/qcsrc/common/gamemodes/gamemode/freezetag/freezetag.qc @@ -2,6 +2,8 @@ // TODO: sv_freezetag #ifdef SVQC +#include + float autocvar_g_freezetag_frozen_maxtime; float autocvar_g_freezetag_revive_clearspeed; float autocvar_g_freezetag_round_timelimit; @@ -15,10 +17,10 @@ void freezetag_count_alive_players() FOREACH_CLIENT(IS_PLAYER(it), { switch(it.team) { - case NUM_TEAM_1: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++redalive; break; - case NUM_TEAM_2: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++bluealive; break; - case NUM_TEAM_3: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++yellowalive; break; - case NUM_TEAM_4: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++pinkalive; break; + case NUM_TEAM_1: ++total_players; if(GetResourceAmount(it, RESOURCE_HEALTH) >= 1 && STAT(FROZEN, it) != 1) ++redalive; break; + case NUM_TEAM_2: ++total_players; if(GetResourceAmount(it, RESOURCE_HEALTH) >= 1 && STAT(FROZEN, it) != 1) ++bluealive; break; + case NUM_TEAM_3: ++total_players; if(GetResourceAmount(it, RESOURCE_HEALTH) >= 1 && STAT(FROZEN, it) != 1) ++yellowalive; break; + case NUM_TEAM_4: ++total_players; if(GetResourceAmount(it, RESOURCE_HEALTH) >= 1 && STAT(FROZEN, it) != 1) ++pinkalive; break; } }); FOREACH_CLIENT(IS_REAL_CLIENT(it), { @@ -140,7 +142,7 @@ entity freezetag_LastPlayerForTeam(entity this) { entity last_pl = NULL; FOREACH_CLIENT(IS_PLAYER(it) && it != this, { - if(it.health >= 1) + if(GetResourceAmount(it, RESOURCE_HEALTH) >= 1) if(!STAT(FROZEN, it)) if(SAME_TEAM(it, this)) if(!last_pl) @@ -232,7 +234,7 @@ void havocbot_goalrating_freeplayers(entity this, float ratingscale, vector org, { // If teamate is not frozen still seek them out as fight better // in a group. - t = 0.2 * 150 / (this.health + this.armorvalue); + t = 0.2 * 150 / (GetResourceAmount(this, RESOURCE_HEALTH) + GetResourceAmount(this, RESOURCE_ARMOR)); navigation_routerating(this, it, t * ratingscale, 2000); } }); @@ -308,7 +310,7 @@ void havocbot_role_ft_freeing(entity this) void ft_RemovePlayer(entity this) { - this.health = 0; // neccessary to update correctly alive stats + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // neccessary to update correctly alive stats if(!STAT(FROZEN, this)) freezetag_LastPlayerForTeam_Notify(this); freezetag_Unfreeze(this); @@ -359,7 +361,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies) } else freezetag_Unfreeze(frag_target); // remove ice - frag_target.health = 0; // Unfreeze resets health + SetResourceAmountExplicit(frag_target, RESOURCE_HEALTH, 0); // Unfreeze resets health frag_target.freezetag_frozen_timeout = -2; // freeze on respawn return true; } @@ -468,7 +470,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST) if(n && STAT(FROZEN, player) == 1) // OK, there is at least one teammate reviving us { STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); - player.health = max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)); + SetResourceAmountExplicit(player, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health))); if(STAT(REVIVE_PROGRESS, player) >= 1) { @@ -502,7 +504,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST) else if(!n && STAT(FROZEN, player) == 1) // only if no teammate is nearby will we reset { STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) - frametime * autocvar_g_freezetag_revive_clearspeed, 1); - player.health = max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)); + SetResourceAmountExplicit(player, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health))); } else if(!n && !STAT(FROZEN, player)) { @@ -564,7 +566,8 @@ MUTATOR_HOOKFUNCTION(ft, FragCenterMessage) return; // target was already frozen, so this is just pushing them off the cliff Send_Notification(NOTIF_ONE, frag_attacker, MSG_CHOICE, CHOICE_FRAG_FREEZE, frag_target.netname, kill_count_to_attacker, (IS_BOT_CLIENT(frag_target) ? -1 : CS(frag_target).ping)); - Send_Notification(NOTIF_ONE, frag_target, MSG_CHOICE, CHOICE_FRAGGED_FREEZE, frag_attacker.netname, kill_count_to_target, frag_attacker.health, frag_attacker.armorvalue, (IS_BOT_CLIENT(frag_attacker) ? -1 : CS(frag_attacker).ping)); + Send_Notification(NOTIF_ONE, frag_target, MSG_CHOICE, CHOICE_FRAGGED_FREEZE, frag_attacker.netname, kill_count_to_target, + GetResourceAmount(frag_attacker, RESOURCE_HEALTH), GetResourceAmount(frag_attacker, RESOURCE_ARMOR), (IS_BOT_CLIENT(frag_attacker) ? -1 : CS(frag_attacker).ping)); return true; } diff --git a/qcsrc/common/gamemodes/gamemode/invasion/invasion.qc b/qcsrc/common/gamemodes/gamemode/invasion/invasion.qc index d6f9860c98..3dff701959 100644 --- a/qcsrc/common/gamemodes/gamemode/invasion/invasion.qc +++ b/qcsrc/common/gamemodes/gamemode/invasion/invasion.qc @@ -290,7 +290,7 @@ bool Invasion_CheckWinner() float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0; - IL_EACH(g_monsters, it.health > 0, + IL_EACH(g_monsters, GetResourceAmount(it, RESOURCE_HEALTH) > 0, { if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) ++supermonster_count; diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/keepaway.qc b/qcsrc/common/gamemodes/gamemode/keepaway/keepaway.qc index ac4c5a6752..fc2793bcab 100644 --- a/qcsrc/common/gamemodes/gamemode/keepaway/keepaway.qc +++ b/qcsrc/common/gamemodes/gamemode/keepaway/keepaway.qc @@ -222,7 +222,7 @@ void havocbot_goalrating_ball(entity this, float ratingscale, vector org) // If ball is carried by player then hunt them down. if (ball_owner) { - t = (this.health + this.armorvalue) / (ball_owner.health + ball_owner.armorvalue); + t = (GetResourceAmount(this, RESOURCE_HEALTH) + GetResourceAmount(this, RESOURCE_ARMOR)) / (GetResourceAmount(ball_owner, RESOURCE_HEALTH) + GetResourceAmount(ball_owner, RESOURCE_ARMOR)); navigation_routerating(this, ball_owner, t * ratingscale, 2000); } else // Ball has been dropped so collect. diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 426b341a63..487012aa50 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -308,7 +308,7 @@ void football_touch(entity this, entity toucher) } if (!IS_PLAYER(toucher)) return; - if(toucher.health < 1) + if(GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) return; if(!this.cnt) this.nextthink = time + autocvar_g_nexball_delay_idle; @@ -348,7 +348,7 @@ void basketball_touch(entity this, entity toucher) } if(!this.cnt && IS_PLAYER(toucher) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (toucher != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect)) { - if(toucher.health <= 0) + if(GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) return; LogNB("caught", toucher); GiveBall(toucher, this); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc index a03e5b3353..b8d49b10f2 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc @@ -30,7 +30,7 @@ void cpicon_draw(entity this) this.cp_bob_spd = this.cp_bob_spd + 1.875 * frametime; this.colormod = '1 1 1' * (2 - bound(0, (this.pain_finished - time) / 10, 1)); - if(!this.iscaptured) this.alpha = this.health / this.max_health; + if(!this.iscaptured) this.alpha = GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health; if(this.iscaptured) { @@ -165,14 +165,14 @@ NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew) this.origin = ReadVector(); setorigin(this, this.origin); - this.health = ReadByte(); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ReadByte()); this.max_health = ReadByte(); this.count = ReadByte(); this.team = ReadByte(); this.iscaptured = ReadByte(); if(!this.count) - this.count = (this.health - this.max_health) * frametime; + this.count = (GetResourceAmount(this, RESOURCE_HEALTH) - this.max_health) * frametime; cpicon_changeteam(this); cpicon_construct(this, isnew); @@ -189,9 +189,9 @@ NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew) _tmp = ReadByte(); - if(_tmp != this.health) + if(_tmp != GetResourceAmount(this, RESOURCE_HEALTH)) cpicon_damage(this, _tmp); - this.health = _tmp; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp); } } diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc index a3374bf91e..9d12c5548e 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc @@ -48,10 +48,10 @@ void generator_draw(entity this) if(time < this.move_time) return; - if(this.health > 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) > 0) { // damaged fx (less probable the more damaged is the generator) - if(random() < 0.9 - this.health / this.max_health) + if(random() < 0.9 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) if(random() < 0.01) { pointparticles(EFFECT_ELECTRO_BALLEXPLODE, this.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); @@ -195,7 +195,7 @@ NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew) this.origin = ReadVector(); setorigin(this, this.origin); - this.health = ReadByte(); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ReadByte()); this.max_health = ReadByte(); this.count = ReadByte(); this.team = ReadByte(); @@ -219,9 +219,9 @@ NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew) _tmp = ReadByte(); - if(_tmp != this.health) + if(_tmp != GetResourceAmount(this, RESOURCE_HEALTH)) generator_damage(this, _tmp); - this.health = _tmp; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp); } } diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc index 80d6a6be29..5deef7ec22 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@ -36,7 +36,7 @@ MUTATOR_HOOKFUNCTION(cl_ons, WantEventchase) entity gen = NULL; if(ons_roundlost) { - IL_EACH(g_onsgenerators, it.health <= 0, + IL_EACH(g_onsgenerators, GetResourceAmount(it, RESOURCE_HEALTH) <= 0, { gen = it; break; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc index d3b6d5c7f4..a00af18ff8 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc @@ -12,7 +12,7 @@ bool cpicon_send(entity this, entity to, int sf) { WriteVector(MSG_ENTITY, this.origin); - WriteByte(MSG_ENTITY, this.health); + WriteByte(MSG_ENTITY, GetResourceAmount(this, RESOURCE_HEALTH)); WriteByte(MSG_ENTITY, this.max_health); WriteByte(MSG_ENTITY, this.count); WriteByte(MSG_ENTITY, this.team); @@ -23,10 +23,10 @@ bool cpicon_send(entity this, entity to, int sf) { WriteByte(MSG_ENTITY, this.team); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) WriteByte(MSG_ENTITY, 0); else - WriteByte(MSG_ENTITY, ceil((this.health / this.max_health) * 255)); + WriteByte(MSG_ENTITY, ceil((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 255)); } return true; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc index ac0596f2e2..a33a430124 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc @@ -8,7 +8,7 @@ bool generator_send(entity this, entity to, int sf) { WriteVector(MSG_ENTITY, this.origin); - WriteByte(MSG_ENTITY, this.health); + WriteByte(MSG_ENTITY, GetResourceAmount(this, RESOURCE_HEALTH)); WriteByte(MSG_ENTITY, this.max_health); WriteByte(MSG_ENTITY, this.count); WriteByte(MSG_ENTITY, this.team); @@ -18,10 +18,10 @@ bool generator_send(entity this, entity to, int sf) { WriteByte(MSG_ENTITY, this.team); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) WriteByte(MSG_ENTITY, 0); else - WriteByte(MSG_ENTITY, ceil((this.health / this.max_health) * 255)); + WriteByte(MSG_ENTITY, ceil((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 255)); } return true; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc index 85d4bef628..7258a190ea 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -400,11 +400,11 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker ons_notification_time[this.team] = time; } - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); if(this.owner.iscaptured) - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + WaypointSprite_UpdateHealth(this.owner.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); else - WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - GetResourceAmount(this, RESOURCE_HEALTH)) / (this.count / ONS_CP_THINKRATE)); this.pain_finished = time + 1; // particles on every hit pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); @@ -414,7 +414,7 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker else sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); - if (this.health < 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 0) { sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); @@ -447,6 +447,21 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker this.SendFlags |= CPSF_STATUS; } +bool ons_ControlPoint_Icon_Heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + if(targ.owner.iscaptured) + WaypointSprite_UpdateHealth(targ.owner.sprite, GetResourceAmount(targ, RESOURCE_HEALTH)); + else + WaypointSprite_UpdateBuildFinished(targ.owner.sprite, time + (targ.max_health - GetResourceAmount(targ, RESOURCE_HEALTH)) / (targ.count / ONS_CP_THINKRATE)); + targ.SendFlags |= CPSF_STATUS; + return true; +} + void ons_ControlPoint_Icon_Think(entity this) { this.nextthink = time + ONS_CP_THINKRATE; @@ -469,9 +484,9 @@ void ons_ControlPoint_Icon_Think(entity this) _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - this.health = bound(0, this.health + (_friendly_count - _enemy_count), this.max_health); + GiveResourceWithLimit(this, RESOURCE_HEALTH, (_friendly_count - _enemy_count), this.max_health); this.SendFlags |= CPSF_STATUS; - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, DMG_NOWEP, this.origin, '0 0 0'); return; @@ -480,12 +495,10 @@ void ons_ControlPoint_Icon_Think(entity this) if (time > this.pain_finished + 5) { - if(this.health < this.max_health) + if(GetResourceAmount(this, RESOURCE_HEALTH) < this.max_health) { - this.health = this.health + this.count; - if (this.health >= this.max_health) - this.health = this.max_health; - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + GiveResourceWithLimit(this, RESOURCE_HEALTH, this.count, this.max_health); + WaypointSprite_UpdateHealth(this.owner.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); } } @@ -504,7 +517,7 @@ void ons_ControlPoint_Icon_Think(entity this) } // damaged fx - if(random() < 0.6 - this.health / this.max_health) + if(random() < 0.6 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) { Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); @@ -526,13 +539,13 @@ void ons_ControlPoint_Icon_BuildThink(entity this) if(!a) return; - this.health = this.health + this.count; + GiveResource(this, RESOURCE_HEALTH, this.count); this.SendFlags |= CPSF_STATUS; - if (this.health >= this.max_health) + if (GetResourceAmount(this, RESOURCE_HEALTH) >= this.max_health) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on setthink(this, ons_ControlPoint_Icon_Think); sound(this, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM); @@ -542,7 +555,7 @@ void ons_ControlPoint_Icon_BuildThink(entity this) Send_Effect(EFFECT_CAP(this.owner.team), this.owner.origin, '0 0 0', 1); WaypointSprite_UpdateMaxHealth(this.owner.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + WaypointSprite_UpdateHealth(this.owner.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); if(IS_PLAYER(this.owner.ons_toucher)) { @@ -565,7 +578,7 @@ void ons_ControlPoint_Icon_BuildThink(entity this) if(this.owner.model != MDL_ONS_CP_PAD2.model_str()) setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2); - if(random() < 0.9 - this.health / this.max_health) + if(random() < 0.9 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1); } @@ -580,15 +593,16 @@ void ons_ControlPoint_Icon_Spawn(entity cp, entity player) e.owner = cp; e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, autocvar_g_onslaught_cp_buildhealth); e.solid = SOLID_NOT; e.takedamage = DAMAGE_AIM; e.bot_attack = true; IL_PUSH(g_bot_targets, e); e.event_damage = ons_ControlPoint_Icon_Damage; + e.event_heal = ons_ControlPoint_Icon_Heal; e.team = player.team; e.colormap = 1024 + (e.team - 1) * 17; - e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + e.count = (e.max_health - GetResourceAmount(e, RESOURCE_HEALTH)) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); @@ -598,7 +612,7 @@ void ons_ControlPoint_Icon_Spawn(entity cp, entity player) Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1); - WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - GetResourceAmount(e, RESOURCE_HEALTH)) / (e.count / ONS_CP_THINKRATE)); WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); cp.sprite.SendFlags |= 16; @@ -640,7 +654,7 @@ void ons_ControlPoint_UpdateSprite(entity e) else { WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); - WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); + WaypointSprite_UpdateHealth(e.sprite, GetResourceAmount(e.goalentity, RESOURCE_HEALTH)); } } if(e.lastshielded) @@ -889,14 +903,14 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK)); } } - this.health = this.health - damage; - WaypointSprite_UpdateHealth(this.sprite, this.health); + TakeResource(this, RESOURCE_HEALTH, damage); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); // choose an animation frame based on health - this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1); + this.frame = 10 * bound(0, (1 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health), 1); // see if the generator is still functional, or dying - if (this.health > 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) > 0) { - this.lasthealth = this.health; + this.lasthealth = GetResourceAmount(this, RESOURCE_HEALTH); } else { @@ -912,6 +926,7 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d this.isshielded = false; this.takedamage = DAMAGE_NO; // can't be hurt anymore this.event_damage = func_null; // won't do anything if hurt + this.event_heal = func_null; this.count = 0; // reset counter setthink(this, func_null); this.nextthink = 0; @@ -946,31 +961,46 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d this.SendFlags |= GSF_STATUS; } +bool ons_GeneratorHeal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH)); + targ.frame = 10 * bound(0, (1 - GetResourceAmount(targ, RESOURCE_HEALTH) / targ.max_health), 1); + targ.lasthealth = GetResourceAmount(targ, RESOURCE_HEALTH); + targ.SendFlags |= GSF_STATUS; + return true; +} + void ons_GeneratorThink(entity this) { this.nextthink = time + GEN_THINKRATE; - if (!game_stopped) + + if (game_stopped || this.isshielded || time < this.wait) + return; + + this.wait = time + 5; + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if(!this.isshielded && this.wait < time) + if (SAME_TEAM(it, this)) { - this.wait = time + 5; - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if(SAME_TEAM(it, this)) - { - Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); - soundto(MSG_ONE, it, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? - } - else - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); - }); + Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); + msg_entity = it; + soundto(MSG_ONE, this, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? } - } + else + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); + }); } void ons_GeneratorReset(entity this) { this.team = this.team_saved; - this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, autocvar_g_onslaught_gen_health); + this.lasthealth = this.max_health = autocvar_g_onslaught_gen_health; this.takedamage = DAMAGE_AIM; this.bot_attack = true; if(!IL_CONTAINS(g_bot_targets, this)) @@ -979,6 +1009,7 @@ void ons_GeneratorReset(entity this) this.islinked = true; this.isshielded = true; this.event_damage = ons_GeneratorDamage; + this.event_heal = ons_GeneratorHeal; setthink(this, ons_GeneratorThink); this.nextthink = time + GEN_THINKRATE; @@ -988,7 +1019,7 @@ void ons_GeneratorReset(entity this) this.SendFlags |= GSF_STATUS; WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); onslaught_updatelinks(); @@ -1035,11 +1066,13 @@ void ons_GeneratorSetup(entity gen) // called when spawning a generator entity o gen.team_saved = teamnumber; IL_PUSH(g_saved_team, gen); set_movetype(gen, MOVETYPE_NONE); - gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; + gen.lasthealth = gen.max_health = autocvar_g_onslaught_gen_health; + SetResourceAmountExplicit(gen, RESOURCE_HEALTH, autocvar_g_onslaught_gen_health); gen.takedamage = DAMAGE_AIM; gen.bot_attack = true; IL_PUSH(g_bot_targets, gen); gen.event_damage = ons_GeneratorDamage; + gen.event_heal = ons_GeneratorHeal; gen.reset = ons_GeneratorReset; setthink(gen, ons_GeneratorThink); gen.nextthink = time + GEN_THINKRATE; @@ -1061,7 +1094,7 @@ void ons_GeneratorSetup(entity gen) // called when spawning a generator entity o WaypointSprite_SpawnFixed(WP_Null, gen.origin + CPGEN_WAYPOINT_OFFSET, gen, sprite, RADARICON_NONE); WaypointSprite_UpdateRule(gen.sprite, gen.team, SPRITERULE_TEAMPLAY); WaypointSprite_UpdateMaxHealth(gen.sprite, gen.max_health); - WaypointSprite_UpdateHealth(gen.sprite, gen.health); + WaypointSprite_UpdateHealth(gen.sprite, GetResourceAmount(gen, RESOURCE_HEALTH)); InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); } @@ -1079,10 +1112,10 @@ void Onslaught_count_generators() for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) { ++total_generators; - redowned += (e.team == NUM_TEAM_1 && e.health > 0); - blueowned += (e.team == NUM_TEAM_2 && e.health > 0); - yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); - pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); + redowned += (e.team == NUM_TEAM_1 && GetResourceAmount(e, RESOURCE_HEALTH) > 0); + blueowned += (e.team == NUM_TEAM_2 && GetResourceAmount(e, RESOURCE_HEALTH) > 0); + yellowowned += (e.team == NUM_TEAM_3 && GetResourceAmount(e, RESOURCE_HEALTH) > 0); + pinkowned += (e.team == NUM_TEAM_4 && GetResourceAmount(e, RESOURCE_HEALTH) > 0); } } @@ -1223,7 +1256,7 @@ void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector bool needarmor = false, needweapons = false; // Needs armor/health? - if(this.health<100) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 100) needarmor = true; // Needs weapons? @@ -1248,7 +1281,7 @@ void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector { // gather health and armor only if (it.solid) - if ( ((it.health || it.armorvalue) && needarmor) || (STAT(WEAPONS, it) && needweapons ) ) + if ( ((GetResourceAmount(it, RESOURCE_HEALTH) || GetResourceAmount(it, RESOURCE_ARMOR)) && needarmor) || (STAT(WEAPONS, it) && needweapons ) ) if (vdist(it.origin - org, <, sradius)) { int t = it.bot_pickupevalfunc(this, it); @@ -1949,7 +1982,7 @@ MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) { entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); - if ( !source_point && player.health > 0 ) + if ( !source_point && GetResourceAmount(player, RESOURCE_HEALTH) > 0 ) { sprint(player, "\nYou need to be next to a control point\n"); return true; @@ -1964,7 +1997,7 @@ MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) return true; } - if ( player.health <= 0 ) + if ( GetResourceAmount(player, RESOURCE_HEALTH) <= 0 ) { player.ons_spawn_by = closest_target; player.respawn_flags = player.respawn_flags | RESPAWN_FORCE; @@ -2030,14 +2063,14 @@ MUTATOR_HOOKFUNCTION(ons, SendWaypoint) { entity wp_owner = wp.owner; entity e = WaypointSprite_getviewentity(to); - if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; } + if(SAME_TEAM(e, wp_owner) && GetResourceAmount(wp_owner.goalentity, RESOURCE_HEALTH) >= wp_owner.goalentity.max_health) { wp_flag |= 2; } if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; } } if(wp.owner.classname == "onslaught_generator") { entity wp_owner = wp.owner; - if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; } - if(wp_owner.health <= 0) { wp_flag |= 2; } + if(wp_owner.isshielded && GetResourceAmount(wp_owner, RESOURCE_HEALTH) >= wp_owner.max_health) { wp_flag |= 2; } + if(GetResourceAmount(wp_owner, RESOURCE_HEALTH) <= 0) { wp_flag |= 2; } } } diff --git a/qcsrc/common/items/item.qh b/qcsrc/common/items/item.qh index 31b8f43cb1..3109e7c92f 100644 --- a/qcsrc/common/items/item.qh +++ b/qcsrc/common/items/item.qh @@ -72,7 +72,8 @@ const int IT_PICKUPMASK = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FU enum { ITEM_FLAG_NORMAL = BIT(0), ///< Item is usable during normal gameplay. - ITEM_FLAG_MUTATORBLOCKED = BIT(1) + ITEM_FLAG_MUTATORBLOCKED = BIT(1), + ITEM_FLAG_RESOURCE = BIT(2) ///< Item is is a resource, not a held item. }; #define ITEM_HANDLE(signal, ...) __Item_Send_##signal(__VA_ARGS__) diff --git a/qcsrc/common/items/item/ammo.qh b/qcsrc/common/items/item/ammo.qh index 7c5c12af72..4c37464ad8 100644 --- a/qcsrc/common/items/item/ammo.qh +++ b/qcsrc/common/items/item/ammo.qh @@ -1,10 +1,13 @@ #pragma once #include "pickup.qh" +#include #ifdef SVQC #include + #include #endif +#if 1 .int ammo_none; .int ammo_shells; .int ammo_nails; @@ -17,6 +20,11 @@ .int ammo_plasma; .int ammo_fuel; #endif +#endif + +#ifdef GAMEQC +.int spawnflags; +#endif #ifdef SVQC PROPERTY(float, g_pickup_ammo_anyway); @@ -40,8 +48,8 @@ MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl")); PROPERTY(int, g_pickup_nails); void ammo_bullets_init(Pickup this, entity item) { - if(!item.ammo_nails) - item.ammo_nails = g_pickup_nails; + if(!GetResourceAmount(item, RESOURCE_BULLETS)) + SetResourceAmountExplicit(item, RESOURCE_BULLETS, g_pickup_nails); } #endif @@ -51,7 +59,7 @@ ENDCLASS(Bullets) REGISTER_ITEM(Bullets, Bullets) { this.m_canonical_spawnfunc = "item_bullets"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Bullets_ITEM; #endif this.netname = "bullets"; @@ -74,14 +82,14 @@ MODEL(Cells_ITEM, Item_Model("a_cells.md3")); PROPERTY(int, g_pickup_cells); void ammo_cells_init(Pickup this, entity item) { - if(!item.ammo_cells) - item.ammo_cells = g_pickup_cells; + if(!GetResourceAmount(item, RESOURCE_CELLS)) + SetResourceAmountExplicit(item, RESOURCE_CELLS, g_pickup_cells); } #endif REGISTER_ITEM(Cells, Ammo) { this.m_canonical_spawnfunc = "item_cells"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Cells_ITEM; #endif this.netname = "cells"; @@ -104,14 +112,14 @@ MODEL(Plasma_ITEM, Item_Model("a_cells.md3")); PROPERTY(int, g_pickup_plasma); void ammo_plasma_init(Pickup this, entity item) { - if(!item.ammo_plasma) - item.ammo_plasma = g_pickup_plasma; + if(!GetResourceAmount(item, RESOURCE_PLASMA)) + SetResourceAmountExplicit(item, RESOURCE_PLASMA, g_pickup_plasma); } #endif REGISTER_ITEM(Plasma, Ammo) { this.m_canonical_spawnfunc = "item_plasma"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Plasma_ITEM; #endif this.netname = "plasma"; @@ -134,14 +142,14 @@ MODEL(Rockets_ITEM, Item_Model("a_rockets.md3")); PROPERTY(int, g_pickup_rockets); void ammo_rockets_init(Pickup this, entity item) { - if(!item.ammo_rockets) - item.ammo_rockets = g_pickup_rockets; + if(!GetResourceAmount(item, RESOURCE_ROCKETS)) + SetResourceAmountExplicit(item, RESOURCE_ROCKETS, g_pickup_rockets); } #endif REGISTER_ITEM(Rockets, Ammo) { this.m_canonical_spawnfunc = "item_rockets"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Rockets_ITEM; #endif this.netname = "rockets"; @@ -164,8 +172,8 @@ MODEL(Shells_ITEM, Item_Model("a_shells.md3")); PROPERTY(int, g_pickup_shells); void ammo_shells_init(Pickup this, entity item) { - if(!item.ammo_shells) - item.ammo_shells = g_pickup_shells; + if(!GetResourceAmount(item, RESOURCE_SHELLS)) + SetResourceAmountExplicit(item, RESOURCE_SHELLS, g_pickup_shells); } #endif @@ -175,7 +183,7 @@ ENDCLASS(Shells) REGISTER_ITEM(Shells, Shells) { this.m_canonical_spawnfunc = "item_shells"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Shells_ITEM; #endif this.netname = "shells"; diff --git a/qcsrc/common/items/item/armor.qh b/qcsrc/common/items/item/armor.qh index 880a932d7c..ee39aa5924 100644 --- a/qcsrc/common/items/item/armor.qh +++ b/qcsrc/common/items/item/armor.qh @@ -26,15 +26,15 @@ void item_armorsmall_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armorsmall_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armorsmall; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armorsmall); } #endif REGISTER_ITEM(ArmorSmall, Armor) { this.m_canonical_spawnfunc = "item_armor_small"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorSmall_ITEM; this.m_sound = SND_ArmorSmall; #endif @@ -64,15 +64,15 @@ void item_armormedium_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armormedium_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armormedium; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armormedium); } #endif REGISTER_ITEM(ArmorMedium, Armor) { this.m_canonical_spawnfunc = "item_armor_medium"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorMedium_ITEM; this.m_sound = SND_ArmorMedium; #endif @@ -102,15 +102,15 @@ void item_armorbig_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armorbig_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armorbig; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armorbig); } #endif REGISTER_ITEM(ArmorBig, Armor) { this.m_canonical_spawnfunc = "item_armor_big"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorBig_ITEM; this.m_sound = SND_ArmorBig; #endif @@ -142,15 +142,15 @@ void item_armormega_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armormega_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armormega; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armormega); } #endif REGISTER_ITEM(ArmorMega, Armor) { this.m_canonical_spawnfunc = "item_armor_mega"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorMega_ITEM; this.m_sound = SND_ArmorMega; #endif diff --git a/qcsrc/common/items/item/health.qh b/qcsrc/common/items/item/health.qh index 6a5ffc5ca4..bf515fe4dd 100644 --- a/qcsrc/common/items/item/health.qh +++ b/qcsrc/common/items/item/health.qh @@ -26,15 +26,15 @@ void item_healthsmall_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthsmall_max; - if(!item.health) - item.health = g_pickup_healthsmall; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthsmall); } #endif REGISTER_ITEM(HealthSmall, Health) { this.m_canonical_spawnfunc = "item_health_small"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthSmall_ITEM; this.m_sound = SND_HealthSmall; #endif @@ -64,15 +64,15 @@ void item_healthmedium_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthmedium_max; - if(!item.health) - item.health = g_pickup_healthmedium; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthmedium); } #endif REGISTER_ITEM(HealthMedium, Health) { this.m_canonical_spawnfunc = "item_health_medium"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthMedium_ITEM; this.m_sound = SND_HealthMedium; #endif @@ -102,15 +102,15 @@ void item_healthbig_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthbig_max; - if(!item.health) - item.health = g_pickup_healthbig; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthbig); } #endif REGISTER_ITEM(HealthBig, Health) { this.m_canonical_spawnfunc = "item_health_big"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthBig_ITEM; this.m_sound = SND_HealthBig; #endif @@ -142,15 +142,15 @@ void item_healthmega_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthmega_max; - if(!item.health) - item.health = g_pickup_healthmega; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthmega); } #endif REGISTER_ITEM(HealthMega, Health) { this.m_canonical_spawnfunc = "item_health_mega"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthMega_ITEM; this.m_sound = SND_HealthMega; #endif diff --git a/qcsrc/common/items/item/jetpack.qh b/qcsrc/common/items/item/jetpack.qh index 73f55e83f5..760033861a 100644 --- a/qcsrc/common/items/item/jetpack.qh +++ b/qcsrc/common/items/item/jetpack.qh @@ -19,8 +19,8 @@ MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3")); PROPERTY(int, g_pickup_fuel_jetpack); void powerup_jetpack_init(Pickup this, entity item) { - if(!item.ammo_fuel) - item.ammo_fuel = g_pickup_fuel_jetpack; + if(!GetResourceAmount(item, RESOURCE_FUEL)) + SetResourceAmountExplicit(item, RESOURCE_FUEL, g_pickup_fuel_jetpack); } #endif @@ -35,10 +35,10 @@ REGISTER_ITEM(Jetpack, Powerup) { this.m_itemid = IT_JETPACK; #endif this.netname = "jetpack"; - this.m_name = "Jet pack"; + this.m_name = "Jetpack"; this.m_icon = "jetpack"; this.m_color = '0.5 0.5 0.5'; - this.m_waypoint = _("Jet Pack"); + this.m_waypoint = _("Jetpack"); this.m_waypointblink = 2; #ifdef SVQC this.m_botvalue = 3000; @@ -57,14 +57,14 @@ MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3")); PROPERTY(int, g_pickup_fuel); void ammo_fuel_init(Pickup this, entity item) { - if(!item.ammo_fuel) - item.ammo_fuel = g_pickup_fuel; + if(!GetResourceAmount(item, RESOURCE_FUEL)) + SetResourceAmountExplicit(item, RESOURCE_FUEL, g_pickup_fuel); } #endif REGISTER_ITEM(JetpackFuel, Ammo) { this.m_canonical_spawnfunc = "item_fuel"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_JetpackFuel_ITEM; #endif this.netname = "fuel"; diff --git a/qcsrc/common/mapobjects/func/breakable.qc b/qcsrc/common/mapobjects/func/breakable.qc index d8f6cb1384..cb17ac442c 100644 --- a/qcsrc/common/mapobjects/func/breakable.qc +++ b/qcsrc/common/mapobjects/func/breakable.qc @@ -83,7 +83,7 @@ void func_breakable_colormod(entity this) float h; if (!(this.spawnflags & BREAKABLE_INDICATE_DAMAGE)) return; - h = this.health / this.max_health; + h = GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health; if(h < 0.25) this.colormod = '1 0 0'; else if(h <= 0.75) @@ -129,7 +129,7 @@ void func_breakable_look_restore(entity this) void func_breakable_behave_destroyed(entity this) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.takedamage = DAMAGE_NO; if(this.bot_attack) IL_REMOVE(g_bot_targets, this); @@ -141,6 +141,11 @@ void func_breakable_behave_destroyed(entity this) func_breakable_colormod(this); if (this.noise1) stopsound (this, CH_TRIGGER_SINGLE); + + IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, + { + RemoveHook(it); + }); } void func_breakable_think(entity this) @@ -152,11 +157,11 @@ void func_breakable_think(entity this) void func_breakable_destroy(entity this, entity actor, entity trigger); void func_breakable_behave_restore(entity this) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); if(this.sprite) { WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); } if(!(this.spawnflags & BREAKABLE_NODAMAGE)) { @@ -200,6 +205,16 @@ void func_breakable_restore(entity this, entity actor, entity trigger) void func_breakable_restore_self(entity this) { + // TODO: use a clipgroup for all func_breakables so they don't collide with eachother + float oldhit = this.dphitcontentsmask; + this.dphitcontentsmask = DPCONTENTS_BODY; // we really only care about when players are standing inside, obey the mapper in other cases! + tracebox(this.origin, this.mins, this.maxs, this.origin, MOVE_NORMAL, this); + this.dphitcontentsmask = oldhit; + if(trace_startsolid || trace_fraction < 1) + { + this.nextthink = time + 5; // retry every 5 seconds until the area becomes clear + return; + } func_breakable_restore(this, NULL, NULL); } @@ -257,15 +272,15 @@ void func_breakable_damage(entity this, entity inflictor, entity attacker, float if(attacker.team == this.team) return; this.pain_finished = time; - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); if(this.sprite) { WaypointSprite_Ping(this.sprite); - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); } func_breakable_colormod(this); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { debrisforce = force; @@ -300,9 +315,9 @@ void func_breakable_reset(entity this) spawnfunc(func_breakable) { float n, i; - if(!this.health) - this.health = 100; - this.max_health = this.health; + if(!GetResourceAmount(this, RESOURCE_HEALTH)) + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway if(!this.debrismovetype) this.debrismovetype = MOVETYPE_BOUNCE; diff --git a/qcsrc/common/mapobjects/func/button.qc b/qcsrc/common/mapobjects/func/button.qc index 28e6481c88..44e3128433 100644 --- a/qcsrc/common/mapobjects/func/button.qc +++ b/qcsrc/common/mapobjects/func/button.qc @@ -27,7 +27,7 @@ void button_return(entity this) this.state = STATE_DOWN; SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, button_done); this.frame = 0; // use normal textures - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) this.takedamage = DAMAGE_YES; // can be shot again } @@ -40,7 +40,7 @@ void button_blocked(entity this, entity blocker) void button_fire(entity this) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.takedamage = DAMAGE_NO; // will be reset upon return if (this.state == STATE_UP || this.state == STATE_TOP) @@ -55,14 +55,14 @@ void button_fire(entity this) void button_reset(entity this) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); setorigin(this, this.pos1); this.frame = 0; // use normal textures this.state = STATE_BOTTOM; this.velocity = '0 0 0'; setthink(this, func_null); this.nextthink = 0; - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) this.takedamage = DAMAGE_YES; // can be shot again } @@ -96,7 +96,7 @@ void button_damage(entity this, entity inflictor, entity attacker, float damage, return; if (this.spawnflags & BUTTON_DONTACCUMULATEDMG) { - if (this.health <= damage) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= damage) { this.enemy = attacker; button_fire(this); @@ -104,8 +104,8 @@ void button_damage(entity this, entity inflictor, entity attacker, float damage, } else { - this.health = this.health - damage; - if (this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.enemy = attacker; button_fire(this); @@ -138,9 +138,9 @@ spawnfunc(func_button) // if (this.health == 0) // all buttons are now shootable // this.health = 10; - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) { - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); this.event_damage = button_damage; this.takedamage = DAMAGE_YES; } diff --git a/qcsrc/common/mapobjects/func/door.qc b/qcsrc/common/mapobjects/func/door.qc index c19041aa0b..8d40a377be 100644 --- a/qcsrc/common/mapobjects/func/door.qc +++ b/qcsrc/common/mapobjects/func/door.qc @@ -113,7 +113,7 @@ void door_go_down(entity this) if (this.max_health) { this.takedamage = DAMAGE_YES; - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); } this.state = STATE_DOWN; @@ -265,7 +265,7 @@ void door_damage(entity this, entity inflictor, entity attacker, float damage, i if(this.spawnflags & NOSPLASH) if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) return; - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); if (this.itemkeys) { @@ -273,10 +273,10 @@ void door_damage(entity this, entity inflictor, entity attacker, float damage, i return; } - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { - this.owner.health = this.owner.max_health; - this.owner.takedamage = DAMAGE_NO; // wil be reset upon return + SetResourceAmountExplicit(this.owner, RESOURCE_HEALTH, this.owner.max_health); + this.owner.takedamage = DAMAGE_NO; // will be reset upon return door_use(this.owner, NULL, NULL); } } @@ -357,7 +357,7 @@ Spawned if a door lacks a real activator void door_trigger_touch(entity this, entity toucher) { - if (toucher.health < 1) + if (GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) #ifdef SVQC if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher))) #elif defined(CSQC) @@ -441,7 +441,7 @@ void LinkDoors(entity this) { this.owner = this.enemy = this; - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) return; IFTARGETED return; @@ -474,8 +474,8 @@ void LinkDoors(entity this) cmaxs = this.absmax; for(t = this; ; t = t.enemy) { - if(t.health && !this.health) - this.health = t.health; + if(GetResourceAmount(t, RESOURCE_HEALTH) && !GetResourceAmount(this, RESOURCE_HEALTH)) + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(t, RESOURCE_HEALTH)); if((t.targetname != "") && (this.targetname == "")) this.targetname = t.targetname; if((t.message != "") && (this.message == "")) @@ -499,7 +499,7 @@ void LinkDoors(entity this) // distribute health, targetname, message for(t = this; t; t = t.enemy) { - t.health = this.health; + SetResourceAmountExplicit(t, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); t.targetname = this.targetname; t.message = this.message; if(t.enemy == this) @@ -509,7 +509,7 @@ void LinkDoors(entity this) // shootable, or triggered doors just needed the owner/enemy links, // they don't spawn a field - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) return; IFTARGETED return; @@ -630,7 +630,7 @@ void door_reset(entity this) // common code for func_door and func_door_rotating spawnfuncs void door_init_shared(entity this) { - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); // unlock sound if(this.noise == "") @@ -683,7 +683,7 @@ void door_init_shared(entity this) this.state = STATE_BOTTOM; - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) { //this.canteamdamage = true; // TODO this.takedamage = DAMAGE_YES; diff --git a/qcsrc/common/mapobjects/func/door_rotating.qc b/qcsrc/common/mapobjects/func/door_rotating.qc index 41fd05e574..39c02a8669 100644 --- a/qcsrc/common/mapobjects/func/door_rotating.qc +++ b/qcsrc/common/mapobjects/func/door_rotating.qc @@ -58,7 +58,7 @@ void door_rotating_go_down(entity this) if (this.max_health) { this.takedamage = DAMAGE_YES; - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); } this.state = STATE_DOWN; diff --git a/qcsrc/common/mapobjects/func/door_secret.qc b/qcsrc/common/mapobjects/func/door_secret.qc index 78e0dd64e9..f06f39e911 100644 --- a/qcsrc/common/mapobjects/func/door_secret.qc +++ b/qcsrc/common/mapobjects/func/door_secret.qc @@ -13,7 +13,7 @@ void fd_secret_use(entity this, entity actor, entity trigger) float temp; string message_save; - this.health = 10000; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); if(!this.bot_attack) IL_PUSH(g_bot_targets, this); this.bot_attack = true; @@ -122,7 +122,7 @@ void fd_secret_done(entity this) { if (this.spawnflags&DOOR_SECRET_YES_SHOOT) { - this.health = 10000; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); this.takedamage = DAMAGE_YES; //this.th_pain = fd_secret_use; } @@ -168,7 +168,7 @@ void secret_reset(entity this) { if (this.spawnflags & DOOR_SECRET_YES_SHOOT) { - this.health = 10000; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); this.takedamage = DAMAGE_YES; } setorigin(this, this.oldorigin); @@ -253,7 +253,7 @@ spawnfunc(func_door_secret) if (this.spawnflags & DOOR_SECRET_YES_SHOOT) { //this.canteamdamage = true; // TODO - this.health = 10000; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); this.takedamage = DAMAGE_YES; this.event_damage = fd_secret_damage; } diff --git a/qcsrc/common/mapobjects/platforms.qc b/qcsrc/common/mapobjects/platforms.qc index 4747877314..cc909e5c56 100644 --- a/qcsrc/common/mapobjects/platforms.qc +++ b/qcsrc/common/mapobjects/platforms.qc @@ -93,7 +93,7 @@ void plat_center_touch(entity this, entity toucher) if (!toucher.iscreature) return; - if (toucher.health <= 0) + if (GetResourceAmount(toucher, RESOURCE_HEALTH) <= 0) return; #elif defined(CSQC) if (!IS_PLAYER(toucher)) @@ -114,7 +114,7 @@ void plat_outside_touch(entity this, entity toucher) if (!toucher.iscreature) return; - if (toucher.health <= 0) + if (GetResourceAmount(toucher, RESOURCE_HEALTH) <= 0) return; #elif defined(CSQC) if (!IS_PLAYER(toucher)) diff --git a/qcsrc/common/mapobjects/teleporters.qc b/qcsrc/common/mapobjects/teleporters.qc index ec6a26d183..403d956c59 100644 --- a/qcsrc/common/mapobjects/teleporters.qc +++ b/qcsrc/common/mapobjects/teleporters.qc @@ -44,12 +44,12 @@ void tdeath(entity player, entity teleporter, entity telefragger, vector telefra { TDEATHLOOP(player.origin) { - if (IS_PLAYER(player) && player.health >= 1) + if (IS_PLAYER(player) && GetResourceAmount(player, RESOURCE_HEALTH) >= 1) { if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) { if(IS_PLAYER(head)) - if(head.health >= 1) + if(GetResourceAmount(head, RESOURCE_HEALTH) >= 1) ++tdeath_hit; Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, head.origin, '0 0 0'); } diff --git a/qcsrc/common/mapobjects/trigger/heal.qc b/qcsrc/common/mapobjects/trigger/heal.qc index cfcd726fcd..866fd88a56 100644 --- a/qcsrc/common/mapobjects/trigger/heal.qc +++ b/qcsrc/common/mapobjects/trigger/heal.qc @@ -18,14 +18,9 @@ void trigger_heal_touch(entity this, entity toucher) toucher.triggerhealtime = time + this.delay; bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS); - if (toucher.health < this.max_health) - { - playthesound = true; - toucher.health = min(toucher.health + this.health, this.max_health); - toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); - } + bool healed = Heal(toucher, this, GetResourceAmount(this, RESOURCE_HEALTH), this.max_health); - if(playthesound) + if(playthesound || healed) _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); } } @@ -41,8 +36,8 @@ void trigger_heal_init(entity this) this.active = ACTIVE_ACTIVE; if(!this.delay) this.delay = 1; - if(!this.health) - this.health = 10; + if(!GetResourceAmount(this, RESOURCE_HEALTH)) + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10); // TODO: use a special field for this, it doesn't have actual health! if(!this.max_health) this.max_health = 200; // max health topoff for field if(this.noise == "") diff --git a/qcsrc/common/mapobjects/trigger/multi.qc b/qcsrc/common/mapobjects/trigger/multi.qc index accfbe8ac4..5447b992c3 100644 --- a/qcsrc/common/mapobjects/trigger/multi.qc +++ b/qcsrc/common/mapobjects/trigger/multi.qc @@ -7,7 +7,7 @@ void multi_wait(entity this) { if (this.max_health) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.takedamage = DAMAGE_YES; this.solid = SOLID_BBOX; } @@ -120,8 +120,8 @@ void multi_eventdamage(entity this, entity inflictor, entity attacker, float dam if(this.team) if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team)) return; - this.health = this.health - damage; - if (this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.enemy = attacker; this.goalentity = inflictor; @@ -135,7 +135,7 @@ void multi_reset(entity this) settouch(this, multi_touch); if (this.max_health) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.takedamage = DAMAGE_YES; this.solid = SOLID_BBOX; } @@ -181,12 +181,12 @@ spawnfunc(trigger_multiple) this.team_saved = this.team; IL_PUSH(g_saved_team, this); - if (this.health) + if (GetResourceAmount(this, RESOURCE_HEALTH)) { if (this.spawnflags & SPAWNFLAG_NOTOUCH) objerror (this, "health and notouch don't make sense\n"); this.canteamdamage = true; - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); this.event_damage = multi_eventdamage; this.takedamage = DAMAGE_YES; this.solid = SOLID_BBOX; diff --git a/qcsrc/common/mapobjects/trigger/secret.qc b/qcsrc/common/mapobjects/trigger/secret.qc index 9377332e2f..5d7c5b6f46 100644 --- a/qcsrc/common/mapobjects/trigger/secret.qc +++ b/qcsrc/common/mapobjects/trigger/secret.qc @@ -73,7 +73,7 @@ spawnfunc(trigger_secret) this.targetname = ""; // you can't just shoot a room to find it, can you? - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // a secret can not be delayed this.delay = 0; diff --git a/qcsrc/common/mapobjects/trigger/swamp.qc b/qcsrc/common/mapobjects/trigger/swamp.qc index 058e41ca27..8e3fd739de 100644 --- a/qcsrc/common/mapobjects/trigger/swamp.qc +++ b/qcsrc/common/mapobjects/trigger/swamp.qc @@ -18,6 +18,7 @@ .float swamp_interval; //Hurt players in swamp with this interval .float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) +.float swamp_lifetime; // holds the points remaining until slug dies (not quite health!) .entity swampslug; #ifdef SVQC @@ -40,10 +41,10 @@ void swampslug_think(entity this); void swampslug_think(entity this) { //Slowly kill the slug - this.health = this.health - 1; + this.swamp_lifetime -= 1; //Slug dead? then remove curses. - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.owner.in_swamp = 0; delete(this); @@ -76,7 +77,7 @@ void swamp_touch(entity this, entity toucher) // If not attach one. //centerprint(toucher,"Entering swamp!\n"); toucher.swampslug = spawn(); - toucher.swampslug.health = 2; + toucher.swampslug.swamp_lifetime = 2; setthink(toucher.swampslug, swampslug_think); toucher.swampslug.nextthink = time; toucher.swampslug.owner = toucher; @@ -90,7 +91,7 @@ void swamp_touch(entity this, entity toucher) //toucher.in_swamp = 1; //Revitalize players swampslug - toucher.swampslug.health = 2; + toucher.swampslug.swamp_lifetime = 2; } REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index b8b647bef3..88120a0ea7 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -87,20 +87,20 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ) { if(targ == NULL) return false; - if(targ.health <= 0) + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0) return false; if(DIFF_TEAM(targ, this) && targ != this.monster_follow) return false; if(STAT(FROZEN, targ)) return false; if(!IS_PLAYER(targ)) - return (IS_MONSTER(targ) && targ.health < targ.max_health); + return (IS_MONSTER(targ) && GetResourceAmount(targ, RESOURCE_HEALTH) < targ.max_health); if(targ.items & ITEM_Shield.m_itemid) return false; switch(this.skin) { - case 0: return (targ.health < autocvar_g_balance_health_regenstable); + case 0: return (GetResourceAmount(targ, RESOURCE_HEALTH) < autocvar_g_balance_health_regenstable); case 1: { return ((GetResourceAmount(targ, RESOURCE_CELLS) && GetResourceAmount(targ, RESOURCE_CELLS) < g_pickup_cells_max) @@ -110,8 +110,8 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ) || (GetResourceAmount(targ, RESOURCE_SHELLS) && GetResourceAmount(targ, RESOURCE_SHELLS) < g_pickup_shells_max) ); } - case 2: return (targ.armorvalue < autocvar_g_balance_armor_regenstable); - case 3: return (targ.health > 0); + case 2: return (GetResourceAmount(targ, RESOURCE_ARMOR) < autocvar_g_balance_armor_regenstable); + case 3: return (GetResourceAmount(targ, RESOURCE_HEALTH) > 0); } return false; @@ -144,7 +144,7 @@ void M_Mage_Attack_Spike_Touch(entity this, entity toucher) // copied from W_Seeker_Think void M_Mage_Attack_Spike_Think(entity this) { - if (time > this.ltime || (this.enemy && this.enemy.health <= 0) || this.owner.health <= 0) { + if (time > this.ltime || (this.enemy && GetResourceAmount(this.enemy, RESOURCE_HEALTH) <= 0) || GetResourceAmount(this.owner, RESOURCE_HEALTH) <= 0) { this.projectiledeathtype |= HITTYPE_SPLASH; M_Mage_Attack_Spike_Explode(this, NULL); } @@ -234,29 +234,32 @@ void M_Mage_Defend_Heal(entity this) switch(this.skin) { case 0: - if(it.health < autocvar_g_balance_health_regenstable) it.health = bound(0, it.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable); + { + Heal(it, this, autocvar_g_monster_mage_heal_allies, autocvar_g_balance_health_regenstable); fx = EFFECT_HEALING; break; + } case 1: { - float tmpfld; - tmpfld = GetResourceAmount(it, RESOURCE_CELLS); if(tmpfld) SetResourceAmount(it, RESOURCE_CELLS, bound(tmpfld, tmpfld + 1, g_pickup_cells_max)); - tmpfld = GetResourceAmount(it, RESOURCE_PLASMA); if(tmpfld) SetResourceAmount(it, RESOURCE_PLASMA, bound(tmpfld, tmpfld + 1, g_pickup_plasma_max)); - tmpfld = GetResourceAmount(it, RESOURCE_ROCKETS); if(tmpfld) SetResourceAmount(it, RESOURCE_ROCKETS, bound(tmpfld, tmpfld + 1, g_pickup_rockets_max)); - tmpfld = GetResourceAmount(it, RESOURCE_SHELLS); if(tmpfld) SetResourceAmount(it, RESOURCE_SHELLS, bound(tmpfld, tmpfld + 2, g_pickup_shells_max)); - tmpfld = GetResourceAmount(it, RESOURCE_BULLETS); if(tmpfld) SetResourceAmount(it, RESOURCE_BULLETS, bound(tmpfld, tmpfld + 5, g_pickup_nails_max)); + if(GetResourceAmount(this, RESOURCE_CELLS)) GiveResourceWithLimit(it, RESOURCE_CELLS, 1, g_pickup_cells_max); + if(GetResourceAmount(this, RESOURCE_PLASMA)) GiveResourceWithLimit(it, RESOURCE_PLASMA, 1, g_pickup_plasma_max); + if(GetResourceAmount(this, RESOURCE_ROCKETS)) GiveResourceWithLimit(it, RESOURCE_ROCKETS, 1, g_pickup_rockets_max); + if(GetResourceAmount(this, RESOURCE_SHELLS)) GiveResourceWithLimit(it, RESOURCE_SHELLS, 2, g_pickup_shells_max); + if(GetResourceAmount(this, RESOURCE_BULLETS)) GiveResourceWithLimit(it, RESOURCE_BULLETS, 5, g_pickup_nails_max); + // TODO: fuel? fx = EFFECT_AMMO_REGEN; break; } case 2: - if(it.armorvalue < autocvar_g_balance_armor_regenstable) + if(GetResourceAmount(it, RESOURCE_ARMOR) < autocvar_g_balance_armor_regenstable) { - it.armorvalue = bound(0, it.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable); + GiveResourceWithLimit(it, RESOURCE_ARMOR, autocvar_g_monster_mage_heal_allies, autocvar_g_balance_armor_regenstable); fx = EFFECT_ARMOR_REPAIR; } break; case 3: - it.health = bound(0, it.health - ((it == this) ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable); + float hp = ((it == this) ? autocvar_g_monster_mage_heal_self : autocvar_g_monster_mage_heal_allies); + TakeResource(it, RESOURCE_HEALTH, hp); // TODO: use regular damage functions? needs a way to bypass friendly fire checks fx = EFFECT_RAGE; break; } @@ -266,9 +269,9 @@ void M_Mage_Defend_Heal(entity this) else { Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1); - it.health = bound(0, it.health + (autocvar_g_monster_mage_heal_allies), it.max_health); + Heal(it, this, autocvar_g_monster_mage_heal_allies, RESOURCE_LIMIT_NONE); if(!(it.spawnflags & MONSTERFLAG_INVINCIBLE) && it.sprite) - WaypointSprite_UpdateHealth(it.sprite, it.health); + WaypointSprite_UpdateHealth(it.sprite, GetResourceAmount(it, RESOURCE_HEALTH)); } }); @@ -322,14 +325,14 @@ void M_Mage_Attack_Teleport(entity this, entity targ) void M_Mage_Defend_Shield_Remove(entity this) { this.effects &= ~(EF_ADDITIVE | EF_BLUE); - this.armorvalue = autocvar_g_monsters_armor_blockpercent; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_monsters_armor_blockpercent); } void M_Mage_Defend_Shield(entity this) { this.effects |= (EF_ADDITIVE | EF_BLUE); this.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay); - this.armorvalue = (autocvar_g_monster_mage_shield_blockpercent); + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_monster_mage_shield_blockpercent); this.mage_shield_time = time + (autocvar_g_monster_mage_shield_time); setanim(this, this.anim_shoot, true, true, true); this.attack_finished_single[0] = time + 1; @@ -416,16 +419,16 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor)) }); } - if(actor.health < (autocvar_g_monster_mage_heal_minhealth) || need_help) + if(GetResourceAmount(actor, RESOURCE_HEALTH) < (autocvar_g_monster_mage_heal_minhealth) || need_help) if(time >= actor.attack_finished_single[0]) if(random() < 0.5) M_Mage_Defend_Heal(actor); - if(time >= actor.mage_shield_time && actor.armorvalue) + if(time >= actor.mage_shield_time && GetResourceAmount(actor, RESOURCE_ARMOR)) M_Mage_Defend_Shield_Remove(actor); if(actor.enemy) - if(actor.health < actor.max_health) + if(GetResourceAmount(actor, RESOURCE_HEALTH) < actor.max_health) if(time >= actor.mage_shield_delay) if(random() < 0.5) M_Mage_Defend_Shield(actor); @@ -466,7 +469,7 @@ METHOD(Mage, mr_anim, bool(Mage this, entity actor)) METHOD(Mage, mr_setup, bool(Mage this, entity actor)) { TC(Mage, this); - if(!actor.health) actor.health = (autocvar_g_monster_mage_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_mage_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_mage_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_mage_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_mage_speed_stop); } diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index eeefeae8ca..9981474f9b 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -85,15 +85,15 @@ void M_Shambler_Attack_Lightning_Explode_use(entity this, entity actor, entity t void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, adaptor_think2use); } @@ -136,7 +136,7 @@ void M_Shambler_Attack_Lightning(entity this) settouch(gren, M_Shambler_Attack_Lightning_Touch); gren.takedamage = DAMAGE_YES; - gren.health = 50; + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, 50); gren.damageforcescale = 0; gren.event_damage = M_Shambler_Attack_Lightning_Damage; gren.damagedbycontents = true; @@ -246,7 +246,7 @@ METHOD(Shambler, mr_anim, bool(Shambler this, entity actor)) METHOD(Shambler, mr_setup, bool(Shambler this, entity actor)) { TC(Shambler, this); - if(!actor.health) actor.health = (autocvar_g_monster_shambler_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_shambler_health); if(!actor.attack_range) actor.attack_range = 150; if(!actor.speed) { actor.speed = (autocvar_g_monster_shambler_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_shambler_speed_run); } diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 12277d1d64..5e2cc05138 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -103,7 +103,7 @@ void M_Spider_Attack_Web_Explode(entity this) Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1); RadiusDamage(this, this.realowner, 0, 0, 25, NULL, NULL, 25, this.projectiledeathtype, DMG_NOWEP, NULL); - FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && it.monsterid != MON_SPIDER.monsterid, + FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && GetResourceAmount(it, RESOURCE_HEALTH) > 0 && it.monsterid != MON_SPIDER.monsterid, { it.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime); }); @@ -151,7 +151,7 @@ void M_Spider_Attack_Web(entity this) setsize(proj, '-4 -4 -4', '4 4 4'); proj.takedamage = DAMAGE_NO; proj.damageforcescale = 0; - proj.health = 500; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, 500); proj.event_damage = func_null; proj.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, proj); @@ -227,7 +227,7 @@ METHOD(Spider, mr_anim, bool(Spider this, entity actor)) METHOD(Spider, mr_setup, bool(Spider this, entity actor)) { TC(Spider, this); - if(!actor.health) actor.health = (autocvar_g_monster_spider_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_spider_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_spider_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_spider_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_spider_speed_stop); } diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index 0a52e61090..f6c905d6d1 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -152,7 +152,7 @@ METHOD(Wyvern, mr_anim, bool(Wyvern this, entity actor)) METHOD(Wyvern, mr_setup, bool(Wyvern this, entity actor)) { TC(Wyvern, this); - if(!actor.health) actor.health = (autocvar_g_monster_wyvern_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_wyvern_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_wyvern_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_wyvern_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_wyvern_speed_stop); } diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index 297bab87dd..aaa27d21b2 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -51,7 +51,7 @@ const float zombie_anim_spawn = 30; void M_Zombie_Attack_Leap_Touch(entity this, entity toucher) { - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; vector angles_face; @@ -74,16 +74,16 @@ void M_Zombie_Attack_Leap_Touch(entity this, entity toucher) void M_Zombie_Defend_Block_End(entity this) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; setanim(this, this.anim_blockend, false, true, true); - this.armorvalue = autocvar_g_monsters_armor_blockpercent; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_monsters_armor_blockpercent); } bool M_Zombie_Defend_Block(entity this) { - this.armorvalue = 0.9; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, 0.9); this.state = MONSTER_ATTACK_MELEE; // freeze monster this.attack_finished_single[0] = time + 2.1; this.anim_finished = this.attack_finished_single[0]; @@ -100,7 +100,7 @@ bool M_Zombie_Attack(int attack_type, entity actor, entity targ, .entity weapone { case MONSTER_ATTACK_MELEE: { - if(random() < 0.3 && actor.health < 75 && actor.enemy.health > 10) + if(random() < 0.3 && GetResourceAmount(actor, RESOURCE_HEALTH) < 75 && GetResourceAmount(actor.enemy, RESOURCE_HEALTH) > 10) return M_Zombie_Defend_Block(actor); float anim_chance = random(); @@ -148,7 +148,7 @@ METHOD(Zombie, mr_pain, float(Zombie this, entity actor, float damage_take, enti METHOD(Zombie, mr_death, bool(Zombie this, entity actor)) { TC(Zombie, this); - actor.armorvalue = autocvar_g_monsters_armor_blockpercent; + SetResourceAmountExplicit(actor, RESOURCE_ARMOR, autocvar_g_monsters_armor_blockpercent); setanim(actor, ((random() > 0.5) ? actor.anim_die1 : actor.anim_die2), false, true, true); return true; @@ -180,7 +180,7 @@ METHOD(Zombie, mr_anim, bool(Zombie this, entity actor)) METHOD(Zombie, mr_setup, bool(Zombie this, entity actor)) { TC(Zombie, this); - if(!actor.health) actor.health = (autocvar_g_monster_zombie_health); + if(!GetResourceAmount(actor, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_zombie_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_zombie_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_zombie_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_zombie_speed_stop); } diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index b4861b917d..84355c7f35 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -84,7 +84,7 @@ bool Monster_ValidTarget(entity this, entity targ) || (game_stopped) || (targ.items & IT_INVISIBILITY) || (IS_SPEC(targ) || IS_OBSERVER(targ)) // don't attack spectators - || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || targ.health <= 0 || this.health <= 0)) + || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(this, RESOURCE_HEALTH) <= 0)) || (this.monster_follow == targ || targ.monster_follow == this) || (!IS_VEHICLE(targ) && (targ.flags & FL_NOTARGET)) || (!autocvar_g_monsters_typefrag && PHYS_INPUT_BUTTON_CHAT(targ)) @@ -375,7 +375,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel) return false; // already attacking if(!IS_ONGROUND(this)) return false; // not on the ground - if(this.health <= 0 || IS_DEAD(this)) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0 || IS_DEAD(this)) return false; // called when dead? if(time < this.attack_finished_single[0]) return false; // still attacking @@ -486,7 +486,7 @@ void Monster_Miniboss_Check(entity this) // g_monsters_miniboss_chance cvar or spawnflags 64 causes a monster to be a miniboss if ((this.spawnflags & MONSTERFLAG_MINIBOSS) || (chance < autocvar_g_monsters_miniboss_chance)) { - this.health += autocvar_g_monsters_miniboss_healthboost; + GiveResource(this, RESOURCE_HEALTH, autocvar_g_monsters_miniboss_healthboost); this.effects |= EF_RED; if(!this.weapon) this.weapon = WEP_VORTEX.m_id; @@ -527,10 +527,11 @@ void Monster_Dead_Fade(entity this) this.pos2 = this.angles; } this.event_damage = func_null; + this.event_heal = func_null; this.takedamage = DAMAGE_NO; setorigin(this, this.pos1); this.angles = this.pos2; - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); setmodel(this, MDL_Null); } else @@ -559,7 +560,7 @@ vector Monster_Move_Target(entity this, entity targ) // cases where the enemy may have changed their state (don't need to check everything here) if((!this.enemy) - || (IS_DEAD(this.enemy) || this.enemy.health < 1) + || (IS_DEAD(this.enemy) || GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 1) || (STAT(FROZEN, this.enemy)) || (this.enemy.flags & FL_NOTARGET) || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0) @@ -896,7 +897,7 @@ void Monster_Reset(entity this) Unfreeze(this); // remove any icy remains - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.velocity = '0 0 0'; this.enemy = NULL; this.goalentity = NULL; @@ -906,11 +907,11 @@ void Monster_Reset(entity this) void Monster_Dead_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - if(this.health <= -50) // 100 health until gone? + if(GetResourceAmount(this, RESOURCE_HEALTH) <= -50) // 100 health until gone? { Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); @@ -932,7 +933,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) if(STAT(FROZEN, this)) { Unfreeze(this); // remove any icy remains - this.health = 0; // reset by Unfreeze + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // reset by Unfreeze (TODO) } monster_dropitem(this, attacker); @@ -956,6 +957,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) _setmodel(this, this.mdl_dead); this.event_damage = ((gibbed) ? func_null : Monster_Dead_Damage); + this.event_heal = func_null; this.solid = SOLID_CORPSE; this.takedamage = DAMAGE_AIM; this.deadflag = DEAD_DEAD; @@ -1000,7 +1002,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL) return; - vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage); + vector v = healtharmor_applydamage(100, GetResourceAmount(this, RESOURCE_ARMOR) / 100, deathtype, damage); float take = v.x; //float save = v.y; @@ -1009,12 +1011,12 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage if(take) { - this.health -= take; + TakeResource(this, RESOURCE_HEALTH, take); Monster_Sound(this, monstersound_pain, 1.2, true, CH_PAIN); } if(this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); this.dmg_time = time; @@ -1032,7 +1034,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); } - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { if(deathtype == DEATH_KILL.m_id) this.candrop = false; // killed by mobkill command @@ -1041,13 +1043,13 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage SUB_UseTargets(this, attacker, this.enemy); this.target2 = this.oldtarget2; // reset to original target on death, incase we respawn - Monster_Dead(this, attacker, (this.health <= -100 || deathtype == DEATH_KILL.m_id)); + Monster_Dead(this, attacker, (GetResourceAmount(this, RESOURCE_HEALTH) <= -100 || deathtype == DEATH_KILL.m_id)); WaypointSprite_Kill(this.sprite); MUTATOR_CALLHOOK(MonsterDies, this, attacker, deathtype); - if(this.health <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed + if(GetResourceAmount(this, RESOURCE_HEALTH) <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed { Violence_GibSplash(this, 1, 0.5, attacker); @@ -1057,6 +1059,18 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage } } +bool Monster_Heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + if(targ.sprite) + WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH)); + return true; +} + // don't check for enemies, just keep walking in a straight line void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) { @@ -1150,11 +1164,11 @@ void Monster_Frozen_Think(entity this) if(STAT(FROZEN, this) == 2) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + this.ticrate * this.revive_speed, 1); - this.health = max(1, STAT(REVIVE_PROGRESS, this) * this.max_health); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * this.max_health)); this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); if(STAT(REVIVE_PROGRESS, this) >= 1) Unfreeze(this); @@ -1162,15 +1176,15 @@ void Monster_Frozen_Think(entity this) else if(STAT(FROZEN, this) == 3) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - this.ticrate * this.revive_speed, 1); - this.health = max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this) ); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); - if(this.health < 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) { Unfreeze(this); - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); if(this.event_damage) this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0'); } @@ -1210,7 +1224,7 @@ void Monster_Think(entity this) if(this.monster_lifetime && time >= this.monster_lifetime) { - Damage(this, this, this, this.health + this.max_health, DEATH_KILL.m_id, DMG_NOWEP, this.origin, this.origin); + Damage(this, this, this, GetResourceAmount(this, RESOURCE_HEALTH) + this.max_health, DEATH_KILL.m_id, DMG_NOWEP, this.origin, this.origin); return; } @@ -1242,8 +1256,8 @@ bool Monster_Spawn_Setup(entity this) mon.mr_setup(mon, this); // ensure some basic needs are met - if(!this.health) { this.health = 100; } - if(!this.armorvalue) { this.armorvalue = bound(0.2, 0.5 * MONSTER_SKILLMOD(this), 0.9); } + if(!GetResourceAmount(this, RESOURCE_HEALTH)) { SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); } + if(!GetResourceAmount(this, RESOURCE_ARMOR)) { SetResourceAmountExplicit(this, RESOURCE_ARMOR, bound(0.2, 0.5 * MONSTER_SKILLMOD(this), 0.9)); } if(!this.target_range) { this.target_range = autocvar_g_monsters_target_range; } if(!this.respawntime) { this.respawntime = autocvar_g_monsters_respawn_delay; } if(!this.monster_moveflags) { this.monster_moveflags = MONSTER_MOVE_WANDER; } @@ -1253,13 +1267,13 @@ bool Monster_Spawn_Setup(entity this) if(!(this.spawnflags & MONSTERFLAG_RESPAWNED)) { Monster_Miniboss_Check(this); - this.health *= MONSTER_SKILLMOD(this); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH) * MONSTER_SKILLMOD(this)); if(!this.skin) this.skin = rint(random() * 4); } - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); this.pain_finished = this.nextthink; if(IS_PLAYER(this.monster_follow)) @@ -1288,7 +1302,7 @@ bool Monster_Spawn_Setup(entity this) if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE)) { WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); } } @@ -1353,6 +1367,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) this.damagedbycontents = true; this.monsterid = mon_id; this.event_damage = Monster_Damage; + this.event_heal = Monster_Heal; settouch(this, Monster_Touch); this.use = Monster_Use; this.solid = SOLID_BBOX; diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index 4be441dc10..27f71b56c2 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -544,7 +544,7 @@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate) 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) + if (GetResourceAmount(frag_target, RESOURCE_ARMOR)) { amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, GetResourceAmount(frag_target, RESOURCE_ARMOR)); diff --git a/qcsrc/common/mutators/mutator/instagib/items.qh b/qcsrc/common/mutators/mutator/instagib/items.qh index 95f4f3210c..3f8d087166 100644 --- a/qcsrc/common/mutators/mutator/instagib/items.qh +++ b/qcsrc/common/mutators/mutator/instagib/items.qh @@ -18,8 +18,8 @@ SOUND(VaporizerCells, Item_Sound("itempickup")); int autocvar_g_instagib_ammo_drop; void ammo_vaporizercells_init(Pickup this, entity item) { - if(!item.ammo_cells) - item.ammo_cells = autocvar_g_instagib_ammo_drop; + if(!GetResourceAmount(item, RESOURCE_CELLS)) + SetResourceAmountExplicit(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop); } #endif REGISTER_ITEM(VaporizerCells, Ammo) { diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc index e68c687bde..443fe24781 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc @@ -7,10 +7,10 @@ MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) { entity proj = M_ARGV(1, entity); - if(proj.health) + if(GetResourceAmount(proj, RESOURCE_HEALTH)) { // disable health which in effect disables damage calculations - proj.health = 0; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, 0); } } diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 7da7c0709b..68a3af3baf 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -978,7 +978,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 SetResourceAmount(_nade, RESOURCE_HEALTH, autocvar_g_nades_nade_health); - _nade.max_health = _nade.health; + _nade.max_health = GetResourceAmount(_nade, RESOURCE_HEALTH); _nade.takedamage = DAMAGE_AIM; _nade.event_damage = nade_damage; setcefc(_nade, func_null); diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc index a691b866f7..1fdf5fd7aa 100644 --- a/qcsrc/common/mutators/mutator/nades/net.qc +++ b/qcsrc/common/mutators/mutator/nades/net.qc @@ -32,7 +32,7 @@ void orb_setup(entity e) e.draw = orb_draw; IL_PUSH(g_drawables, e); - e.health = 255; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, 255); set_movetype(e, MOVETYPE_NONE); e.solid = SOLID_NOT; e.drawmask = MASK_NORMAL; 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 540edc69d9..37dac8f931 100644 --- a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc @@ -108,7 +108,7 @@ REGISTER_MUTATOR(nt, expr_evaluate(cvar_string("g_new_toys")) && !MUTATOR_IS_ENA .string new_toys; float autocvar_g_new_toys_autoreplace; -bool autocvar_g_new_toys_use_pickupsound = true; +bool autocvar_g_new_toys_use_pickupsound = false; const float NT_AUTOREPLACE_NEVER = 0; const float NT_AUTOREPLACE_ALWAYS = 1; const float NT_AUTOREPLACE_RANDOM = 2; diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc index da5dcc234e..586deda3ef 100644 --- a/qcsrc/common/mutators/mutator/nix/sv_nix.qc +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qc @@ -130,7 +130,7 @@ void NIX_GiveCurrentWeapon(entity this) } // get weapon info - entity e = Weapons_from(nix_weapon); + entity wpn = Weapons_from(nix_weapon); if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round! { @@ -142,7 +142,7 @@ void NIX_GiveCurrentWeapon(entity this) SetResourceAmount(this, RESOURCE_FUEL, 0); if(this.items & IT_UNLIMITED_WEAPON_AMMO) { - switch (e.ammo_type) + switch (wpn.ammo_type) { case RESOURCE_SHELLS: SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_pickup_shells_max); break; case RESOURCE_BULLETS: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_pickup_nails_max); break; @@ -154,7 +154,7 @@ void NIX_GiveCurrentWeapon(entity this) } else { - switch (e.ammo_type) + switch (wpn.ammo_type) { case RESOURCE_SHELLS: SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammo_shells); break; case RESOURCE_BULLETS: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammo_nails); break; @@ -171,15 +171,15 @@ void NIX_GiveCurrentWeapon(entity this) else Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - e.wr_resetplayer(e, this); + wpn.wr_resetplayer(wpn, this); // all weapons must be fully loaded when we spawn - if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars + if (wpn.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars { - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { .entity weaponentity = weaponentities[slot]; - this.(weaponentity).(weapon_load[nix_weapon]) = e.reloading_ammo; + this.(weaponentity).(weapon_load[nix_weapon]) = wpn.reloading_ammo; } } @@ -195,7 +195,7 @@ void NIX_GiveCurrentWeapon(entity this) if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr) { - switch (e.ammo_type) + switch (wpn.ammo_type) { case RESOURCE_SHELLS: GiveResource(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammoincr_shells); break; case RESOURCE_BULLETS: GiveResource(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammoincr_nails); break; @@ -211,20 +211,19 @@ void NIX_GiveCurrentWeapon(entity this) STAT(WEAPONS, this) = '0 0 0'; if(g_nix_with_blaster) STAT(WEAPONS, this) |= WEPSET(BLASTER); - STAT(WEAPONS, this) |= e.m_wepset; + STAT(WEAPONS, this) |= wpn.m_wepset; - Weapon w = Weapons_from(nix_weapon); - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(this.(weaponentity).m_weapon == WEP_Null && slot != 0) - continue; + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if (this.(weaponentity).m_weapon == WEP_Null && slot != 0) + continue; - if(this.(weaponentity).m_switchweapon != w) - if(!client_hasweapon(this, this.(weaponentity).m_switchweapon, weaponentity, true, false)) + if (this.(weaponentity).m_switchweapon != wpn) + if (!client_hasweapon(this, this.(weaponentity).m_switchweapon, weaponentity, true, false)) { - if(client_hasweapon(this, w, weaponentity, true, false)) - W_SwitchWeapon(this, w, weaponentity); + if (client_hasweapon(this, wpn, weaponentity, true, false)) + W_SwitchWeapon(this, wpn, weaponentity); } } } diff --git a/qcsrc/common/mutators/mutator/overkill/okrpc.qc b/qcsrc/common/mutators/mutator/overkill/okrpc.qc index c06ca5b78c..37d82e22ef 100644 --- a/qcsrc/common/mutators/mutator/overkill/okrpc.qc +++ b/qcsrc/common/mutators/mutator/overkill/okrpc.qc @@ -28,15 +28,15 @@ void W_OverkillRocketPropelledChainsaw_Touch (entity this, entity toucher) void W_OverkillRocketPropelledChainsaw_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_OverkillRocketPropelledChainsaw_Explode_think); } @@ -78,7 +78,7 @@ void W_OverkillRocketPropelledChainsaw_Attack (Weapon thiswep, entity actor, .en missile.takedamage = DAMAGE_YES; missile.damageforcescale = WEP_CVAR_PRI(okrpc, damageforcescale); - missile.health = WEP_CVAR_PRI(okrpc, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_PRI(okrpc, health)); missile.event_damage = W_OverkillRocketPropelledChainsaw_Damage; missile.damagedbycontents = true; IL_PUSH(g_damagedbycontents, missile); 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 61c302c3e7..ee2a5be7f5 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 @@ -91,7 +91,7 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) if (PHYS_INPUT_BUTTON_CHAT(it)) continue; if (!SAME_TEAM(player, it)) continue; - if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && it.health < autocvar_g_balance_health_regenstable) continue; + if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && GetResourceAmount(it, RESOURCE_HEALTH) < autocvar_g_balance_health_regenstable) continue; if (IS_DEAD(it)) continue; if (time < it.msnt_timer) continue; if (time < it.spawnshieldtime) continue; diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc index b446c92705..56198186f1 100644 --- a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc @@ -14,7 +14,7 @@ MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) if(!IS_DEAD(frag_target)) { GiveResource(frag_attacker, RESOURCE_HEALTH, - bound(0, damage_take, frag_target.health)); + bound(0, damage_take, GetResourceAmount(frag_target, RESOURCE_HEALTH))); } } diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc index d0f01a576a..115e6ca910 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc @@ -21,18 +21,16 @@ MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) if(!STAT(FROZEN, thehook.aiment)) if(time >= game_starttime) if(DIFF_TEAM(thehook.owner, thehook.aiment) || autocvar_g_vampirehook_teamheal) - if(thehook.aiment.health > 0) + if(GetResourceAmount(thehook.aiment, RESOURCE_HEALTH) > 0) if(autocvar_g_vampirehook_damage) { thehook.last_dmg = time + autocvar_g_vampirehook_damagerate; thehook.owner.damage_dealt += autocvar_g_vampirehook_damage; Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, DMG_NOWEP, thehook.origin, '0 0 0'); - if(SAME_TEAM(thehook.owner, thehook.aiment)) - thehook.aiment.health = min(thehook.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - else - thehook.owner.health = min(thehook.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + entity targ = ((SAME_TEAM(thehook.owner, thehook.aiment)) ? thehook.aiment : thehook.owner); + Heal(targ, thehook.owner, autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); if(dmgent == thehook.owner) - dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! + TakeResource(dmgent, RESOURCE_HEALTH, autocvar_g_vampirehook_damage); // FIXME: friendly fire?! } } diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index 326a26219b..dcbb65f65c 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -34,7 +34,7 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) { if (this.max_health) { - WriteByte(MSG_ENTITY, (this.health / this.max_health) * 191.0); + WriteByte(MSG_ENTITY, (GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 191.0); } else { @@ -134,7 +134,7 @@ void Ent_WaypointSprite(entity this, bool isnew) int t = ReadByte(); if (t < 192) { - this.health = t / 191.0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, t / 191.0); this.build_finished = 0; } else @@ -142,7 +142,7 @@ void Ent_WaypointSprite(entity this, bool isnew) t = (t - 192) * 256 + ReadByte(); this.build_started = servertime; if (this.build_finished) - this.build_starthealth = bound(0, this.health, 1); + this.build_starthealth = bound(0, GetResourceAmount(this, RESOURCE_HEALTH), 1); else this.build_starthealth = 0; this.build_finished = servertime + t / 32; @@ -150,7 +150,7 @@ void Ent_WaypointSprite(entity this, bool isnew) } else { - this.health = -1; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, -1); this.build_finished = 0; } @@ -654,14 +654,14 @@ void Draw_WaypointSprite(entity this) if (time < this.build_finished + 0.25) { if (time < this.build_started) - this.health = this.build_starthealth; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.build_starthealth); else if (time < this.build_finished) - this.health = (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth); else - this.health = 1; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 1); } else - this.health = -1; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, -1); } o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t); @@ -709,7 +709,7 @@ void Draw_WaypointSprite(entity this) } draw_beginBoldFont(); - if (this.health >= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) >= 0) { float align = 0, marg; if (this.build_finished) @@ -726,7 +726,7 @@ void Draw_WaypointSprite(entity this) drawhealthbar( o, 0, - this.health, + GetResourceAmount(this, RESOURCE_HEALTH), '0 0 0', '0 0 0', SPRITE_HEALTHBAR_WIDTH * t, @@ -831,9 +831,9 @@ void WaypointSprite_UpdateSprites(entity e, entity _m1, entity _m2, entity _m3) void WaypointSprite_UpdateHealth(entity e, float f) { f = bound(0, f, e.max_health); - if (f != e.health || e.pain_finished) + if (f != GetResourceAmount(e, RESOURCE_HEALTH) || e.pain_finished) { - e.health = f; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, f); e.pain_finished = 0; e.SendFlags |= 0x80; } @@ -1160,10 +1160,10 @@ entity WaypointSprite_AttachCarrier( { WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon); - if (carrier.health) + if (GetResourceAmount(carrier, RESOURCE_HEALTH)) { WaypointSprite_UpdateMaxHealth(e, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2); - WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(carrier.health, carrier.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); + WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(GetResourceAmount(carrier, RESOURCE_HEALTH), GetResourceAmount(carrier, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); } return e; } diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index b4d14034de..a15ef80d1e 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -697,7 +697,7 @@ MSG_CENTER_NOTIF(ITEM_BUFF_DROP, N_ENABLE, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG buff!"), "") MSG_CENTER_NOTIF(ITEM_BUFF_GOT, N_ENABLE, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG buff!"), "") MSG_CENTER_NOTIF(ITEM_FUELREGEN_GOT, N_ENABLE, 0, 0, "", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1Fuel regenerator"), "") - MSG_CENTER_NOTIF(ITEM_JETPACK_GOT, N_ENABLE, 0, 0, "", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1Jet pack"), "") + MSG_CENTER_NOTIF(ITEM_JETPACK_GOT, N_ENABLE, 0, 0, "", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1Jetpack"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_DONTHAVE, N_ENABLE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_DROP, N_ENABLE, 1, 1, "item_wepname item_wepammo", CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_GOT, N_ENABLE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc index 2b9808a554..2f4ebb1ff0 100644 --- a/qcsrc/common/physics/player.qc +++ b/qcsrc/common/physics/player.qc @@ -792,7 +792,7 @@ void PM_jetpack(entity this, float maxspd_mod, float dt) #ifdef SVQC if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) - this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f; + TakeResource(this, RESOURCE_FUEL, PHYS_JETPACK_FUEL(this) * dt * fvel * f); ITEMS_STAT(this) |= IT_USING_JETPACK; diff --git a/qcsrc/common/resources.qh b/qcsrc/common/resources.qh index a562292b40..8e33c649b3 100644 --- a/qcsrc/common/resources.qh +++ b/qcsrc/common/resources.qh @@ -5,6 +5,10 @@ /// \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; +const int RESOURCE_LIMIT_NONE = -1; + /// \brief Describes the available resource types. enum { diff --git a/qcsrc/common/state.qc b/qcsrc/common/state.qc index ea936185b9..37813ef716 100644 --- a/qcsrc/common/state.qc +++ b/qcsrc/common/state.qc @@ -31,7 +31,6 @@ void PlayerScore_Attach(entity this); void ClientData_Attach(entity this); void accuracy_init(entity this); void entcs_attach(entity this); -void playerdemo_init(entity this); void anticheat_init(entity this); void W_HitPlotOpen(entity this); void bot_clientconnect(entity this); @@ -51,7 +50,6 @@ void ClientState_attach(entity this) ClientData_Attach(this); accuracy_init(this); entcs_attach(this); - playerdemo_init(this); anticheat_init(this); W_HitPlotOpen(this); @@ -61,7 +59,6 @@ void ClientState_attach(entity this) void bot_clientdisconnect(entity this); void W_HitPlotClose(entity this); void anticheat_report_to_eventlog(entity this); -void playerdemo_shutdown(entity this); void entcs_detach(entity this); void accuracy_free(entity this); void ClientData_Detach(entity this); @@ -81,6 +78,5 @@ void ClientState_detach(entity this) bot_clientdisconnect(this); anticheat_report_to_eventlog(this); - playerdemo_shutdown(this); entcs_detach(this); } diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index f4ed4f1bf4..68fa7ef578 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -824,7 +824,8 @@ float Item_GiveTo(entity item, entity player) { pickedup = true; player.items |= its; - Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); + // TODO: we probably want to show a message in the console, but not this one! + //Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); } if (item.strength_finished) @@ -1070,12 +1071,12 @@ float ammo_pickupevalfunc(entity player, entity item) if(item.itemdef.instanceOfWeaponPickup) { entity ammo = NULL; - if(item.ammo_shells) { need_shells = true; ammo = ITEM_Shells; } - else if(item.ammo_nails) { need_nails = true; ammo = ITEM_Bullets; } - else if(item.ammo_rockets) { need_rockets = true; ammo = ITEM_Rockets; } - else if(item.ammo_cells) { need_cells = true; ammo = ITEM_Cells; } - else if(item.ammo_plasma) { need_plasma = true; ammo = ITEM_Plasma; } - else if(item.ammo_fuel) { need_fuel = true; ammo = ITEM_JetpackFuel; } + if(GetResourceAmount(item, RESOURCE_SHELLS)) { need_shells = true; ammo = ITEM_Shells; } + else if(GetResourceAmount(item, RESOURCE_BULLETS)) { need_nails = true; ammo = ITEM_Bullets; } + else if(GetResourceAmount(item, RESOURCE_ROCKETS)) { need_rockets = true; ammo = ITEM_Rockets; } + else if(GetResourceAmount(item, RESOURCE_CELLS)) { need_cells = true; ammo = ITEM_Cells; } + else if(GetResourceAmount(item, RESOURCE_PLASMA)) { need_plasma = true; ammo = ITEM_Plasma; } + else if(GetResourceAmount(item, RESOURCE_FUEL)) { need_fuel = true; ammo = ITEM_JetpackFuel; } if(!ammo) return 0; @@ -1103,23 +1104,23 @@ float ammo_pickupevalfunc(entity player, entity item) float noammorating = 0.5; - if ((need_shells) && (item.ammo_shells) && (GetResourceAmount(player, RESOURCE_SHELLS) < g_pickup_shells_max)) - c = item.ammo_shells / max(noammorating, GetResourceAmount(player, RESOURCE_SHELLS)); + if ((need_shells) && GetResourceAmount(item, RESOURCE_SHELLS) && (GetResourceAmount(player, RESOURCE_SHELLS) < g_pickup_shells_max)) + c = GetResourceAmount(item, RESOURCE_SHELLS) / max(noammorating, GetResourceAmount(player, RESOURCE_SHELLS)); - if ((need_nails) && (item.ammo_nails) && (GetResourceAmount(player, RESOURCE_BULLETS) < g_pickup_nails_max)) - c = item.ammo_nails / max(noammorating, GetResourceAmount(player, RESOURCE_BULLETS)); + if ((need_nails) && GetResourceAmount(item, RESOURCE_BULLETS) && (GetResourceAmount(player, RESOURCE_BULLETS) < g_pickup_nails_max)) + c = GetResourceAmount(item, RESOURCE_BULLETS) / max(noammorating, GetResourceAmount(player, RESOURCE_BULLETS)); - if ((need_rockets) && (item.ammo_rockets) && (GetResourceAmount(player, RESOURCE_ROCKETS) < g_pickup_rockets_max)) - c = item.ammo_rockets / max(noammorating, GetResourceAmount(player, RESOURCE_ROCKETS)); + if ((need_rockets) && GetResourceAmount(item, RESOURCE_ROCKETS) && (GetResourceAmount(player, RESOURCE_ROCKETS) < g_pickup_rockets_max)) + c = GetResourceAmount(item, RESOURCE_ROCKETS) / max(noammorating, GetResourceAmount(player, RESOURCE_ROCKETS)); - if ((need_cells) && (item.ammo_cells) && (GetResourceAmount(player, RESOURCE_CELLS) < g_pickup_cells_max)) - c = item.ammo_cells / max(noammorating, GetResourceAmount(player, RESOURCE_CELLS)); + if ((need_cells) && GetResourceAmount(item, RESOURCE_CELLS) && (GetResourceAmount(player, RESOURCE_CELLS) < g_pickup_cells_max)) + c = GetResourceAmount(item, RESOURCE_CELLS) / max(noammorating, GetResourceAmount(player, RESOURCE_CELLS)); - if ((need_plasma) && (item.ammo_plasma) && (GetResourceAmount(player, RESOURCE_PLASMA) < g_pickup_plasma_max)) - c = item.ammo_plasma / max(noammorating, GetResourceAmount(player, RESOURCE_PLASMA)); + if ((need_plasma) && GetResourceAmount(item, RESOURCE_PLASMA) && (GetResourceAmount(player, RESOURCE_PLASMA) < g_pickup_plasma_max)) + c = GetResourceAmount(item, RESOURCE_PLASMA) / max(noammorating, GetResourceAmount(player, RESOURCE_PLASMA)); - if ((need_fuel) && (item.ammo_fuel) && (GetResourceAmount(player, RESOURCE_FUEL) < g_pickup_fuel_max)) - c = item.ammo_fuel / max(noammorating, GetResourceAmount(player, RESOURCE_FUEL)); + if ((need_fuel) && GetResourceAmount(item, RESOURCE_FUEL) && (GetResourceAmount(player, RESOURCE_FUEL) < g_pickup_fuel_max)) + c = GetResourceAmount(item, RESOURCE_FUEL) / max(noammorating, GetResourceAmount(player, RESOURCE_FUEL)); rating *= min(c, 2); if(wpn) @@ -1132,8 +1133,8 @@ float healtharmor_pickupevalfunc(entity player, entity item) float c = 0; float rating = item.bot_pickupbasevalue; - float itemarmor = item.armorvalue; - float itemhealth = item.health; + float itemarmor = GetResourceAmount(item, RESOURCE_ARMOR); + float itemhealth = GetResourceAmount(item, RESOURCE_HEALTH); if(item.item_group) { @@ -1141,11 +1142,11 @@ float healtharmor_pickupevalfunc(entity player, entity item) itemhealth *= min(4, item.item_group_count); } - if (itemarmor && (player.armorvalue < item.max_armorvalue)) - c = itemarmor / max(1, player.armorvalue * 2/3 + player.health * 1/3); + if (itemarmor && (GetResourceAmount(player, RESOURCE_ARMOR) < item.max_armorvalue)) + c = itemarmor / max(1, GetResourceAmount(player, RESOURCE_ARMOR) * 2/3 + GetResourceAmount(player, RESOURCE_HEALTH) * 1/3); - if (itemhealth && (player.health < item.max_health)) - c = itemhealth / max(1, player.health); + if (itemhealth && (GetResourceAmount(player, RESOURCE_HEALTH) < item.max_health)) + c = itemhealth / max(1, GetResourceAmount(player, RESOURCE_HEALTH)); rating *= min(2, c); return rating; @@ -1334,7 +1335,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(def.instanceOfPowerup) this.ItemStatus |= ITS_ANIMATE1; - if(this.armorvalue || this.health) + if(GetResourceAmount(this, RESOURCE_ARMOR) || GetResourceAmount(this, RESOURCE_HEALTH)) this.ItemStatus |= ITS_ANIMATE2; } @@ -1554,14 +1555,14 @@ spawnfunc(target_items) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, this.superweapons_finished * boolean(this.items & IT_SUPERWEAPON), "superweapons"); this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, boolean(this.items & ITEM_Jetpack.m_itemid), "jetpack"); this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, boolean(this.items & ITEM_JetpackRegen.m_itemid), "fuel_regen"); - if(this.ammo_shells != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_shells), "shells"); - if(this.ammo_nails != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_nails), "nails"); - if(this.ammo_rockets != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_rockets), "rockets"); - if(this.ammo_cells != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_cells), "cells"); - if(this.ammo_plasma != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_plasma), "plasma"); - if(this.ammo_fuel != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_fuel), "fuel"); - if(this.health != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.health), "health"); - if(this.armorvalue != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.armorvalue), "armor"); + if(GetResourceAmount(this, RESOURCE_SHELLS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_SHELLS)), "shells"); + if(GetResourceAmount(this, RESOURCE_BULLETS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_BULLETS)), "nails"); + if(GetResourceAmount(this, RESOURCE_ROCKETS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_ROCKETS)), "rockets"); + if(GetResourceAmount(this, RESOURCE_CELLS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_CELLS)), "cells"); + if(GetResourceAmount(this, RESOURCE_PLASMA) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_PLASMA)), "plasma"); + if(GetResourceAmount(this, RESOURCE_FUEL) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_FUEL)), "fuel"); + if(GetResourceAmount(this, RESOURCE_HEALTH) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_HEALTH)), "health"); + if(GetResourceAmount(this, RESOURCE_ARMOR) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_ARMOR)), "armor"); FOREACH(Buffs, it != BUFF_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(STAT(BUFFS, this) & (it.m_itemid)), it.m_name)); FOREACH(Weapons, it != WEP_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(STAT(WEAPONS, this) & (it.m_wepset)), it.netname)); } @@ -1661,6 +1662,31 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa else if(v0 > v1) e.(regenfield) = max(e.(regenfield), time + regentime); } +bool GiveResourceValue(entity e, int resource_type, int op, int val) +{ + int v0 = GetResourceAmount(e, resource_type); + switch (op) + { + case OP_SET: + SetResourceAmount(e, resource_type, val); + break; + case OP_MIN: + SetResourceAmount(e, resource_type, max(v0, val)); // min 100 cells = at least 100 cells + break; + case OP_MAX: + SetResourceAmount(e, resource_type, min(v0, val)); + break; + case OP_PLUS: + SetResourceAmount(e, resource_type, v0 + val); + break; + case OP_MINUS: + SetResourceAmount(e, resource_type, v0 - val); + break; + } + int v1 = GetResourceAmount(e, resource_type); + return v0 != v1; +} + float GiveItems(entity e, float beginarg, float endarg) { float got, i, val, op; @@ -1693,14 +1719,14 @@ float GiveItems(entity e, float beginarg, float endarg) PREGIVE(e, strength_finished); PREGIVE(e, invincible_finished); PREGIVE(e, superweapons_finished); - PREGIVE(e, ammo_nails); - PREGIVE(e, ammo_cells); - PREGIVE(e, ammo_plasma); - PREGIVE(e, ammo_shells); - PREGIVE(e, ammo_rockets); - PREGIVE(e, ammo_fuel); - PREGIVE(e, armorvalue); - PREGIVE(e, health); + PREGIVE_RESOURCE(e, RESOURCE_BULLETS); + PREGIVE_RESOURCE(e, RESOURCE_CELLS); + PREGIVE_RESOURCE(e, RESOURCE_PLASMA); + PREGIVE_RESOURCE(e, RESOURCE_SHELLS); + PREGIVE_RESOURCE(e, RESOURCE_ROCKETS); + PREGIVE_RESOURCE(e, RESOURCE_FUEL); + PREGIVE_RESOURCE(e, RESOURCE_ARMOR); + PREGIVE_RESOURCE(e, RESOURCE_HEALTH); for(i = beginarg; i < endarg; ++i) { @@ -1737,19 +1763,19 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); case "all": got += GiveBit(e, items, ITEM_Jetpack.m_itemid, op, val); - got += GiveValue(e, health, op, val); - got += GiveValue(e, armorvalue, op, val); + got += GiveResourceValue(e, RESOURCE_HEALTH, op, val); + got += GiveResourceValue(e, RESOURCE_ARMOR, op, val); case "allweapons": FOREACH(Weapons, it != WEP_Null && !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED), got += GiveWeapon(e, it.m_id, op, val)); //case "allbuffs": // all buffs makes a player god, do not want! //FOREACH(Buffs, it != BUFF_Null, got += GiveBuff(e, it.m_itemid, op, val)); case "allammo": - got += GiveValue(e, ammo_cells, op, val); - got += GiveValue(e, ammo_plasma, op, val); - got += GiveValue(e, ammo_shells, op, val); - got += GiveValue(e, ammo_nails, op, val); - got += GiveValue(e, ammo_rockets, op, val); - got += GiveValue(e, ammo_fuel, op, val); + got += GiveResourceValue(e, RESOURCE_CELLS, op, val); + got += GiveResourceValue(e, RESOURCE_PLASMA, op, val); + got += GiveResourceValue(e, RESOURCE_SHELLS, op, val); + got += GiveResourceValue(e, RESOURCE_BULLETS, op, val); + got += GiveResourceValue(e, RESOURCE_ROCKETS, op, val); + got += GiveResourceValue(e, RESOURCE_FUEL, op, val); break; case "unlimited_ammo": got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); @@ -1776,29 +1802,29 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveValue(e, superweapons_finished, op, val); break; case "cells": - got += GiveValue(e, ammo_cells, op, val); + got += GiveResourceValue(e, RESOURCE_CELLS, op, val); break; case "plasma": - got += GiveValue(e, ammo_plasma, op, val); + got += GiveResourceValue(e, RESOURCE_PLASMA, op, val); break; case "shells": - got += GiveValue(e, ammo_shells, op, val); + got += GiveResourceValue(e, RESOURCE_SHELLS, op, val); break; case "nails": case "bullets": - got += GiveValue(e, ammo_nails, op, val); + got += GiveResourceValue(e, RESOURCE_BULLETS, op, val); break; case "rockets": - got += GiveValue(e, ammo_rockets, op, val); + got += GiveResourceValue(e, RESOURCE_ROCKETS, op, val); break; case "health": - got += GiveValue(e, health, op, val); + got += GiveResourceValue(e, RESOURCE_HEALTH, op, val); break; case "armor": - got += GiveValue(e, armorvalue, op, val); + got += GiveResourceValue(e, RESOURCE_ARMOR, op, val); break; case "fuel": - got += GiveValue(e, ammo_fuel, op, val); + got += GiveResourceValue(e, RESOURCE_FUEL, op, val); break; default: FOREACH(Buffs, it != BUFF_Null && Buff_UndeprecateName(cmd) == it.m_name, @@ -1829,14 +1855,14 @@ float GiveItems(entity e, float beginarg, float endarg) POSTGIVE_VALUE(e, strength_finished, 1, SND_POWERUP, SND_POWEROFF); POSTGIVE_VALUE(e, invincible_finished, 1, SND_Shield, SND_POWEROFF); //POSTGIVE_VALUE(e, superweapons_finished, 1, SND_Null, SND_Null); - POSTGIVE_VALUE(e, ammo_nails, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_cells, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_plasma, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_shells, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_rockets, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE_ROT(e, armorvalue, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_ARMOR25, SND_Null); - POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_MEGAHEALTH, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_BULLETS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_CELLS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_PLASMA, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_SHELLS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_ROCKETS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE_ROT(e, RESOURCE_FUEL, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE_ROT(e, RESOURCE_ARMOR, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_ARMOR25, SND_Null); + POSTGIVE_RESOURCE_ROT(e, RESOURCE_HEALTH, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_MEGAHEALTH, SND_Null); if(e.superweapons_finished <= 0) if(STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) diff --git a/qcsrc/common/t_items.qh b/qcsrc/common/t_items.qh index 50228a0a8f..9fdb0b0925 100644 --- a/qcsrc/common/t_items.qh +++ b/qcsrc/common/t_items.qh @@ -128,8 +128,11 @@ spawnfunc(target_items); #define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = STAT(WEAPONS, e) #define PREGIVE(e,f) float save_##f; save_##f = (e).f +#define PREGIVE_RESOURCE(e,f) float save_##f = GetResourceAmount((e), (f)) #define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(STAT(WEAPONS, e) & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr) #define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr) +#define POSTGIVE_RESOURCE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, GetResourceAmount((e), (f)), t, snd_incr, snd_decr) +#define POSTGIVE_RESOURCE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e),save_##f,GetResourceAmount((e),(f)),rotfield,rottime,regenfield,regentime);GiveSound((e),save_##f,GetResourceAmount((e),(f)),t,snd_incr,snd_decr) #define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) #define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index ba7b5d01bb..ac68003a6c 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -37,7 +37,7 @@ void turret_draw(entity this) this.tur_head.angles += dt * this.tur_head.avelocity; - if (this.health < 127) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 127) { dt = random(); @@ -45,11 +45,11 @@ void turret_draw(entity this) te_spark(this.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); } - if(this.health < 85) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 85) if(dt < 0.01) pointparticles(EFFECT_SMOKE_LARGE, (this.origin + (randomvec() * 80)), '0 0 0', 1); - if(this.health < 32) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 32) if(dt < 0.015) pointparticles(EFFECT_SMOKE_SMALL, (this.origin + (randomvec() * 80)), '0 0 0', 1); @@ -180,7 +180,7 @@ void turret_draw2d(entity this) drawhealthbar( o, 0, - this.health / 255, + GetResourceAmount(this, RESOURCE_HEALTH) / 255, '0 0 0', '0 0 0', 0.5 * SPRITE_HEALTHBAR_WIDTH * t, @@ -221,7 +221,7 @@ void turret_construct(entity this, bool isnew) set_movetype(this.tur_head, MOVETYPE_NOCLIP); set_movetype(this, MOVETYPE_NOCLIP); this.tur_head.angles = this.angles; - this.health = 255; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 255); this.solid = SOLID_BBOX; this.tur_head.solid = SOLID_NOT; set_movetype(this, MOVETYPE_NOCLIP); @@ -422,13 +422,15 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) } _tmp = ReadByte(); - if(_tmp == 0 && this.health != 0) + float myhp = GetResourceAmount(this, RESOURCE_HEALTH); + if(_tmp == 0 && myhp != 0) turret_die(this); - else if(this.health && this.health != _tmp) + else if(myhp && myhp > _tmp) this.helpme = servertime + 10; + else if(myhp && myhp < _tmp) + this.helpme = 0; // we're being healed, don't spam help me waypoints - this.health = _tmp; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp); } - //this.enemy.health = this.health / 255; return true; } diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index 06f8bab9c9..b68aca16fe 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -182,9 +182,10 @@ void turret_die(entity this) this.tur_head.solid = this.solid; this.event_damage = func_null; + this.event_heal = func_null; this.takedamage = DAMAGE_NO; - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // Go boom //RadiusDamage (this,this, min(this.ammo,50),min(this.ammo,50) * 0.25,250,NULL,min(this.ammo,50)*5,DEATH_TURRET,NULL); @@ -230,7 +231,7 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage, return; } - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); // thorw head slightly off aim when hit? if (this.damage_flags & TFL_DMG_HEADSHAKE) @@ -244,10 +245,12 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage, if (this.turret_flags & TUR_FLAG_MOVE) this.velocity = this.velocity + vforce; - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.event_damage = func_null; this.tur_head.event_damage = func_null; + this.event_heal = func_null; + this.tur_head.event_heal = func_null; this.takedamage = DAMAGE_NO; this.nextthink = time; setthink(this, turret_die); @@ -256,6 +259,17 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage, this.SendFlags |= TNSF_STATUS; } +bool turret_heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + targ.SendFlags |= TNSF_STATUS; + return true; +} + void turret_think(entity this); void turret_respawn(entity this) { @@ -268,10 +282,11 @@ void turret_respawn(entity this) this.solid = SOLID_BBOX; this.takedamage = DAMAGE_AIM; this.event_damage = turret_damage; + this.event_heal = turret_heal; this.avelocity = '0 0 0'; this.tur_head.avelocity = this.avelocity; this.tur_head.angles = this.idle_aim; - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.enemy = NULL; this.volly_counter = this.shot_volly; this.ammo = this.ammo_max; @@ -350,10 +365,10 @@ bool turret_send(entity this, entity to, float sf) { WriteByte(MSG_ENTITY, this.team); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) WriteByte(MSG_ENTITY, 0); else - WriteByte(MSG_ENTITY, ceil((this.health / this.max_health) * 255)); + WriteByte(MSG_ENTITY, ceil((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 255)); } return true; @@ -384,7 +399,7 @@ void load_unit_settings(entity ent, bool is_reload) ent.tur_head.angles = '0 0 0'; } - ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health; + SetResourceAmountExplicit(ent, RESOURCE_HEALTH, cvar(strcat(sbase,"_health")) * ent.turret_scale_health); ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn; ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage; @@ -451,9 +466,9 @@ void turret_projectile_touch(entity this, entity toucher) void turret_projectile_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce) { this.velocity += vforce; - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); //this.realowner = attacker; // Dont change realowner, it does not make much sense for turrets - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, this.owner, turret_projectile_explode); } @@ -483,7 +498,7 @@ entity turret_projectile(entity actor, Sound _snd, float _size, float _health, f PROJECTILE_MAKETRIGGER(proj); if(_health) { - proj.health = _health; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, _health); proj.takedamage = DAMAGE_YES; proj.event_damage = turret_projectile_damage; } @@ -720,7 +735,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl if (e_target.vehicle_health <= 0) return -6; } - else if (e_target.health <= 0) + else if (GetResourceAmount(e_target, RESOURCE_HEALTH) <= 0) return -6; else if(STAT(FROZEN, e_target) > 0) return -6; @@ -1292,7 +1307,7 @@ bool turret_initialize(entity this, Turret tur) if(!this.team || !teamplay) { this.team = FLOAT_MAX; } if(!this.ticrate) { this.ticrate = ((this.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); } - if(!this.health) { this.health = 1000; } + if(!GetResourceAmount(this, RESOURCE_HEALTH)) { SetResourceAmountExplicit(this, RESOURCE_HEALTH, 1000); } if(!this.shot_refire) { this.shot_refire = 1; } if(!this.tur_shotorg) { this.tur_shotorg = '50 0 50'; } if(!this.turret_flags) { this.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; } @@ -1349,7 +1364,7 @@ bool turret_initialize(entity this, Turret tur) this.effects = EF_NODRAW; this.netname = tur.turret_name; this.ticrate = bound(sys_frametime, this.ticrate, 60); - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); this.target_validate_flags = this.target_select_flags; this.ammo = this.ammo_max; this.ammo_recharge *= this.ticrate; @@ -1360,6 +1375,7 @@ bool turret_initialize(entity this, Turret tur) this.idle_aim = '0 0 0'; this.turret_firecheckfunc = turret_firecheck; this.event_damage = turret_damage; + this.event_heal = turret_heal; this.use = turret_use; this.bot_attack = true; this.nextthink = time + 1; diff --git a/qcsrc/common/turrets/turret/ewheel.qc b/qcsrc/common/turrets/turret/ewheel.qc index 9a9001c42d..c0a0b177ee 100644 --- a/qcsrc/common/turrets/turret/ewheel.qc +++ b/qcsrc/common/turrets/turret/ewheel.qc @@ -228,17 +228,17 @@ void ewheel_draw(entity this) setorigin(this, this.origin + this.velocity * dt); this.tur_head.angles += dt * this.tur_head.avelocity; - if (this.health < 127) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 127) if(random() < 0.05) te_spark(this.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); } - METHOD(EWheel, tr_setup, void(EWheel this, entity it)) - { - it.gravity = 1; - set_movetype(it, MOVETYPE_BOUNCE); - it.move_time = time; - it.draw = ewheel_draw; - } +METHOD(EWheel, tr_setup, void(EWheel this, entity it)) +{ + it.gravity = 1; + set_movetype(it, MOVETYPE_BOUNCE); + it.move_time = time; + it.draw = ewheel_draw; +} #endif // CSQC diff --git a/qcsrc/common/turrets/turret/hk_weapon.qc b/qcsrc/common/turrets/turret/hk_weapon.qc index 3141b3d10f..b68bfb7730 100644 --- a/qcsrc/common/turrets/turret/hk_weapon.qc +++ b/qcsrc/common/turrets/turret/hk_weapon.qc @@ -58,7 +58,7 @@ void turret_hk_missile_think(entity this) //if (this.cnt < time) // turret_hk_missile_explode(); - if (IS_DEAD(this.enemy)) + if (IS_DEAD(this.enemy) || IS_SPEC(this.enemy) || IS_OBSERVER(this.enemy)) this.enemy = NULL; // Pick the closest valid target. @@ -251,7 +251,7 @@ bool hk_is_valid_target(entity this, entity proj, entity targ) return false; // Cant touch this - if ((targ.takedamage == DAMAGE_NO) || (targ.health < 0)) + if ((targ.takedamage == DAMAGE_NO) || (GetResourceAmount(targ, RESOURCE_HEALTH) < 0)) return false; // player diff --git a/qcsrc/common/turrets/turret/walker.qc b/qcsrc/common/turrets/turret/walker.qc index 93b9483def..6aa0865e69 100644 --- a/qcsrc/common/turrets/turret/walker.qc +++ b/qcsrc/common/turrets/turret/walker.qc @@ -86,10 +86,10 @@ void walker_rocket_touch(entity this, entity toucher) void walker_rocket_damage(entity this, entity inflictor, entity attacker, float damage, float deathtype, .entity weaponentity, vector hitloc, vector vforce) { - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.velocity = this.velocity + vforce; - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, this.owner, walker_rocket_explode); } @@ -218,7 +218,7 @@ void walker_fire_rocket(entity this, vector org) rocket.bot_dodgerating = 50; rocket.takedamage = DAMAGE_YES; rocket.damageforcescale = 2; - rocket.health = 25; + SetResourceAmountExplicit(rocket, RESOURCE_HEALTH, 25); rocket.tur_shotorg = randomvec() * 512; rocket.cnt = time + 1; rocket.enemy = this.enemy; @@ -629,17 +629,17 @@ void walker_draw(entity this) setorigin(this, this.origin + this.velocity * dt); this.tur_head.angles += dt * this.tur_head.avelocity; - if (this.health < 127) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 127) if(random() < 0.15) te_spark(this.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); } - METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) - { - it.gravity = 1; - set_movetype(it, MOVETYPE_BOUNCE); - it.move_time = time; - it.draw = walker_draw; - } +METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) +{ + it.gravity = 1; + set_movetype(it, MOVETYPE_BOUNCE); + it.move_time = time; + it.draw = walker_draw; +} #endif // CSQC diff --git a/qcsrc/common/vehicles/sv_vehicles.qc b/qcsrc/common/vehicles/sv_vehicles.qc index 0eaf69eac6..df0f5d9169 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qc +++ b/qcsrc/common/vehicles/sv_vehicles.qc @@ -205,9 +205,9 @@ void vehicles_projectile_damage(entity this, entity inflictor, entity attacker, if(inflictor.owner == this.owner) return; - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.velocity += force; - if(this.health < 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) { this.takedamage = DAMAGE_NO; this.event_damage = func_null; @@ -282,7 +282,7 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound, { proj.takedamage = DAMAGE_AIM; proj.event_damage = vehicles_projectile_damage; - proj.health = _health; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, _health); } else proj.flags |= FL_NOTARGET; @@ -727,6 +727,20 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag } } +bool vehicles_heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + //if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + if(targ.vehicle_health <= 0 || targ.vehicle_health >= true_limit) + return false; + + targ.vehicle_health = min(targ.vehicle_health + amount, true_limit); + //GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + //if(targ.owner) + //targ.owner.vehicle_health = (targ.vehicle_health / targ.max_health) * 100; + return true; +} + bool vehicles_crushable(entity e) { if(IS_PLAYER(e) && time >= e.vehicle_enter_delay) @@ -1005,6 +1019,7 @@ void vehicles_enter(entity pl, entity veh) setsize(pl, STAT(PL_MIN, pl), STAT(PL_MAX, pl)); veh.event_damage = vehicles_damage; + veh.event_heal = vehicles_heal; veh.nextthink = 0; pl.items &= ~IT_USING_JETPACK; pl.angles = veh.angles; @@ -1118,6 +1133,7 @@ void vehicles_spawn(entity this) this.owner = NULL; settouch(this, vehicles_touch); this.event_damage = vehicles_damage; + this.event_heal = vehicles_heal; this.reset = vehicles_reset; this.iscreature = true; this.teleportable = false; // no teleporting for vehicles, too buggy @@ -1230,6 +1246,7 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) this.vehicleid = info.vehicleid; this.PlayerPhysplug = info.PlayerPhysplug; this.event_damage = func_null; + this.event_heal = func_null; settouch(this, vehicles_touch); setthink(this, vehicles_spawn); this.nextthink = time; diff --git a/qcsrc/common/vehicles/sv_vehicles.qh b/qcsrc/common/vehicles/sv_vehicles.qh index d0f63c96a3..0cc9da56ea 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qh +++ b/qcsrc/common/vehicles/sv_vehicles.qh @@ -45,14 +45,14 @@ float autocvar_g_vehicles_weapon_damagerate = 2; .entity gunner1; .entity gunner2; -.float vehicle_health = _STAT(VEHICLESTAT_HEALTH); /// If ent is player this is 0..100 indicating precentage of health left on vehicle. If ent is vehile, this is the real health value. -.float vehicle_energy = _STAT(VEHICLESTAT_ENERGY); /// If ent is player this is 0..100 indicating precentage of energy left on vehicle. If ent is vehile, this is the real energy value. -.float vehicle_shield = _STAT(VEHICLESTAT_SHIELD); /// If ent is player this is 0..100 indicating precentage of shield left on vehicle. If ent is vehile, this is the real shield value. +.float vehicle_health = _STAT(VEHICLESTAT_HEALTH); /// If ent is player this is 0..100 indicating precentage of health left on vehicle. If ent is vehicle, this is the real health value. +.float vehicle_energy = _STAT(VEHICLESTAT_ENERGY); /// If ent is player this is 0..100 indicating precentage of energy left on vehicle. If ent is vehicle, this is the real energy value. +.float vehicle_shield = _STAT(VEHICLESTAT_SHIELD); /// If ent is player this is 0..100 indicating precentage of shield left on vehicle. If ent is vehicle, this is the real shield value. -.float vehicle_ammo1 = _STAT(VEHICLESTAT_AMMO1); /// If ent is player this is 0..100 indicating percentage of primary ammo left UNLESS value is already stored in vehicle_energy. If ent is vehile, this is the real ammo1 value. -.float vehicle_reload1 = _STAT(VEHICLESTAT_RELOAD1); /// If ent is player this is 0..100 indicating percentage of primary reload status. If ent is vehile, this is the real reload1 value. -.float vehicle_ammo2 = _STAT(VEHICLESTAT_AMMO2); /// If ent is player this is 0..100 indicating percentage of secondary ammo left. If ent is vehile, this is the real ammo2 value. -.float vehicle_reload2 = _STAT(VEHICLESTAT_RELOAD2); /// If ent is player this is 0..100 indicating percentage of secondary reload status. If ent is vehile, this is the real reload2 value. +.float vehicle_ammo1 = _STAT(VEHICLESTAT_AMMO1); /// If ent is player this is 0..100 indicating percentage of primary ammo left UNLESS value is already stored in vehicle_energy. If ent is vehicle, this is the real ammo1 value. +.float vehicle_reload1 = _STAT(VEHICLESTAT_RELOAD1); /// If ent is player this is 0..100 indicating percentage of primary reload status. If ent is vehicle, this is the real reload1 value. +.float vehicle_ammo2 = _STAT(VEHICLESTAT_AMMO2); /// If ent is player this is 0..100 indicating percentage of secondary ammo left. If ent is vehicle, this is the real ammo2 value. +.float vehicle_reload2 = _STAT(VEHICLESTAT_RELOAD2); /// If ent is player this is 0..100 indicating percentage of secondary reload status. If ent is vehicle, this is the real reload2 value. .float sound_nexttime; const float VOL_VEHICLEENGINE = 1; @@ -79,7 +79,6 @@ const int MAX_AXH = 4; .float lock_strength; .float lock_time; .float lock_soundtime; -const float DAMAGE_TARGETDRONE = 10; // vehicle functions .void(int _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns @@ -87,7 +86,7 @@ const float DAMAGE_TARGETDRONE = 10; .void(entity this, int exit_flags) vehicle_exit; .bool(entity this, entity player) vehicle_enter; const int VHEF_NORMAL = 0; /// User pressed exit key -const int VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehile is dying +const int VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehicle is dying const int VHEF_RELEASE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented) float force_fromtag_power; diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 4e842a865c..c340d94703 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -544,36 +544,27 @@ bool bumblebee_pilot_frame(entity this, float dt) else { if(!IS_DEAD(trace_ent)) + { if((teamplay && trace_ent.team == this.team) || !teamplay) { + if(autocvar_g_vehicle_bumblebee_healgun_hps) + { + float hplimit = ((IS_PLAYER(trace_ent)) ? autocvar_g_vehicle_bumblebee_healgun_hmax : RESOURCE_LIMIT_NONE); + Heal(trace_ent, this, autocvar_g_vehicle_bumblebee_healgun_hps * dt, hplimit); + } if(IS_VEHICLE(trace_ent)) { if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health) trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * dt, trace_ent.tur_head.max_health); - - if(autocvar_g_vehicle_bumblebee_healgun_hps) - trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health); } else if(IS_CLIENT(trace_ent)) { - if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps) - trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax); - - if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps) - trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax); - - trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax); - } - else if(IS_TURRET(trace_ent)) - { - if(trace_ent.health <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps) - trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health); - //else ..hmmm what? ammo? - - trace_ent.SendFlags |= TNSF_STATUS; + if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps) + GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax); } } + } } } @@ -803,7 +794,7 @@ METHOD(Bumblebee, vr_death, void(Bumblebee thisveh, entity instance)) Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation(instance.origin, 16), '0 0 0', 1); - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.solid = SOLID_NOT; instance.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 93ed6d31d5..18e13bcbbe 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -566,7 +566,7 @@ METHOD(Racer, vr_death, void(Racer thisveh, entity instance)) { #ifdef SVQC setSendEntity(instance, func_null); // stop networking this racer (for now) - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.solid = SOLID_CORPSE; instance.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index bf3e443620..f44dcc5784 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -609,7 +609,7 @@ METHOD(Raptor, vr_enter, void(Raptor thisveh, entity instance)) } METHOD(Raptor, vr_death, void(Raptor thisveh, entity instance)) { - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.solid = SOLID_CORPSE; instance.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc index 37c4fc391f..53475d6cfd 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc @@ -74,7 +74,7 @@ METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weapone _flare.solid = SOLID_CORPSE; _flare.takedamage = DAMAGE_YES; _flare.event_damage = raptor_flare_damage; - _flare.health = 20; + SetResourceAmountExplicit(_flare, RESOURCE_HEALTH, 20); _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime; settouch(_flare, raptor_flare_touch); } @@ -191,8 +191,8 @@ void raptor_flare_touch(entity this, entity toucher) void raptor_flare_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - this.health -= damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) delete(this); } diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index 994a642d73..09d0eb2af9 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -540,7 +540,7 @@ METHOD(Spiderbot, vr_think, void(Spiderbot thisveh, entity instance)) } METHOD(Spiderbot, vr_death, void(Spiderbot thisveh, entity instance)) { - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.takedamage = DAMAGE_NO; settouch(instance, func_null); diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index e5d4f2eb23..c7066e6fbd 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -1,6 +1,8 @@ #include "arc.qh" #ifdef SVQC +#include +#include bool W_Arc_Beam_Send(entity this, entity to, int sf) { @@ -102,16 +104,16 @@ void W_Arc_Bolt_Explode_use(entity this, entity actor, entity trigger) void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, getthink(this)); } @@ -138,7 +140,7 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR(arc, bolt_damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR(arc, bolt_health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(arc, bolt_health)); missile.damageforcescale = WEP_CVAR(arc, bolt_damageforcescale); missile.event_damage = W_Arc_Bolt_Damage; missile.damagedbycontents = true; @@ -189,11 +191,9 @@ void W_Arc_Beam_Think(entity this) if( !IS_PLAYER(own) || - (!thiswep.wr_checkammo1(thiswep, own, weaponentity) && !(own.items & IT_UNLIMITED_WEAPON_AMMO)) - || IS_DEAD(own) || - forbidWeaponUse(own) + !weapon_prepareattack_check(thiswep, own, weaponentity, this.beam_bursting, -1) || own.(weaponentity).m_switchweapon != WEP_ARC || @@ -408,7 +408,7 @@ void W_Arc_Beam_Think(entity this) beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos); new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir); - float is_player = ( + bool is_player = ( IS_PLAYER(trace_ent) || trace_ent.classname == "body" @@ -416,65 +416,42 @@ void W_Arc_Beam_Think(entity this) IS_MONSTER(trace_ent) ); - if(trace_ent && trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage))) + if(trace_ent) { - // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?) - // NO. trace_endpos should be just fine. If not, - // that's an engine bug that needs proper debugging. - vector hitorigin = trace_endpos; - - float falloff = ExponentialFalloff( - WEP_CVAR(arc, beam_falloff_mindist), - WEP_CVAR(arc, beam_falloff_maxdist), - WEP_CVAR(arc, beam_falloff_halflifedist), - vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg) - ); - - if(is_player && SAME_TEAM(own, trace_ent)) + if(SAME_TEAM(own, trace_ent)) { - float roothealth, rootarmor; - if(burst) - { - roothealth = WEP_CVAR(arc, burst_healing_hps); - rootarmor = WEP_CVAR(arc, burst_healing_aps); - } - else + float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps)); + float rootarmor = ((burst) ? WEP_CVAR(arc, burst_healing_aps) : WEP_CVAR(arc, beam_healing_aps)); + float hplimit = ((IS_PLAYER(trace_ent)) ? WEP_CVAR(arc, beam_healing_hmax) : RESOURCE_LIMIT_NONE); + Heal(trace_ent, own, (roothealth * coefficient), hplimit); + if(IS_PLAYER(trace_ent) && rootarmor) { - roothealth = WEP_CVAR(arc, beam_healing_hps); - rootarmor = WEP_CVAR(arc, beam_healing_aps); + if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= WEP_CVAR(arc, beam_healing_amax)) + { + GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, (rootarmor * coefficient), WEP_CVAR(arc, beam_healing_amax)); + trace_ent.pauserotarmor_finished = max( + trace_ent.pauserotarmor_finished, + time + autocvar_g_balance_pause_armor_rot + ); + } } - - if(trace_ent.health <= WEP_CVAR(arc, beam_healing_hmax) && roothealth) - { - trace_ent.health = min( - trace_ent.health + (roothealth * coefficient), - WEP_CVAR(arc, beam_healing_hmax) - ); - } - if(trace_ent.armorvalue <= WEP_CVAR(arc, beam_healing_amax) && rootarmor) - { - trace_ent.armorvalue = min( - trace_ent.armorvalue + (rootarmor * coefficient), - WEP_CVAR(arc, beam_healing_amax) - ); - } - - // stop rot, set visual effect if(roothealth || rootarmor) - { - trace_ent.pauserothealth_finished = max( - trace_ent.pauserothealth_finished, - time + autocvar_g_balance_pause_health_rot - ); - trace_ent.pauserotarmor_finished = max( - trace_ent.pauserotarmor_finished, - time + autocvar_g_balance_pause_armor_rot - ); new_beam_type = ARC_BT_HEAL; - } } - else + else if(trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage))) { + // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?) + // NO. trace_endpos should be just fine. If not, + // that's an engine bug that needs proper debugging. + vector hitorigin = trace_endpos; + + float falloff = ExponentialFalloff( + WEP_CVAR(arc, beam_falloff_mindist), + WEP_CVAR(arc, beam_falloff_maxdist), + WEP_CVAR(arc, beam_falloff_halflifedist), + vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg) + ); + float rootdamage; if(is_player) { diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index c53e110fdd..ab97e3d7f0 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -289,20 +289,20 @@ void W_Devastator_Touch(entity this, entity toucher) void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode_think); } -void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) +void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_DecreaseAmmo(thiswep, actor, WEP_CVAR(devastator, ammo), weaponentity); @@ -324,7 +324,7 @@ void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) missile.takedamage = DAMAGE_YES; missile.damageforcescale = WEP_CVAR(devastator, damageforcescale); - missile.health = WEP_CVAR(devastator, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(devastator, health)); missile.event_damage = W_Devastator_Damage; missile.damagedbycontents = true; IL_PUSH(g_damagedbycontents, missile); @@ -342,6 +342,7 @@ void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) setthink(missile, W_Devastator_Think); missile.nextthink = time; missile.cnt = time + WEP_CVAR(devastator, lifetime); + missile.rl_detonate_later = (fire & 2); // allow instant detonation missile.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, missile); IL_PUSH(g_bot_dodge, missile); @@ -358,6 +359,11 @@ void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) // common properties MUTATOR_CALLHOOK(EditProjectile, actor, missile); + + if (time >= missile.nextthink) + { + getthink(missile)(missile); + } } METHOD(Devastator, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) @@ -431,7 +437,7 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor, .entity weaponenti // but don't fire a new shot at the same time! if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events PHYS_INPUT_BUTTON_ATCK2(actor) = true; - if((skill > 6.5) && (selfdamage > actor.health)) + if((skill > 6.5) && (selfdamage > GetResourceAmount(actor, RESOURCE_HEALTH))) PHYS_INPUT_BUTTON_ATCK2(actor) = false; //if(PHYS_INPUT_BUTTON_ATCK2(actor) == true) // dprint(ftos(desirabledamage),"\n"); @@ -449,7 +455,7 @@ METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponen if(actor.(weaponentity).rl_release || WEP_CVAR(devastator, guidestop)) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire))) { - W_Devastator_Attack(thiswep, actor, weaponentity); + W_Devastator_Attack(thiswep, actor, weaponentity, fire); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); actor.(weaponentity).rl_release = 0; } diff --git a/qcsrc/common/weapons/weapon/electro.qc b/qcsrc/common/weapons/weapon/electro.qc index 2276b6042a..89738289f2 100644 --- a/qcsrc/common/weapons/weapon/electro.qc +++ b/qcsrc/common/weapons/weapon/electro.qc @@ -258,7 +258,7 @@ void W_Electro_Orb_Stick(entity this, entity to) newproj.takedamage = this.takedamage; newproj.damageforcescale = this.damageforcescale; - newproj.health = this.health; + SetResourceAmountExplicit(newproj, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); newproj.event_damage = this.event_damage; newproj.spawnshieldtime = this.spawnshieldtime; newproj.damagedbycontents = true; @@ -300,7 +300,7 @@ void W_Electro_Orb_Touch(entity this, entity toucher) void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; // note: combos are usually triggered by W_Electro_TriggerCombo, not damage @@ -309,8 +309,8 @@ void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, (is_combo ? 1 : -1))) return; // g_projectiles_damage says to halt - this.health = this.health - damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.takedamage = DAMAGE_NO; this.nextthink = time; @@ -381,7 +381,7 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity) setsize(proj, '-4 -4 -4', '4 4 4'); proj.takedamage = DAMAGE_YES; proj.damageforcescale = WEP_CVAR_SEC(electro, damageforcescale); - proj.health = WEP_CVAR_SEC(electro, health); + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, WEP_CVAR_SEC(electro, health)); proj.event_damage = W_Electro_Orb_Damage; proj.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, proj); diff --git a/qcsrc/common/weapons/weapon/fireball.qc b/qcsrc/common/weapons/weapon/fireball.qc index ee4b2e0847..84113b7020 100644 --- a/qcsrc/common/weapons/weapon/fireball.qc +++ b/qcsrc/common/weapons/weapon/fireball.qc @@ -14,9 +14,9 @@ void W_Fireball_Explode(entity this, entity directhitentity) this.takedamage = DAMAGE_NO; // 1. dist damage - d = (this.realowner.health + this.realowner.armorvalue); + d = (GetResourceAmount(this.realowner, RESOURCE_HEALTH) + GetResourceAmount(this.realowner, RESOURCE_ARMOR)); RadiusDamage(this, this.realowner, WEP_CVAR_PRI(fireball, damage), WEP_CVAR_PRI(fireball, edgedamage), WEP_CVAR_PRI(fireball, radius), NULL, NULL, WEP_CVAR_PRI(fireball, force), this.projectiledeathtype, this.weaponentity_fld, directhitentity); - if(this.realowner.health + this.realowner.armorvalue >= d) + if(GetResourceAmount(this.realowner, RESOURCE_HEALTH) + GetResourceAmount(this.realowner, RESOURCE_ARMOR) >= d) if(!this.cnt) { modeleffect_spawn("models/sphere/sphere.md3", 0, 0, this.origin, '0 0 0', '0 0 0', '0 0 0', 0, WEP_CVAR_PRI(fireball, bfgradius), 0.2, 0.05, 0.25); @@ -119,14 +119,14 @@ void W_Fireball_Think(entity this) void W_Fireball_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.cnt = 1; W_PrepareExplosionByDamage(this, attacker, W_Fireball_Explode_think); @@ -147,7 +147,7 @@ void W_Fireball_Attack1(entity actor, .entity weaponentity) proj.use = W_Fireball_Explode_use; setthink(proj, W_Fireball_Think); proj.nextthink = time; - proj.health = WEP_CVAR_PRI(fireball, health); + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, WEP_CVAR_PRI(fireball, health)); proj.team = actor.team; proj.event_damage = W_Fireball_Damage; proj.takedamage = DAMAGE_YES; diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc index d9164dc19a..7e76ffb1ce 100644 --- a/qcsrc/common/weapons/weapon/hagar.qc +++ b/qcsrc/common/weapons/weapon/hagar.qc @@ -32,7 +32,7 @@ void W_Hagar_Explode2_use(entity this, entity actor, entity trigger) void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; float is_linkexplode = ( ((inflictor.owner != NULL) ? (inflictor.owner == this.owner) : true) @@ -47,10 +47,10 @@ void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, is_linkexplode)) return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, getthink(this)); } @@ -91,7 +91,7 @@ void W_Hagar_Attack(Weapon thiswep, entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR_PRI(hagar, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_PRI(hagar, health)); missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; @@ -137,7 +137,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR_SEC(hagar, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_SEC(hagar, health)); missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; @@ -200,7 +200,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR_SEC(hagar, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_SEC(hagar, health)); missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; @@ -255,9 +255,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity) { // loadable hagar secondary attack, must always run each frame - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) - return; - if(time < game_starttime) + if(!weapon_prepareattack_check(thiswep, actor, weaponentity, true, -1)) return; bool loaded = actor.(weaponentity).hagar_load >= WEP_CVAR_SEC(hagar, load_max); @@ -363,7 +361,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity) void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - if(!(fire & 1) || actor.(weaponentity).hagar_load || actor.(weaponentity).hagar_loadblock || actor.(weaponentity).m_switchweapon != WEP_HAGAR) + if(!(fire & 1) || actor.(weaponentity).hagar_load || actor.(weaponentity).hagar_loadblock || actor.(weaponentity).m_switchweapon != WEP_HAGAR || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1)) { w_ready(thiswep, actor, weaponentity, fire); return; diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc index 985d2ae59e..e17e976208 100644 --- a/qcsrc/common/weapons/weapon/hook.qc +++ b/qcsrc/common/weapons/weapon/hook.qc @@ -48,15 +48,15 @@ void W_Hook_Explode2_use(entity this, entity actor, entity trigger) void W_Hook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, this.realowner, W_Hook_Explode2); } @@ -88,7 +88,7 @@ void W_Hook_Attack2(Weapon thiswep, entity actor, .entity weaponentity) settouch(gren, W_Hook_Touch2); gren.takedamage = DAMAGE_YES; - gren.health = WEP_CVAR_SEC(hook, health); + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, WEP_CVAR_SEC(hook, health)); gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale); gren.event_damage = W_Hook_Damage; gren.damagedbycontents = true; @@ -165,7 +165,7 @@ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, { if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { - if( actor.ammo_fuel >= (time - actor.(weaponentity).hook_time_fueldecrease) * hooked_fuel ) + if( GetResourceAmount(actor, RESOURCE_FUEL) >= (time - actor.(weaponentity).hook_time_fueldecrease) * hooked_fuel ) { W_DecreaseAmmo(thiswep, actor, (time - actor.(weaponentity).hook_time_fueldecrease) * hooked_fuel, weaponentity); actor.(weaponentity).hook_time_fueldecrease = time; @@ -173,7 +173,7 @@ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, } else { - actor.ammo_fuel = 0; + SetResourceAmount(actor, RESOURCE_FUEL, 0); actor.(weaponentity).hook_state |= HOOK_REMOVING; if(actor.(weaponentity).m_weapon != WEP_Null) // offhand W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity); @@ -214,9 +214,9 @@ METHOD(Hook, wr_checkammo1, bool(Hook thiswep, entity actor, .entity weaponentit if (!thiswep.ammo_factor) return true; if(actor.(weaponentity).hook) - return actor.ammo_fuel > 0; + return GetResourceAmount(actor, RESOURCE_FUEL) > 0; - return actor.ammo_fuel >= WEP_CVAR_PRI(hook, ammo); + return GetResourceAmount(actor, RESOURCE_FUEL) >= WEP_CVAR_PRI(hook, ammo); } METHOD(Hook, wr_checkammo2, bool(Hook thiswep, entity actor, .entity weaponentity)) { diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index b5c3bf2fa4..6319dc36ae 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -86,7 +86,7 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we // weapon frames void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - if(actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon) // abort immediately if switching + if(actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon || !weapon_prepareattack_check(thiswep, actor, weaponentity, (fire & 2), -1)) // abort immediately if switching { w_ready(thiswep, actor, weaponentity, fire); return; @@ -113,7 +113,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity { float machinegun_spread; - if(!(fire & 1)) + if(!(fire & 1) || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1)) { w_ready(thiswep, actor, weaponentity, fire); return; diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index d8075c9fe4..6d3270a2b2 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -27,7 +27,7 @@ void W_MineLayer_Stick(entity this, entity to) newmine.takedamage = this.takedamage; newmine.damageforcescale = this.damageforcescale; - newmine.health = this.health; + SetResourceAmountExplicit(newmine, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); newmine.event_damage = this.event_damage; newmine.spawnshieldtime = this.spawnshieldtime; newmine.damagedbycontents = true; @@ -249,7 +249,7 @@ void W_MineLayer_Touch(entity this, entity toucher) void W_MineLayer_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; float is_from_enemy = (inflictor.realowner != this.realowner); @@ -257,10 +257,10 @@ void W_MineLayer_Damage(entity this, entity inflictor, entity attacker, float da if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, (is_from_enemy ? 1 : -1))) return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_MineLayer_Explode_think); } @@ -300,7 +300,7 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity) mine.takedamage = DAMAGE_YES; mine.damageforcescale = WEP_CVAR(minelayer, damageforcescale); - mine.health = WEP_CVAR(minelayer, health); + SetResourceAmountExplicit(mine, RESOURCE_HEALTH, WEP_CVAR(minelayer, health)); mine.event_damage = W_MineLayer_Damage; mine.damagedbycontents = true; IL_PUSH(g_damagedbycontents, mine); @@ -439,7 +439,7 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor, .entity weaponentit // but don't fire a new shot at the same time! if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events PHYS_INPUT_BUTTON_ATCK2(actor) = true; - if((skill > 6.5) && (selfdamage > actor.health)) + if((skill > 6.5) && (selfdamage > GetResourceAmount(actor, RESOURCE_HEALTH))) PHYS_INPUT_BUTTON_ATCK2(actor) = false; //if(PHYS_INPUT_BUTTON_ATCK2(actor) == true) // dprint(ftos(desirabledamage),"\n"); diff --git a/qcsrc/common/weapons/weapon/mortar.qc b/qcsrc/common/weapons/weapon/mortar.qc index 2dcde20d04..51267e50d4 100644 --- a/qcsrc/common/weapons/weapon/mortar.qc +++ b/qcsrc/common/weapons/weapon/mortar.qc @@ -54,15 +54,15 @@ void W_Mortar_Grenade_Explode2_use(entity this, entity actor, entity trigger) void W_Mortar_Grenade_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, adaptor_think2use); } @@ -176,7 +176,7 @@ void W_Mortar_Attack(Weapon thiswep, entity actor, .entity weaponentity) settouch(gren, W_Mortar_Grenade_Touch1); gren.takedamage = DAMAGE_YES; - gren.health = WEP_CVAR_PRI(mortar, health); + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, WEP_CVAR_PRI(mortar, health)); gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale); gren.event_damage = W_Mortar_Grenade_Damage; gren.damagedbycontents = true; @@ -227,7 +227,7 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) settouch(gren, W_Mortar_Grenade_Touch2); gren.takedamage = DAMAGE_YES; - gren.health = WEP_CVAR_SEC(mortar, health); + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, WEP_CVAR_SEC(mortar, health)); gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale); gren.event_damage = W_Mortar_Grenade_Damage; gren.damagedbycontents = true; diff --git a/qcsrc/common/weapons/weapon/seeker.qc b/qcsrc/common/weapons/weapon/seeker.qc index 0f0c426ecc..10080bbad2 100644 --- a/qcsrc/common/weapons/weapon/seeker.qc +++ b/qcsrc/common/weapons/weapon/seeker.qc @@ -124,18 +124,18 @@ void W_Seeker_Missile_Think(entity this) void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt if(this.realowner == attacker) - this.health = this.health - (damage * 0.25); + TakeResource(this, RESOURCE_HEALTH, (damage * 0.25)); else - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode_think); } @@ -190,7 +190,7 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, v missile.scale = 2; missile.takedamage = DAMAGE_YES; missile.weaponentity_fld = weaponentity; - missile.health = WEP_CVAR(seeker, missile_health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(seeker, missile_health)); missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale); missile.damagedbycontents = true; IL_PUSH(g_damagedbycontents, missile); @@ -415,10 +415,10 @@ void W_Seeker_Tag_Explode(entity this) void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; - this.health = this.health - damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_Seeker_Tag_Explode(this); } @@ -506,7 +506,7 @@ void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity) missile.takedamage = DAMAGE_YES; missile.event_damage = W_Seeker_Tag_Damage; - missile.health = WEP_CVAR(seeker, tag_health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(seeker, tag_health)); missile.damageforcescale = WEP_CVAR(seeker, tag_damageforcescale); setorigin(missile, w_shotorg); diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index c3baa12127..2dd5cae155 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -295,7 +295,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent } else if(WEP_CVAR(vaporizer, reload_ammo) && actor.(weaponentity).clip_load < vaporizer_ammo) { // forced reload thiswep.wr_reload(thiswep, actor, weaponentity); } - if((fire & 1) && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor)) + if((fire & 1) && (GetResourceAmount(actor, RESOURCE_CELLS) || !autocvar_g_rm) && !forbidWeaponUse(actor)) { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire))) { @@ -303,7 +303,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); } } - if((fire & 2) || ((fire & 1) && !actor.ammo_cells && autocvar_g_rm)) + if((fire & 2) || ((fire & 1) && !GetResourceAmount(actor, RESOURCE_CELLS) && autocvar_g_rm)) { if((autocvar_g_rm && autocvar_g_rm_laser) || autocvar_g_rm_laser == 2) { diff --git a/qcsrc/ecs/systems/sv_physics.qc b/qcsrc/ecs/systems/sv_physics.qc index c3594c0136..45128393ba 100644 --- a/qcsrc/ecs/systems/sv_physics.qc +++ b/qcsrc/ecs/systems/sv_physics.qc @@ -34,7 +34,6 @@ void sys_phys_monitor(entity this, float dt) void sys_phys_ai(entity this) { if (!IS_BOT_CLIENT(this)) { return; } - if (playerdemo_read(this)) { return; } bot_think(this); } diff --git a/qcsrc/lib/csqcmodel/cl_player.qc b/qcsrc/lib/csqcmodel/cl_player.qc index 225c7307de..f87f000337 100644 --- a/qcsrc/lib/csqcmodel/cl_player.qc +++ b/qcsrc/lib/csqcmodel/cl_player.qc @@ -291,7 +291,7 @@ void CSQCPlayer_SetCamera() // note: these two only work in WIP2, but are harmless in WIP1 if (PHYS_HEALTH(NULL) <= 0 && PHYS_HEALTH(NULL) != -666 && PHYS_HEALTH(NULL) != -2342) refdefflags |= REFDEFFLAG_DEAD; if (intermission) refdefflags |= REFDEFFLAG_INTERMISSION; - V_CalcRefdef(view, refdefflags); + V_CalcRefdef(view, refdefflags); // TODO? uses .health stat in the engine when this isn't called here, may be broken! } else { diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc index 994c5a7c95..21e7ecadc4 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc @@ -87,7 +87,7 @@ string XonoticMutatorsDialog_toString(entity me) if(cvar("g_bloodloss") > 0) s = cons_mid(s, ", ", _("Blood loss")); if(cvar("g_jetpack")) - s = cons_mid(s, ", ", _("Jet pack")); + s = cons_mid(s, ", ", _("Jetpack")); if(cvar("g_buffs") > 0) s = cons_mid(s, ", ", _("Buffs")); if(cvar("g_overkill")) @@ -209,7 +209,7 @@ void XonoticMutatorsDialog_fill(entity me) _("Players spawn with the grappling hook"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticCheckBox_T(0, "g_jetpack", _("Jet pack"), + me.TD(me, 1, 1.8, e = makeXonoticCheckBox_T(0, "g_jetpack", _("Jetpack"), _("Players spawn with the jetpack"))); me.TR(me); me.TDempty(me, 0.2); @@ -252,14 +252,12 @@ void XonoticMutatorsDialog_fill(entity me) w = Weapons_from(i); if(w.spawnflags & WEP_FLAG_HIDDEN) continue; - if((j & 1) == 0) - me.TDempty(me, 0.2); - else + if ((j % 3) == 0) { me.TR(me); me.TDempty(me, 0.4); } - me.TD(me, 1, 1.7, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name))); + me.TD(me, 1, 1.2, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name))); setDependentWeird(e, checkCompatibility_weaponarena_weapon); ++j; } diff --git a/qcsrc/menu/xonotic/keybinder.qc b/qcsrc/menu/xonotic/keybinder.qc index cde80d693d..a14406bcec 100644 --- a/qcsrc/menu/xonotic/keybinder.qc +++ b/qcsrc/menu/xonotic/keybinder.qc @@ -34,7 +34,7 @@ void Xonotic_KeyBinds_Read() KEYBIND_DEF("+jump" , _("jump / swim")); KEYBIND_DEF("+crouch" , _("crouch / sink")); KEYBIND_DEF("+hook" , _("off-hand hook")); - KEYBIND_DEF("+jetpack" , _("jet pack")); + KEYBIND_DEF("+jetpack" , _("jetpack")); KEYBIND_DEF("" , ""); KEYBIND_DEF("" , _("Attacking")); KEYBIND_DEF("+fire" , _("primary fire")); diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index 0e6ec096df..a4cb8bd5e1 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index a897b456a6..0a00d68078 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index 1f9b8625f4..712872dc94 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -74,12 +74,6 @@ void bot_think(entity this) this.bot_nextthink = max(time, this.bot_nextthink) + max(0.01, autocvar_bot_ai_thinkinterval * (0.5 ** this.bot_aiskill) * min(14 / (skill + 14), 1)); - //if (this.bot_painintensity > 0) - // this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime; - - //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health; - //this.bot_painintensity = bound(0, this.bot_painintensity, 100); - if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start)) { CS(this).movement = '0 0 0'; diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index 788ee1bc6e..c995ce8c8f 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -473,7 +473,7 @@ void havocbot_movetogoal(entity this) // Jetpack navigation if(this.navigation_jetpack_goal) if(this.goalcurrent==this.navigation_jetpack_goal) - if(this.ammo_fuel) + if(GetResourceAmount(this, RESOURCE_FUEL)) { if(autocvar_bot_debug_goalstack) { @@ -668,7 +668,7 @@ void havocbot_movetogoal(entity this) return; } - else if(this.health + this.armorvalue > ROCKETJUMP_DAMAGE()) + else if(GetResourceAmount(this, RESOURCE_HEALTH) + GetResourceAmount(this, RESOURCE_ARMOR) > ROCKETJUMP_DAMAGE()) { if(this.velocity.z < 0) { @@ -1225,7 +1225,7 @@ void havocbot_chooseenemy(entity this) traceline(this.origin+this.view_ofs, ( this.enemy.absmin + this.enemy.absmax ) * 0.5,false,NULL); if (trace_ent == this.enemy || trace_fraction == 1) if (vdist(((this.enemy.absmin + this.enemy.absmax) * 0.5) - this.origin, <, 1000)) - if (this.health > 30) + if (GetResourceAmount(this, RESOURCE_HEALTH) > 30) { // remain tracking him for a shot while (case he went after a small corner or pilar this.havocbot_chooseenemy_finished = time + 0.5; diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc index 361032fbee..1d66df0966 100644 --- a/qcsrc/server/bot/default/havocbot/roles.qc +++ b/qcsrc/server/bot/default/havocbot/roles.qc @@ -50,14 +50,14 @@ void havocbot_goalrating_waypoints(entity this, float ratingscale, vector org, f bool havocbot_goalrating_item_can_be_left_to_teammate(entity this, entity player, entity item) { - if (item.health && player.health <= this.health) {return true;} - if (item.armorvalue && player.armorvalue <= this.armorvalue) {return true;} + if (GetResourceAmount(item, RESOURCE_HEALTH) && GetResourceAmount(player, RESOURCE_HEALTH) <= GetResourceAmount(this, RESOURCE_HEALTH)) {return true;} + if (GetResourceAmount(item, RESOURCE_ARMOR) && GetResourceAmount(player, RESOURCE_ARMOR) <= GetResourceAmount(this, RESOURCE_ARMOR)) {return true;} if (STAT(WEAPONS, item) && !(STAT(WEAPONS, player) & STAT(WEAPONS, item))) {return true;} - if (item.ammo_shells && GetResourceAmount(player, RESOURCE_SHELLS) <= GetResourceAmount(this, RESOURCE_SHELLS)) {return true;} - if (item.ammo_nails && GetResourceAmount(player, RESOURCE_BULLETS) <= GetResourceAmount(this, RESOURCE_BULLETS)) {return true;} - if (item.ammo_rockets && GetResourceAmount(player, RESOURCE_ROCKETS) <= GetResourceAmount(this, RESOURCE_ROCKETS)) {return true;} - if (item.ammo_cells && GetResourceAmount(player, RESOURCE_CELLS) <= GetResourceAmount(this, RESOURCE_CELLS)) {return true;} - if (item.ammo_plasma && GetResourceAmount(player, RESOURCE_PLASMA) <= GetResourceAmount(this, RESOURCE_PLASMA)) {return true;} + if (GetResourceAmount(item, RESOURCE_SHELLS) && GetResourceAmount(player, RESOURCE_SHELLS) <= GetResourceAmount(this, RESOURCE_SHELLS)) {return true;} + if (GetResourceAmount(item, RESOURCE_BULLETS) && GetResourceAmount(player, RESOURCE_BULLETS) <= GetResourceAmount(this, RESOURCE_BULLETS)) {return true;} + if (GetResourceAmount(item, RESOURCE_ROCKETS) && GetResourceAmount(player, RESOURCE_ROCKETS) <= GetResourceAmount(this, RESOURCE_ROCKETS)) {return true;} + if (GetResourceAmount(item, RESOURCE_CELLS) && GetResourceAmount(player, RESOURCE_CELLS) <= GetResourceAmount(this, RESOURCE_CELLS)) {return true;} + if (GetResourceAmount(item, RESOURCE_PLASMA) && GetResourceAmount(player, RESOURCE_PLASMA) <= GetResourceAmount(this, RESOURCE_PLASMA)) {return true;} if (item.itemdef.instanceOfPowerup) {return true;} return false; @@ -208,7 +208,7 @@ void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org continue; */ - t = ((this.health + this.armorvalue) - (it.health + it.armorvalue)) / 150; + t = ((GetResourceAmount(this, RESOURCE_HEALTH) + GetResourceAmount(this, RESOURCE_ARMOR)) - (GetResourceAmount(it, RESOURCE_HEALTH) + GetResourceAmount(it, RESOURCE_ARMOR))) / 150; t = bound(0, 1 + t, 3); if (skill > 3) { diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index ac027bd229..4665be482a 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -1234,12 +1234,13 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) { entity theEnemy = e; entity best_wp = NULL; - float best_dist = 10000; - IL_EACH(g_waypoints, vdist(it.origin - theEnemy.origin, <, 500) + float best_dist = FLOAT_MAX; + IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_TELEPORT) + && vdist(it.origin - theEnemy.origin, <, 500) && vdist(it.origin - this.origin, >, 100) - && !(it.wpflags & WAYPOINTFLAG_TELEPORT), + && vdist(it.origin - this.origin, <, 10000), { - float dist = vlen(it.origin - theEnemy.origin); + float dist = vlen2(it.origin - theEnemy.origin); if (dist < best_dist) { best_wp = it; @@ -1315,10 +1316,10 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) t += xydistance / autocvar_g_jetpack_maxspeed_side; fuel = t * autocvar_g_jetpack_fuel * 0.8; - LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel)); + LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), ", have ", ftos(GetResourceAmount(this, RESOURCE_FUEL))); // enough fuel ? - if(this.ammo_fuel>fuel || (this.items & IT_UNLIMITED_WEAPON_AMMO)) + if(GetResourceAmount(this, RESOURCE_FUEL) > fuel || (this.items & IT_UNLIMITED_WEAPON_AMMO)) { // Estimate cost // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc index 82d82cb590..48975b8367 100644 --- a/qcsrc/server/bot/default/scripting.qc +++ b/qcsrc/server/bot/default/scripting.qc @@ -620,10 +620,11 @@ float bot_cmd_eval(entity this, string expr) return cvar(substring(expr, 5, strlen(expr))); // Search for fields + // TODO: expand with support for more fields (key carrier, ball carrier, armor etc) switch(expr) { case "health": - return this.health; + return GetResourceAmount(this, RESOURCE_HEALTH); case "speed": return vlen(this.velocity); case "flagcarrier": diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 1dcdab7381..04172b5eb2 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -158,8 +158,8 @@ float CheatImpulse(entity this, int imp) SetResourceAmount(this.personal, RESOURCE_PLASMA, GetResourceAmount(this, RESOURCE_PLASMA)); SetResourceAmount(this.personal, RESOURCE_SHELLS, GetResourceAmount(this, RESOURCE_SHELLS)); SetResourceAmount(this.personal, RESOURCE_FUEL, GetResourceAmount(this, RESOURCE_FUEL)); - this.personal.health = max(1, this.health); - this.personal.armorvalue = this.armorvalue; + SetResourceAmount(this.personal, RESOURCE_HEALTH, max(1, GetResourceAmount(this, RESOURCE_HEALTH))); + SetResourceAmount(this.personal, RESOURCE_ARMOR, GetResourceAmount(this, RESOURCE_ARMOR)); STAT(WEAPONS, this.personal) = STAT(WEAPONS, this); this.personal.items = this.items; this.personal.pauserotarmor_finished = this.pauserotarmor_finished; @@ -217,8 +217,8 @@ float CheatImpulse(entity this, int imp) SetResourceAmount(this, RESOURCE_PLASMA, GetResourceAmount(this.personal, RESOURCE_PLASMA)); SetResourceAmount(this, RESOURCE_SHELLS, GetResourceAmount(this.personal, RESOURCE_SHELLS)); SetResourceAmount(this, RESOURCE_FUEL, GetResourceAmount(this.personal, RESOURCE_FUEL)); - this.health = this.personal.health; - this.armorvalue = this.personal.armorvalue; + SetResourceAmount(this, RESOURCE_HEALTH, GetResourceAmount(this.personal, RESOURCE_HEALTH)); + SetResourceAmount(this, RESOURCE_ARMOR, GetResourceAmount(this.personal, RESOURCE_ARMOR)); STAT(WEAPONS, this) = STAT(WEAPONS, this.personal); this.items = this.personal.items; this.pauserotarmor_finished = time + this.personal.pauserotarmor_finished - this.personal.teleport_time; @@ -355,7 +355,7 @@ float CheatCommand(entity this, int argc) entity e = spawn(); e.model = strzone(argv(1)); e.mdl = "rocket_explode"; - e.health = 1000; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, 1000); setorigin(e, trace_endpos); e.effects = EF_NOMODELFLAGS; if(f == 1) diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 93d037aada..cf114e5f05 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -10,7 +10,6 @@ #include "miscfunctions.qh" #include "portals.qh" #include "teamplay.qh" -#include "playerdemo.qh" #include "spawnpoints.qh" #include "resources.qh" #include "g_damage.qh" @@ -229,7 +228,7 @@ void PutObserverInServer(entity this) if (IS_PLAYER(this)) { - if(this.health >= 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) >= 1) { // despawn effect Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); @@ -324,15 +323,14 @@ void PutObserverInServer(entity this) if(this.damagedbycontents) IL_REMOVE(g_damagedbycontents, this); this.damagedbycontents = false; - this.health = FRAGS_SPECTATOR; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, FRAGS_SPECTATOR); SetSpectatee_status(this, etof(this)); this.takedamage = DAMAGE_NO; this.solid = SOLID_NOT; set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink this.flags = FL_CLIENT | FL_NOTARGET; - this.armorvalue = 666; this.effects = 0; - this.armorvalue = autocvar_g_balance_armor_start; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_balance_armor_start); // was 666?! this.pauserotarmor_finished = 0; this.pauserothealth_finished = 0; this.pauseregen_finished = 0; @@ -381,6 +379,7 @@ void PutObserverInServer(entity this) this.oldvelocity = this.velocity; this.fire_endtime = -1; this.event_damage = func_null; + this.event_heal = func_null; for(int slot = 0; slot < MAX_AXH; ++slot) { @@ -560,8 +559,8 @@ void PutPlayerInServer(entity this) SetResourceAmount(this, RESOURCE_CELLS, warmup_start_ammo_cells); SetResourceAmount(this, RESOURCE_PLASMA, warmup_start_ammo_plasma); SetResourceAmount(this, RESOURCE_FUEL, warmup_start_ammo_fuel); - this.health = warmup_start_health; - this.armorvalue = warmup_start_armorvalue; + SetResourceAmount(this, RESOURCE_HEALTH, warmup_start_health); + SetResourceAmount(this, RESOURCE_ARMOR, warmup_start_armorvalue); STAT(WEAPONS, this) = WARMUP_START_WEAPONS; } else { SetResourceAmount(this, RESOURCE_SHELLS, start_ammo_shells); @@ -570,8 +569,8 @@ void PutPlayerInServer(entity this) SetResourceAmount(this, RESOURCE_CELLS, start_ammo_cells); SetResourceAmount(this, RESOURCE_PLASMA, start_ammo_plasma); SetResourceAmount(this, RESOURCE_FUEL, start_ammo_fuel); - this.health = start_health; - this.armorvalue = start_armorvalue; + SetResourceAmount(this, RESOURCE_HEALTH, start_health); + SetResourceAmount(this, RESOURCE_ARMOR, start_armorvalue); STAT(WEAPONS, this) = start_weapons; if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false) { @@ -675,6 +674,7 @@ void PutPlayerInServer(entity this) STAT(HUD, this) = HUD_NORMAL; this.event_damage = PlayerDamage; + this.event_heal = PlayerHeal; if(!this.bot_attack) IL_PUSH(g_bot_targets, this); @@ -750,6 +750,8 @@ void PutPlayerInServer(entity this) this.(weaponentity).weaponname = ""; this.(weaponentity).m_switchingweapon = WEP_Null; this.(weaponentity).cnt = -1; + + W_WeaponFrame(this, weaponentity); } MUTATOR_CALLHOOK(PlayerWeaponSelect, this); @@ -968,7 +970,7 @@ void KillIndicator_Think(entity this) ClientKill_Now(this.owner); return; } - else if(this.health == 1) // health == 1 means that it's silent + else if(this.count == 1) // count == 1 means that it's silent { this.nextthink = time + 1; this.cnt -= 1; @@ -1083,6 +1085,8 @@ void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, void ClientKill (entity this) { + // TODO: once .health is removed, will need to check it here for the "already dead" message! + if(game_stopped) return; if(this.player_blocked) return; if(STAT(FROZEN, this)) return; @@ -1724,13 +1728,17 @@ void player_regen(entity this) limith = limith * limit_mod; limita = limita * limit_mod; - this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita); - this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith); + SetResourceAmount(this, RESOURCE_ARMOR, CalcRotRegen(GetResourceAmount(this, RESOURCE_ARMOR), mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, + regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, + rot_mod * frametime * (time > this.pauserotarmor_finished), limita)); + SetResourceAmount(this, RESOURCE_HEALTH, CalcRotRegen(GetResourceAmount(this, RESOURCE_HEALTH), regen_health_stable, regen_health, regen_health_linear, + regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, + rot_mod * frametime * (time > this.pauserothealth_finished), limith)); } // if player rotted to death... die! // check this outside above checks, as player may still be able to rot to death - if(this.health < 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) { if(this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); @@ -1746,20 +1754,10 @@ void player_regen(entity this) minf = autocvar_g_balance_fuel_regenstable; 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); - } - // Ugly hack to make sure the health and armor don't go beyond hard limit. - // TODO: Remove this hack when all code uses GivePlayerHealth and - // GivePlayerArmor. - if (this.health > RESOURCE_AMOUNT_HARD_LIMIT) - { - this.health = RESOURCE_AMOUNT_HARD_LIMIT; + SetResourceAmount(this, RESOURCE_FUEL, CalcRotRegen(GetResourceAmount(this, RESOURCE_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)); } - if (this.armorvalue > RESOURCE_AMOUNT_HARD_LIMIT) - { - this.armorvalue = RESOURCE_AMOUNT_HARD_LIMIT; - } - // End hack. } bool zoomstate_set; @@ -1804,15 +1802,15 @@ void SpectateCopy(entity this, entity spectatee) MUTATOR_CALLHOOK(SpectateCopy, spectatee, this); PS(this) = PS(spectatee); this.armortype = spectatee.armortype; - this.armorvalue = spectatee.armorvalue; - this.ammo_cells = spectatee.ammo_cells; // TODO: these will be a part of inventory, so no need to worry about setting them later! - this.ammo_plasma = spectatee.ammo_plasma; - this.ammo_shells = spectatee.ammo_shells; - this.ammo_nails = spectatee.ammo_nails; - this.ammo_rockets = spectatee.ammo_rockets; - this.ammo_fuel = spectatee.ammo_fuel; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, GetResourceAmount(spectatee, RESOURCE_ARMOR)); + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(spectatee, RESOURCE_CELLS)); + SetResourceAmountExplicit(this, RESOURCE_PLASMA, GetResourceAmount(spectatee, RESOURCE_PLASMA)); + SetResourceAmountExplicit(this, RESOURCE_SHELLS, GetResourceAmount(spectatee, RESOURCE_SHELLS)); + SetResourceAmountExplicit(this, RESOURCE_BULLETS, GetResourceAmount(spectatee, RESOURCE_BULLETS)); + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(spectatee, RESOURCE_ROCKETS)); + SetResourceAmountExplicit(this, RESOURCE_FUEL, GetResourceAmount(spectatee, RESOURCE_FUEL)); this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance - this.health = spectatee.health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(spectatee, RESOURCE_HEALTH)); CS(this).impulse = 0; this.items = spectatee.items; STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee); @@ -2309,7 +2307,7 @@ bool PlayerThink(entity this) } this.items_added = 0; - if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || this.ammo_fuel >= 0.01)) + if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || GetResourceAmount(this, RESOURCE_FUEL) >= 0.01)) this.items_added |= IT_FUEL; this.items |= this.items_added; @@ -2565,7 +2563,7 @@ void PlayerPreThink (entity this) if (STAT(FROZEN, this) == 2) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1); - this.health = max(1, STAT(REVIVE_PROGRESS, this) * start_health); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health)); this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); if (STAT(REVIVE_PROGRESS, this) >= 1) @@ -2574,9 +2572,9 @@ void PlayerPreThink (entity this) else if (STAT(FROZEN, this) == 3) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1); - this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this) ); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); - if (this.health < 1) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 1) { if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); @@ -2818,12 +2816,10 @@ void PlayerPostThink (entity this) } if (this.waypointsprite_attachedforcarrier) { - vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); + vector v = healtharmor_maxdamage(GetResourceAmount(this, RESOURCE_HEALTH), GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); } - playerdemo_write(this); - CSQCMODEL_AUTOUPDATE(this); } diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index db822eb71f..cb8ab239fb 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -405,7 +405,7 @@ void CommonCommand_editmob(int request, entity caller, int argc) if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } - Damage(mon, NULL, NULL, mon.health + mon.max_health + 200, DEATH_KILL.m_id, DMG_NOWEP, mon.origin, '0 0 0'); + Damage(mon, NULL, NULL, GetResourceAmount(mon, RESOURCE_HEALTH) + mon.max_health + 200, DEATH_KILL.m_id, DMG_NOWEP, mon.origin, '0 0 0'); print_to(caller, strcat("Your pet '", mon.monster_name, "' has been brutally mutilated")); return; } diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 2903f55354..1076225d82 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -15,7 +15,6 @@ #include "../player.qh" #include "../g_world.qh" #include "../ipban.qh" -#include "../playerdemo.qh" #include "../teamplay.qh" #include "../bot/api.qh" @@ -1166,86 +1165,6 @@ void GameCommand_nospectators(float request) } } -void GameCommand_playerdemo(float request, float argc) -{ - switch (request) - { - case CMD_REQUEST_COMMAND: - { - if (argv(2) && argv(3)) - { - entity client; - float i, n, accepted; - - switch (argv(1)) - { - case "read": - { - client = GetIndexedEntity(argc, 2); - accepted = VerifyClientEntity(client, false, true); - - if (accepted <= 0) - { - LOG_INFO("playerdemo: read: ", GetClientErrorString(accepted, argv(2)), "."); - return; - } - - playerdemo_open_read(client, argv(next_token)); - return; - } - - case "write": - { - client = GetIndexedEntity(argc, 2); - accepted = VerifyClientEntity(client, false, false); - - if (accepted <= 0) - { - LOG_INFO("playerdemo: write: ", GetClientErrorString(accepted, argv(2)), "."); - return; - } - - playerdemo_open_write(client, argv(next_token)); - return; - } - - case "auto_read_and_write": - { - n = GetFilteredNumber(argv(3)); - cvar_set("bot_number", ftos(n)); - - localcmd("wait; wait; wait\n"); - for (i = 0; i < n; ++i) - localcmd("sv_cmd playerdemo read ", ftos(i + 2), " ", argv(2), ftos(i + 1), "\n"); - localcmd("sv_cmd playerdemo write 1 ", ftos(n + 1), "\n"); - return; - } - - case "auto_read": - { - n = GetFilteredNumber(argv(3)); - cvar_set("bot_number", ftos(n)); - - localcmd("wait; wait; wait\n"); - for (i = 0; i < n; ++i) - localcmd("sv_cmd playerdemo read ", ftos(i + 2), " ", argv(2), ftos(i + 1), "\n"); - return; - } - } - } - } - - default: - LOG_INFO("Incorrect parameters for ^2playerdemo^7"); - case CMD_REQUEST_USAGE: - { - LOG_INFO("Usage:^3 sv_cmd playerdemo command (entitynumber filename | entitynumber botnumber)"); - LOG_INFO(" Full list of commands here: \"read, write, auto_read_and_write, auto_read.\""); - return; - } - } -} - void GameCommand_printstats(float request) { switch (request) @@ -1729,7 +1648,6 @@ SERVER_COMMAND(lockteams, "Disable the ability for players to switch or enter te SERVER_COMMAND(make_mapinfo, "Automatically rebuild mapinfo files") { GameCommand_make_mapinfo(request); } SERVER_COMMAND(moveplayer, "Change the team/status of a player") { GameCommand_moveplayer(request, arguments); } SERVER_COMMAND(nospectators, "Automatically remove spectators from a match") { GameCommand_nospectators(request); } -SERVER_COMMAND(playerdemo, "Control the ability to save demos of players") { GameCommand_playerdemo(request, arguments); } SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); } SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); } SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); } diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc index 85b063a682..4511100581 100644 --- a/qcsrc/server/compat/quake3.qc +++ b/qcsrc/server/compat/quake3.qc @@ -141,33 +141,33 @@ void target_give_init(entity this) IL_EACH(g_items, it.targetname == this.target, { if (it.classname == "weapon_devastator") { - this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo); + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS) + it.count * WEP_CVAR_PRI(devastator, ammo)); // WEAPONTODO this.netname = cons(this.netname, "devastator"); } else if (it.classname == "weapon_vortex") { - this.ammo_cells += it.count * WEP_CVAR_PRI(vortex, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_CELLS) + it.count * WEP_CVAR_PRI(vortex, ammo)); // WEAPONTODO this.netname = cons(this.netname, "vortex"); } else if (it.classname == "weapon_electro") { - this.ammo_cells += it.count * WEP_CVAR_PRI(electro, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_CELLS) + it.count * WEP_CVAR_PRI(electro, ammo)); // WEAPONTODO this.netname = cons(this.netname, "electro"); } else if (it.classname == "weapon_hagar") { - this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS) + it.count * WEP_CVAR_PRI(hagar, ammo)); // WEAPONTODO this.netname = cons(this.netname, "hagar"); } else if (it.classname == "weapon_crylink") { - this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo); + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_CELLS) + it.count * WEP_CVAR_PRI(crylink, ammo)); // WEAPONTODO this.netname = cons(this.netname, "crylink"); } else if (it.classname == "weapon_mortar") { - this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS) + it.count * WEP_CVAR_PRI(mortar, ammo)); // WEAPONTODO this.netname = cons(this.netname, "mortar"); } else if (it.classname == "item_armor_mega") - this.armorvalue = 100; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, 100); else if (it.classname == "item_health_mega") - this.health = 200; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 200); //remove(it); // removing ents in init functions causes havoc, workaround: setthink(it, SUB_Remove); it.nextthink = time; diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 1db5dd0c5b..4c23aaeb3b 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -36,6 +36,8 @@ float server_is_dedicated; .void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage; +.bool(entity targ, entity inflictor, float amount, float limit) event_heal; + //.string wad; //.string map; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 4bb38780d2..cc0c71be5d 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -593,9 +593,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... - SetResourceAmount(targ, RESOURCE_ARMOR, 0); + SetResourceAmountExplicit(targ, RESOURCE_ARMOR, 0); targ.spawnshieldtime = 0; - SetResourceAmount(targ, RESOURCE_HEALTH, 0.9); // this is < 1 + SetResourceAmountExplicit(targ, RESOURCE_HEALTH, 0.9); // this is < 1 targ.flags -= targ.flags & FL_GODMODE; damage = 100000; } @@ -636,7 +636,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d if(autocvar_g_mirrordamage_virtual) { - vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage); + vector v = healtharmor_applydamage(GetResourceAmount(attacker, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage); attacker.dmg_take += v.x; attacker.dmg_save += v.y; attacker.dmg_inflictor = inflictor; @@ -646,7 +646,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d if(autocvar_g_friendlyfire_virtual) { - vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + vector v = healtharmor_applydamage(GetResourceAmount(targ, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage); targ.dmg_take += v.x; targ.dmg_save += v.y; targ.dmg_inflictor = inflictor; @@ -1061,6 +1061,20 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e return RadiusDamageForSource (inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, false, forceintensity, deathtype, weaponentity, directhitentity); } +bool Heal(entity targ, entity inflictor, float amount, float limit) +{ + if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ)) + return false; + + bool healed = false; + if(targ.event_heal) + healed = targ.event_heal(targ, inflictor, amount, limit); + // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc + // TODO: healing fx! + // TODO: armor healing? + return healed; +} + float Fire_IsBurning(entity e) { return (time < e.fire_endtime); diff --git a/qcsrc/server/g_damage.qh b/qcsrc/server/g_damage.qh index 9ab817853b..88fbf13442 100644 --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@ -96,6 +96,10 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity); +// Calls .event_heal on the target so that they can handle healing themselves +// a limit of RESOURCE_LIMIT_NONE should be handled by the entity as its max health (if applicable) +bool Heal(entity targ, entity inflictor, float amount, float limit); + .float fire_damagepersec; .float fire_endtime; .float fire_deathtype; diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index 09135d4827..68aa7154ec 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -327,7 +327,7 @@ void GrapplingHookTouch(entity this, entity toucher) GrapplingHook_Stop(this); if(toucher) - if(toucher.move_movetype != MOVETYPE_NONE) + //if(toucher.move_movetype != MOVETYPE_NONE) { SetMovetypeFollow(this, toucher); WarpZone_RefSys_BeginAddingIncrementally(this, this.aiment); @@ -338,15 +338,15 @@ void GrapplingHookTouch(entity this, entity toucher) void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_balance_projectiledamage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { if(attacker != this.realowner) { @@ -363,19 +363,9 @@ void FireGrapplingHook(entity actor, .entity weaponentity) if(forbidWeaponUse(actor)) return; if(actor.vehicle) return; - makevectors(actor.v_angle); - - int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1; - vector vs = hook_shotorigin[s]; - - // UGLY WORKAROUND: play this on CH_WEAPON_B so it can't cut off fire sounds - sound (actor, CH_WEAPON_B, SND_HOOK_FIRE, VOL_BASE, ATTEN_NORM); - vector org = actor.origin + actor.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z; - - tracebox(actor.origin + actor.view_ofs, '-3 -3 -3', '3 3 3', org, MOVE_NORMAL, actor); - org = trace_endpos; - - Send_Effect(EFFECT_HOOK_MUZZLEFLASH, org, '0 0 0', 1); + // TODO: offhand hook shoots from eye + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id); + Send_Effect(EFFECT_HOOK_MUZZLEFLASH, w_shotorg, '0 0 0', 1); entity missile = WarpZone_RefSys_SpawnSameRefSys(actor); missile.owner = missile.realowner = actor; @@ -392,11 +382,11 @@ void FireGrapplingHook(entity actor, .entity weaponentity) //setmodel (missile, MDL_HOOK); // precision set below setsize (missile, '-3 -3 -3', '3 3 3'); - setorigin(missile, org); + setorigin(missile, w_shotorg); missile.state = 0; // not latched onto anything - W_SetupProjVelocity_Explicit(missile, v_forward, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, false); + W_SetupProjVelocity_Explicit(missile, w_shotdir, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, false); missile.angles = vectoangles (missile.velocity); //missile.glow_color = 250; // 244, 250 @@ -407,7 +397,7 @@ void FireGrapplingHook(entity actor, .entity weaponentity) missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION; - missile.health = autocvar_g_balance_grapplehook_health;//120 + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, autocvar_g_balance_grapplehook_health); missile.event_damage = GrapplingHook_Damage; missile.takedamage = DAMAGE_AIM; missile.damageforcescale = 0; diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 0b80094681..26a4cfb086 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -356,6 +356,7 @@ void cvar_changes_init() BADCVAR("sv_stepheight"); BADCVAR("sv_timeout"); BADCVAR("sv_weapons_modeloverride"); + BADCVAR("w_prop_interval"); BADPREFIX("crypto_"); BADPREFIX("gameversion_"); BADPREFIX("g_chat_"); @@ -1466,7 +1467,7 @@ void FixIntermissionClient(entity e) if(!e.autoscreenshot) // initial call { e.autoscreenshot = time + 0.8; // used for autoscreenshot - e.health = -2342; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, -2342); // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not) for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { diff --git a/qcsrc/server/mapvoting.qc b/qcsrc/server/mapvoting.qc index 816945d2ab..5c564d56db 100644 --- a/qcsrc/server/mapvoting.qc +++ b/qcsrc/server/mapvoting.qc @@ -591,9 +591,9 @@ void MapVote_Tick() int totalvotes = 0; FOREACH_CLIENT(IS_REAL_CLIENT(it), { // hide scoreboard again - if(it.health != 2342) + if(GetResourceAmount(it, RESOURCE_HEALTH) != 2342) { - it.health = 2342; + SetResourceAmountExplicit(it, RESOURCE_HEALTH, 2342); CS(it).impulse = 0; msg_entity = it; diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index ecaf2faab1..319b6f16ff 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -266,8 +266,8 @@ string formatmessage(entity this, string msg) case "%": replacement = "%"; break; case "\\":replacement = "\\"; break; case "n": replacement = "\n"; break; - case "a": replacement = ftos(floor(this.armorvalue)); break; - case "h": replacement = ftos(floor(this.health)); break; + case "a": replacement = ftos(floor(GetResourceAmount(this, RESOURCE_ARMOR))); break; + case "h": replacement = ftos(floor(GetResourceAmount(this, RESOURCE_HEALTH))); break; case "l": replacement = NearestLocation(this.origin); break; case "y": replacement = NearestLocation(cursor); break; case "d": replacement = NearestLocation(this.death_origin); break; @@ -1060,7 +1060,7 @@ bool SUB_NoImpactCheck(entity this, entity toucher) if(trace_dphitcontents == 0) { LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin); - checkclient(this); + checkclient(this); // TODO: .health is checked in the engine with this, possibly replace with a QC function? } if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) return true; diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 98db7219d2..de21a59035 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -745,6 +745,30 @@ RESOURCE_* constants for resource types. Return true to forbid giving. */ /**/ MUTATOR_HOOKABLE(GiveResourceWithLimit, EV_GiveResourceWithLimit); +/** Called when some resource is being taken from an entity. See RESOURCE_* constants +for resource types. Return true to forbid giving. */ +#define EV_TakeResource(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(TakeResource, EV_TakeResource); + +/** Called when some resource is being taken from an entity, with a limit. See +RESOURCE_* constants for resource types. Return true to forbid giving. */ +#define EV_TakeResourceWithLimit(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) \ + /** limit */ i(float, MUTATOR_ARGV_3_float) \ + /**/ o(float, MUTATOR_ARGV_3_float) \ + /**/ +MUTATOR_HOOKABLE(TakeResourceWithLimit, EV_TakeResourceWithLimit); + /** called at when a player connect */ #define EV_ClientConnect(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index 4d59943b68..cc58cdc6d7 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -74,6 +74,7 @@ void CopyBody(entity this, float keepvelocity) clone.effects = this.effects; clone.glowmod = this.glowmod; clone.event_damage = this.event_damage; + clone.event_heal = this.event_heal; clone.anim_state = this.anim_state; clone.anim_time = this.anim_time; clone.anim_lower_action = this.anim_lower_action; @@ -89,8 +90,8 @@ void CopyBody(entity this, float keepvelocity) clone.dphitcontentsmask = this.dphitcontentsmask; clone.death_time = this.death_time; clone.pain_finished = this.pain_finished; - clone.health = this.health; - clone.armorvalue = this.armorvalue; + SetResourceAmountExplicit(clone, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); + SetResourceAmountExplicit(clone, RESOURCE_ARMOR, GetResourceAmount(this, RESOURCE_ARMOR)); clone.armortype = this.armortype; clone.model = this.model; clone.modelindex = this.modelindex; @@ -173,7 +174,7 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da vector v; Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + v = healtharmor_applydamage(GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage); take = v.x; save = v.y; @@ -192,8 +193,8 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da if (take > 100) Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; + TakeResource(this, RESOURCE_ARMOR, save); + TakeResource(this, RESOURCE_HEALTH, take); // pause regeneration for 5 seconds this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); @@ -201,7 +202,7 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da this.dmg_take = this.dmg_take + take;//max(take - 10, 0); this.dmg_inflictor = inflictor; - if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= -autocvar_sv_gibhealth && this.alpha >= 0) { // don't use any animations as a gib this.frame = 0; @@ -310,8 +311,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, vector v; float excess; - dh = max(this.health, 0); - da = max(this.armorvalue, 0); + dh = max(GetResourceAmount(this, RESOURCE_HEALTH), 0); + da = max(GetResourceAmount(this, RESOURCE_ARMOR), 0); if(!DEATH_ISSPECIAL(deathtype)) { @@ -359,7 +360,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, else Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + v = healtharmor_applydamage(GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage); take = v.x; save = v.y; @@ -388,8 +389,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, } MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage); - take = bound(0, M_ARGV(4, float), this.health); - save = bound(0, M_ARGV(5, float), this.armorvalue); + take = bound(0, M_ARGV(4, float), GetResourceAmount(this, RESOURCE_HEALTH)); + save = bound(0, M_ARGV(5, float), GetResourceAmount(this, RESOURCE_ARMOR)); excess = max(0, damage - take - save); if(sound_allowed(MSG_BROADCAST, attacker)) @@ -411,8 +412,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, { if (!(this.flags & FL_GODMODE)) { - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; + TakeResource(this, RESOURCE_ARMOR, save); + TakeResource(this, RESOURCE_HEALTH, take); // pause regeneration for 5 seconds if(take) this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); @@ -432,19 +433,19 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, animdecide_setaction(this, ANIMACTION_PAIN2, true); } } - + float myhp = GetResourceAmount(this, RESOURCE_HEALTH); + if(myhp > 1) + if(myhp < 25 || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || take > 20 || attacker != this) if(sound_allowed(MSG_BROADCAST, attacker)) - if(this.health < 25 || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || take > 20 || attacker != this) - if(this.health > 1) // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two { if(deathtype == DEATH_FALL.m_id) PlayerSound(this, playersound_fall, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 75) + else if(myhp > 75) PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 50) + else if(myhp > 50) PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 25) + else if(myhp > 25) PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); else PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); @@ -454,7 +455,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // throw off bot aim temporarily float shake; - if(IS_BOT_CLIENT(this) && this.health >= 1) + if(IS_BOT_CLIENT(this) && GetResourceAmount(this, RESOURCE_HEALTH) >= 1) { shake = damage * 5 / (bound(0,skill,100) + 1); this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake; @@ -497,8 +498,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, valid_damage_for_weaponstats = true; } - dh = dh - max(this.health, 0); - da = da - max(this.armorvalue, 0); + dh = dh - max(GetResourceAmount(this, RESOURCE_HEALTH), 0); + da = da - max(GetResourceAmount(this, RESOURCE_ARMOR), 0); if(valid_damage_for_weaponstats) { WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da); @@ -506,7 +507,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage); - if (this.health < 1) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 1) { float defer_ClientKill_Now_TeamChange; defer_ClientKill_Now_TeamChange = false; @@ -580,7 +581,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // player could have been miraculously resuscitated ;) // e.g. players in freezetag get frozen, they don't really die - if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body")) + if(GetResourceAmount(this, RESOURCE_HEALTH) >= 1 || !(IS_PLAYER(this) || this.classname == "body")) return; if (!this.respawn_time) // can be set in the mutator hook PlayerDies @@ -589,7 +590,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // when we get here, player actually dies Unfreeze(this); // remove any icy remains - this.health = 0; // Unfreeze resets health, so we need to set it back + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // Unfreeze resets health, so we need to set it back // clear waypoints WaypointSprite_PlayerDead(this); @@ -629,6 +630,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // set damage function to corpse damage this.event_damage = PlayerCorpseDamage; + this.event_heal = func_null; // call the corpse damage function just in case it wants to gib this.event_damage(this, inflictor, attacker, excess, deathtype, weaponentity, hitloc, force); @@ -664,6 +666,15 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, } } +bool PlayerHeal(entity targ, entity inflictor, float amount, float limit) +{ + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, limit); + return true; +} + bool MoveToTeam(entity client, int team_colour, int type) { int lockteams_backup = lockteams; // backup any team lock diff --git a/qcsrc/server/player.qh b/qcsrc/server/player.qh index 5e6642e047..8c9bac9b62 100644 --- a/qcsrc/server/player.qh +++ b/qcsrc/server/player.qh @@ -39,4 +39,6 @@ bool MoveToTeam(entity client, float team_colour, float type); void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); +bool PlayerHeal(entity targ, entity inflictor, float amount, float limit); + int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); diff --git a/qcsrc/server/playerdemo.qc b/qcsrc/server/playerdemo.qc deleted file mode 100644 index 411d826a87..0000000000 --- a/qcsrc/server/playerdemo.qc +++ /dev/null @@ -1,170 +0,0 @@ -#include "playerdemo.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include "defs.qh" - #include "playerdemo.qh" - #include -#endif - -.float playerdemo_fh; -.float playerdemo_mode; -.float playerdemo_starttime; -.float playerdemo_time; -const float PLAYERDEMO_MODE_OFF = 0; -const float PLAYERDEMO_MODE_READING = 1; -const float PLAYERDEMO_MODE_WRITING = 2; -void playerdemo_init(entity this) -{ - this.playerdemo_mode = PLAYERDEMO_MODE_OFF; -} -void playerdemo_shutdown(entity this) -{ - if(this.playerdemo_mode != PLAYERDEMO_MODE_OFF) - { - LOG_INFO("playerdemo: ", this.netname, " closed"); - fclose(this.playerdemo_fh); - } - this.playerdemo_mode = 0; -} -void playerdemo_open_read(entity this, string f) -{ - playerdemo_shutdown(this); - this.playerdemo_mode = PLAYERDEMO_MODE_READING; - this.playerdemo_fh = fopen(f, FILE_READ); - this.playerdemo_starttime = time - 1; - this.playerdemo_time = stof(fgets(this.playerdemo_fh)); - this.playerdemo_time += this.playerdemo_starttime; - set_movetype(this, MOVETYPE_NONE); - LOG_INFO("playerdemo: ", this.netname, " reading from ", f); -} -void playerdemo_open_write(entity this, string f) -{ - playerdemo_shutdown(this); - this.playerdemo_mode = PLAYERDEMO_MODE_WRITING; - this.playerdemo_fh = fopen(f, FILE_WRITE); - this.playerdemo_starttime = time - 1; - LOG_INFO("playerdemo: ", this.netname, " writing to ", f); - LOG_INFO("WARNING: playerdemo file format is incomplete and not stable yet. DO NOT RELY ON IT!"); -} -#define PLAYERDEMO_FIELD(ent,func,t,f) func##t(ent,f,#f); -#define PLAYERDEMO_FIELDS(ent,func) \ - PLAYERDEMO_FIELD(ent,func,originvector,origin) \ - PLAYERDEMO_FIELD(ent,func,vector,angles) \ - PLAYERDEMO_FIELD(ent,func,sizevector,mins) \ - PLAYERDEMO_FIELD(ent,func,sizevector,maxs) \ - PLAYERDEMO_FIELD(ent,func,vector,v_angle) \ - PLAYERDEMO_FIELD(ent,func,modelstring,model) \ - PLAYERDEMO_FIELD(ent,func,string,playermodel) \ - PLAYERDEMO_FIELD(ent,func,float,skin) \ - PLAYERDEMO_FIELD(ent,func,string,playerskin) \ - PLAYERDEMO_FIELD(ent,func,float,frame) \ - PLAYERDEMO_FIELD(ent,func,float,effects) \ - /* PLAYERDEMO_FIELD(ent,func,float,switchweapon) */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button0) /* TODO: PHYS_INPUT_BUTTON_ATCK */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button3) /* TODO: PHYS_INPUT_BUTTON_ATCK2 */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button5) /* TODO: PHYS_INPUT_BUTTON_CROUCH */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button6) /* TODO: PHYS_INPUT_BUTTON_HOOK */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,buttonuse) /* TODO: PHYS_INPUT_BUTTON_USE */ \ - PLAYERDEMO_FIELD(ent,func,float,flags) \ - // end of list - -void playerdemo_write_originvector(entity this, .vector f, string name) -{ - fputs(this.playerdemo_fh, strcat(vtos(this.(f)), "\n")); -} -void playerdemo_write_sizevector(entity this, .vector f, string name) -{ - fputs(this.playerdemo_fh, strcat(vtos(this.(f)), "\n")); -} -void playerdemo_write_vector(entity this, .vector f, string name) -{ - fputs(this.playerdemo_fh, strcat(vtos(this.(f)), "\n")); -} -void playerdemo_write_string(entity this, .string f, string name) -{ - fputs(this.playerdemo_fh, strcat(this.(f), "\n")); -} -void playerdemo_write_modelstring(entity this, .string f, string name) -{ - fputs(this.playerdemo_fh, strcat(this.(f), "\n")); -} -void playerdemo_write_float(entity this, .float f, string name) -{ - fputs(this.playerdemo_fh, strcat(ftos(this.(f)), "\n")); -} -void playerdemo_write(entity this) -{ - if(this.playerdemo_mode != PLAYERDEMO_MODE_WRITING) - return; - fputs(this.playerdemo_fh, strcat(ftos(time - this.playerdemo_starttime), "\n")); - PLAYERDEMO_FIELDS(this, playerdemo_write_) -} -void playerdemo_read_originvector(entity this, .vector f, string name) -{ - setorigin(this, stov(fgets(this.playerdemo_fh))); -} -void playerdemo_read_sizevector(entity this, .vector f, string name) -{ - this.(f) = stov(fgets(this.playerdemo_fh)); - setsize(this, this.mins, this.maxs); -} -void playerdemo_read_vector(entity this, .vector f, string name) -{ - this.(f) = stov(fgets(this.playerdemo_fh)); -} -void playerdemo_read_string(entity this, .string f, string name) -{ - string s = fgets(this.playerdemo_fh); - if (s != this.(f)) - { - /* - if(this.f) - strunzone(this.f); - */ - this.(f) = strzone(s); - } -} -void playerdemo_read_modelstring(entity this, .string f, string name) -{ - string s = fgets(this.playerdemo_fh); - if (s != this.(f)) - _setmodel(this, s); -} -void playerdemo_read_float(entity this, .float f, string name) -{ - this.(f) = stof(fgets(this.playerdemo_fh)); -} -float playerdemo_read(entity this) -{ - if(this.playerdemo_mode != PLAYERDEMO_MODE_READING) - return 0; - if(this.playerdemo_time < 0) - return 1; - float t; - t = time; - while(time >= this.playerdemo_time) - { - PLAYERDEMO_FIELDS(this, playerdemo_read_) - { - time = this.playerdemo_time; - PlayerPreThink(this); - // not running physics though... this is just so we can run weapon stuff - PlayerPostThink(this); - } - this.playerdemo_time = stof(fgets(this.playerdemo_fh)); - if(this.playerdemo_time == 0) - { - this.playerdemo_time = -1; - return 1; - } - this.playerdemo_time += this.playerdemo_starttime; - } - this.velocity = '0 0 0'; - CS(this).movement = '0 0 0'; - this.dmg_take = 0; // so screen doesn't stay blurry - this.dmg_save = 0; - this.dmg_inflictor = NULL; - time = t; - return 1; -} diff --git a/qcsrc/server/playerdemo.qh b/qcsrc/server/playerdemo.qh deleted file mode 100644 index c2da2bc6f0..0000000000 --- a/qcsrc/server/playerdemo.qh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -void playerdemo_init(entity this); -void playerdemo_shutdown(entity this); -void playerdemo_write(entity this); -float playerdemo_read(entity this); - -void playerdemo_open_read(entity this, string f); -void playerdemo_open_write(entity this, string f); diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index 64d7f0a8e0..162026a169 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -199,8 +199,8 @@ float Portal_TeleportPlayer(entity teleporter, entity player) // reset fade counter teleporter.portal_wants_to_vanish = 0; teleporter.fade_time = time + autocvar_g_balance_portal_lifetime; - teleporter.health = autocvar_g_balance_portal_health; - teleporter.enemy.health = autocvar_g_balance_portal_health; + SetResourceAmountExplicit(teleporter, RESOURCE_HEALTH, autocvar_g_balance_portal_health); + SetResourceAmountExplicit(teleporter.enemy, RESOURCE_HEALTH, autocvar_g_balance_portal_health); return 1; } @@ -435,8 +435,8 @@ void Portal_Damage(entity this, entity inflictor, entity attacker, float damage, if(attacker != this.aiment) if(IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(this.aiment)) return; - this.health -= damage; - if(this.health < 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) < 0) Portal_Remove(this, 1); } @@ -639,7 +639,7 @@ entity Portal_Spawn(entity own, vector org, vector ang) portal.takedamage = DAMAGE_AIM; portal.event_damage = Portal_Damage; portal.fade_time = time + autocvar_g_balance_portal_lifetime; - portal.health = autocvar_g_balance_portal_health; + SetResourceAmountExplicit(portal, RESOURCE_HEALTH, autocvar_g_balance_portal_health); setmodel(portal, MDL_PORTAL); portal.savemodelindex = portal.modelindex; setcefc(portal, Portal_Customize); diff --git a/qcsrc/server/resources.qc b/qcsrc/server/resources.qc index 4ad66bb98e..b3b19095ae 100644 --- a/qcsrc/server/resources.qc +++ b/qcsrc/server/resources.qc @@ -10,6 +10,9 @@ float GetResourceLimit(entity e, int resource_type) { + if(!IS_PLAYER(e)) + return RESOURCE_LIMIT_NONE; // no limits on non-players + float limit; switch (resource_type) { @@ -74,6 +77,17 @@ float GetResourceAmount(entity e, int resource_type) return e.(resource_field); } +bool SetResourceAmountExplicit(entity e, int resource_type, float amount) +{ + .float resource_field = GetResourceField(resource_type); + if (e.(resource_field) != amount) + { + e.(resource_field) = amount; + return true; + } + return false; +} + void SetResourceAmount(entity e, int resource_type, float amount) { bool forbid = MUTATOR_CALLHOOK(SetResourceAmount, e, resource_type, amount); @@ -83,17 +97,16 @@ void SetResourceAmount(entity e, int resource_type, float amount) } resource_type = M_ARGV(1, int); amount = M_ARGV(2, float); - float max_amount = GetResourceLimit(e, resource_type); + float max_amount = GetResourceLimit(e, resource_type); // TODO: should allow overriding these limits if cheats are enabled! float amount_wasted = 0; - if (amount > max_amount) + if (amount > max_amount && max_amount != RESOURCE_LIMIT_NONE) { amount_wasted = amount - max_amount; amount = max_amount; } - .float resource_field = GetResourceField(resource_type); - if (e.(resource_field) != amount) + bool changed = SetResourceAmountExplicit(e, resource_type, amount); + if (changed) { - e.(resource_field) = amount; MUTATOR_CALLHOOK(ResourceAmountChanged, e, resource_type, amount); } if (amount_wasted == 0) @@ -105,7 +118,7 @@ void SetResourceAmount(entity e, int resource_type, float amount) void GiveResource(entity receiver, int resource_type, float amount) { - if (amount == 0) + if (amount <= 0) { return; } @@ -151,7 +164,7 @@ void GiveResource(entity receiver, int resource_type, float amount) void GiveResourceWithLimit(entity receiver, int resource_type, float amount, float limit) { - if (amount == 0) + if (amount <= 0) { return; } @@ -164,18 +177,68 @@ void GiveResourceWithLimit(entity receiver, int resource_type, float amount, resource_type = M_ARGV(1, int); amount = M_ARGV(2, float); limit = M_ARGV(3, float); - if (amount == 0) + if (amount <= 0) { return; } float current_amount = GetResourceAmount(receiver, resource_type); - if (current_amount + amount > limit) + if (current_amount + amount > limit && limit != RESOURCE_LIMIT_NONE) { amount = limit - current_amount; } GiveResource(receiver, resource_type, amount); } +void TakeResource(entity receiver, int resource_type, float amount) +{ + if (amount <= 0) + { + return; + } + bool forbid = MUTATOR_CALLHOOK(TakeResource, 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); +} + +void TakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit) +{ + if (amount <= 0) + { + return; + } + bool forbid = MUTATOR_CALLHOOK(TakeResourceWithLimit, receiver, + resource_type, amount, limit); + if (forbid) + { + return; + } + resource_type = M_ARGV(1, int); + amount = M_ARGV(2, float); + limit = M_ARGV(3, float); + if (amount <= 0) + { + return; + } + float current_amount = GetResourceAmount(receiver, resource_type); + if (current_amount - amount < limit) + { + amount = limit + current_amount; + } + TakeResource(receiver, resource_type, amount); +} + int GetResourceType(.float resource_field) { switch (resource_field) diff --git a/qcsrc/server/resources.qh b/qcsrc/server/resources.qh index 6ff3cea679..e4631ab560 100644 --- a/qcsrc/server/resources.qh +++ b/qcsrc/server/resources.qh @@ -7,9 +7,6 @@ #include -/// \brief Unconditional maximum amount of resources the entity can have. -const int RESOURCE_AMOUNT_HARD_LIMIT = 999; - // ============================ Public API ==================================== /// \brief Returns the maximum amount of the given resource. @@ -24,6 +21,13 @@ float GetResourceLimit(entity e, int resource_type); /// \return Current amount of resource the given entity has. float GetResourceAmount(entity e, int resource_type); +/// \brief Sets the resource amount of an entity without calling any hooks. +/// \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 Boolean for whether the ammo amount was changed +bool SetResourceAmountExplicit(entity e, int resource_type, float amount); + /// \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). @@ -47,6 +51,22 @@ void GiveResource(entity receiver, int resource_type, float amount); void GiveResourceWithLimit(entity receiver, int resource_type, float amount, float limit); +/// \brief Takes an entity some resource. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \return No return. +void TakeResource(entity receiver, int resource_type, float amount); + +/// \brief Takes an entity some resource but not less than a limit. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \param[in] limit Limit of resources to take. +/// \return No return. +void TakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit); + // ===================== Legacy and/or internal API =========================== /// \brief Converts an entity field to resource type. diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index 2cb40a8349..af2f4b574f 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -402,7 +402,7 @@ float PlayerScore_Compare(entity t1, entity t2, float strict) }); if (result.x == 0 && strict) - result.x = etof(t1.owner) - etof(t2.owner); + result.x = t1.owner.playerid - t2.owner.playerid; return result.x; } diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index dcf6016c5e..e0e33e4803 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -327,14 +327,21 @@ SelectSpawnPoint Finds a point to respawn ============= */ +bool testspawn_checked; +entity testspawn_point; entity SelectSpawnPoint(entity this, bool anypoint) { float teamcheck; - entity spot, firstspot; + entity spot = NULL; - spot = find(NULL, classname, "testplayerstart"); - if (spot) - return spot; + if(!testspawn_checked) + { + testspawn_point = find(NULL, classname, "testplayerstart"); + testspawn_checked = true; + } + + if(testspawn_point) + return testspawn_point; if(this.spawnpoint_targ) return this.spawnpoint_targ; @@ -365,7 +372,16 @@ entity SelectSpawnPoint(entity this, bool anypoint) // get the entire list of spots - firstspot = findchain(classname, "info_player_deathmatch"); + //entity firstspot = findchain(classname, "info_player_deathmatch"); + entity firstspot = IL_FIRST(g_spawnpoints); + entity prev = NULL; + IL_EACH(g_spawnpoints, true, + { + if(prev) + prev.chain = it; + it.chain = NULL; + prev = it; + }); // filter out the bad ones // (note this returns the original list if none survived) if(anypoint) diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index d9bab5d7f8..b0d9e0992e 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -116,7 +116,7 @@ string getwelcomemessage(entity this) if(g_weapon_stay && !g_cts) modifications = strcat(modifications, ", Weapons stay"); if(g_jetpack) - modifications = strcat(modifications, ", Jet pack"); + modifications = strcat(modifications, ", Jetpack"); if(autocvar_g_powerups == 0) modifications = strcat(modifications, ", No powerups"); if(autocvar_g_powerups > 0) diff --git a/qcsrc/server/tests.qc b/qcsrc/server/tests.qc index 50dc5a35bc..e52d4fcf11 100644 --- a/qcsrc/server/tests.qc +++ b/qcsrc/server/tests.qc @@ -2,7 +2,7 @@ void test_weapons_hurt(entity this) { - EXPECT_NE(100, this.health); + EXPECT_NE(100, GetResourceAmount(this, RESOURCE_HEALTH)); delete(this.enemy); delete(this); } diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index 2d553ba168..8cd7ab66c3 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -94,6 +94,7 @@ bool accuracy_isgooddamage(entity attacker, entity targ) int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ); if (warmup_stage) return false; + if (game_stopped) return false; // damage to dead/frozen players is good only if it happens in the frame they get killed / frozen // so that stats for weapons that shoot multiple projectiles per shot are properly counted diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index ab600f3317..bf81f70441 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -433,7 +433,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) entity this = actor.(weaponentity); if (frametime) this.weapon_frametime = frametime; - if (!this || actor.health < 1) return; // Dead player can't use weapons and injure impulse commands + if (!this || GetResourceAmount(actor, RESOURCE_HEALTH) < 1) return; // Dead player can't use weapons and injure impulse commands int button_atck = PHYS_INPUT_BUTTON_ATCK(actor); int button_atck2 = PHYS_INPUT_BUTTON_ATCK2(actor); diff --git a/ruleset-XDF.cfg b/ruleset-XDF.cfg index b37f01dcff..20740d9a86 100644 --- a/ruleset-XDF.cfg +++ b/ruleset-XDF.cfg @@ -7,7 +7,7 @@ exec balance-xdf.cfg exec physicsXDF.cfg // general gameplay -set g_jump_grunt 1 // make enemies even easier to hear when they're jumping around +// set g_jump_grunt 1 // just no set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly) set g_balance_kill_antispam 0 set g_forced_respawn 1 diff --git a/ruleset-XPM.cfg b/ruleset-XPM.cfg index f3607bb2f8..eb682aa55c 100644 --- a/ruleset-XPM.cfg +++ b/ruleset-XPM.cfg @@ -24,3 +24,4 @@ set g_monsters 0 set g_turrets 0 set g_vehicles 0 set sv_showspectators 0 +set sv_taunt 0 diff --git a/xonotic-client.cfg b/xonotic-client.cfg index b8d865d4df..5ac5a1ec8d 100644 --- a/xonotic-client.cfg +++ b/xonotic-client.cfg @@ -677,8 +677,8 @@ set cl_effects_lightningarc_branchfactor_add 0.1 set menu_updatecheck_getpacks 1 "get update packs from update server" -set cl_loddistance1 1024 -set cl_loddistance2 3072 +seta cl_loddistance1 1024 +seta cl_loddistance2 3072 seta cl_playerdetailreduction 4 "the higher, the less detailed player models are displayed (LOD)" seta cl_modeldetailreduction 1 "the higher, the less detailed certain map models are displayed (LOD)" diff --git a/xonotic-server.cfg b/xonotic-server.cfg index b5f977c238..9d4064949c 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -43,7 +43,7 @@ set sv_timeout_number 2 "how many timeouts one player is allowed to call (gets r set sv_timeout_leadtime 4 "how long the players will be informed that a timeout was called before it starts, in seconds" set sv_timeout_resumetime 3 "how long the remaining timeout-time will be after a player called the timein command" -set g_allow_oldvortexbeam 0 "If enabled, clients are allowed to use old v2.3 Vortex beam" +set g_allow_oldvortexbeam 1 "If enabled, clients are allowed to use old v2.3 Vortex beam" set g_telefrags 1 "telefragging, i.e. killing someone who stands in the way of someone who is teleporting" set g_telefrags_teamplay 1 "never telefrag team mates"