- 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=b8f4fa5002af1f9f2d5ac3d1809ed188
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
-Sun Jun 3 07:24:16 CEST 2018
+Wed Jun 20 07:24:25 CEST 2018
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"
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
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
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
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_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
set g_balance_crylink_secondary_bounces 0
set g_balance_devastator_guideratedelay 999
set g_balance_devastator_guidestop 1
set g_balance_devastator_health 30
-set g_balance_devastator_lifetime 100
+set g_balance_devastator_lifetime 20
set g_balance_devastator_radius 110
set g_balance_devastator_refire 0.9
set g_balance_devastator_reload_ammo 0
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_okmachinegun_primary_damage 25
set g_balance_okmachinegun_primary_force 5
set g_balance_okmachinegun_primary_refire 0.1
-set g_balance_okmachinegun_primary_solidpenetration 13.1
+set g_balance_okmachinegun_primary_solidpenetration 63
set g_balance_okmachinegun_primary_spread_add 0.012
set g_balance_okmachinegun_primary_spread_max 0.05
set g_balance_okmachinegun_primary_spread_min 0
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
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
#
# Translators:
# Jisoo Lim <liminj0719@gmail.com>, 2017
-# Kyf Lee (coughingmouse) <coughingmouse@gmail.com>, 2016
-# Kyf Lee (coughingmouse) <coughingmouse@gmail.com>, 2016-2017
-# Kyf Lee (coughingmouse) <coughingmouse@gmail.com>, 2016-2017
+# Kyf Lee <coughingmouse@gmail.com>, 2016
+# Kyf Lee <coughingmouse@gmail.com>, 2016-2017
+# Kyf Lee <coughingmouse@gmail.com>, 2016-2017
+# Kyf Lee <coughingmouse@gmail.com>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Xonotic\n"
cl_playerdetailreduction 4
gl_flashblend 0
gl_picmip -1
+gl_texturecompression_2d 0
+gl_texturecompression_sky 0
mod_q3bsp_nolightmaps 0
r_bloom 1
r_coronas 1
cl_playerdetailreduction 4
gl_flashblend 1
gl_picmip 1
+gl_texturecompression_2d 1
+gl_texturecompression_sky 1
mod_q3bsp_nolightmaps 1
r_bloom 0
r_coronas 1
cl_playerdetailreduction 4
gl_flashblend 0
gl_picmip 0
+gl_texturecompression_2d 0
+gl_texturecompression_sky 1
mod_q3bsp_nolightmaps 0
r_bloom 0
r_coronas 1
cl_playerdetailreduction 4
gl_flashblend 0
gl_picmip 0
+gl_texturecompression_2d 0
+gl_texturecompression_sky 1
mod_q3bsp_nolightmaps 0
r_bloom 0
r_coronas 1
cl_playerdetailreduction 4
gl_flashblend 1
gl_picmip 1337
+gl_texturecompression_2d 1
+gl_texturecompression_sky 1
mod_q3bsp_nolightmaps 1
r_bloom 0
r_coronas 1
cl_playerdetailreduction 0
gl_flashblend 0
gl_picmip -1
+gl_texturecompression_2d 0
+gl_texturecompression_sky 0
mod_q3bsp_nolightmaps 0
r_bloom 1
r_coronas 1
cl_playerdetailreduction 0
gl_flashblend 0
gl_picmip -1
+gl_texturecompression_2d 0
+gl_texturecompression_sky 0
mod_q3bsp_nolightmaps 0
r_bloom 1
r_coronas 1
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"
bone_aim2 0.35 bip01 r hand
bone_weapon bip01 r hand
fixbone 1
+hidden 1
-name Gak Masked
+name Gak
species alien
sex Male
weight 87
bone_aim3 0.35 bip01 r hand
bone_weapon bip01 r hand
fixbone 1
+hidden 1
bone_aim2 0.35 bip01 r hand
bone_weapon bip01 r hand
fixbone 1
+hidden 1
// ==========
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"
// =======
g_mod_physics Xonotic
-// current Xonotic physics
+// Xonotic 0.7 physics
sv_gravity 800
sv_maxspeed 360
#include <client/mapvoting.qc>
#include <client/miscfunctions.qc>
#include <client/player_skeleton.qc>
+#include <client/resources.qc>
#include <client/shownames.qc>
#include <client/teamradar.qc>
#include <client/view.qc>
#include <client/mapvoting.qh>
#include <client/miscfunctions.qh>
#include <client/player_skeleton.qh>
+#include <client/resources.qh>
#include <client/shownames.qh>
#include <client/teamradar.qh>
#include <client/view.qh>
void Announcer_Time()
{
+ if(intermission)
+ return;
+
float timeleft;
if(warmup_stage)
{
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;
case "clickradar":
{
- HUD_Radar_Show_Maximized(!hud_panel_radar_mouse, 1);
+ if(!isdemo())
+ HUD_Radar_Show_Maximized(!hud_panel_radar_mouse, 1);
return;
}
}
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;
#include <common/ent_cs.qh>
#include <common/mapinfo.qh>
#include <client/mapvoting.qh>
+#include <client/resources.qh>
#include <client/teamradar.qh>
#include <common/mutators/mutator/waypoints/all.qh>
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));
{
LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
LOG_INFO(_("Usage:"));
- LOG_INFO("^2scoreboard_columns_set default");
- LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ..."));
+ LOG_INFO("^2scoreboard_columns_set ^3default");
+ LOG_INFO(_("^2scoreboard_columns_set ^3field1 field2 ..."));
+ LOG_INFO(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
+ LOG_INFO(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
+ LOG_INFO(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields."));
LOG_INFO(_("The following field names are recognized (case insensitive):"));
LOG_INFO("");
if(argc == 3)
{
- if(argv(2) == "default")
+ if(argv(2) == "default" || argv(2) == "expand_default")
+ {
+ if(argv(2) == "expand_default")
+ cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
+ }
else if(argv(2) == "all")
{
string s = "ping pl name |"; // scores without a label
}
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)
{
// 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)
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)
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
{
}
// 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));
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;
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;
}
}
int spectatorlist[MAX_SPECTATORS];
int framecount;
-.float health;
float GetSpeedUnitFactor(int speed_unit);
string GetSpeedUnit(int speed_unit);
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
--- /dev/null
+#include "resources.qh"
+#include <common/items/item/ammo.qh>
+
+/// \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;
+}
--- /dev/null
+#pragma once
+
+/// \file
+/// \brief Header file that describes the resource system.
+/// \copyright GNU GPLv2 or any later version.
+
+#include <common/resources.qh>
+
+// ============================ 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;
#include "autocvars.qh"
#include "miscfunctions.qh"
+#include "resources.qh"
#include "hud/_mod.qh"
#include <common/ent_cs.qh>
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);
}
}
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);
if(zoomfactor < 1 || zoomfactor > 30)
zoomfactor = 2.5;
zoomspeed = autocvar_cl_zoomspeed;
- if(zoomspeed >= 0)
- if(zoomspeed < 0.5 || zoomspeed > 16)
- zoomspeed = 3.5;
+ if (zoomspeed >= 0 && (zoomspeed < 0.5 || zoomspeed > 16))
+ zoomspeed = 3.5;
zoomdir = button_zoom;
if(zoomdir) { zoomin_effect = 0; }
- if(camera_active)
+ if (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2)
+ {
+ current_viewzoom = 1;
+ }
+ else if (camera_active)
{
current_viewzoom = min(1, current_viewzoom + drawframetime);
}
if(autocvar_cl_velocityzoom_enabled && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too
{
- if(intermission) { curspeed = 0; }
+ if (intermission || (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2))
+ curspeed = 0;
else
{
-
makevectors(view_angles);
v = pmove_vel;
if(csqcplayer)
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
#pragma once
+#ifdef CSQC
+#include <client/resources.qh>
+#endif
+
// This includes some functions useful for debugging.
// Some more bot-specific ones are in server/pathlib/debug.qc.
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;
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
#include "ent_cs.qh"
#include <common/gamemodes/_mod.qh>
+#include <common/resources.qh>
+#ifdef SVQC
+#include <server/resources.qh>
+#endif
REGISTRY(EntCSProps, BITS(16) - 1)
#define EntCSProps_from(i) _EntCSProps_from(i, NULL)
}
#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
{ 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); },
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");
vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current)
{
- if(this.health < 0 || this.health >= ASSAULT_VALUE_INACTIVE)
+ float hlth = GetResourceAmount(this, RESOURCE_HEALTH);
+ if (hlth < 0 || hlth >= ASSAULT_VALUE_INACTIVE)
return '-1 0 0';
return current;
}
// 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
else
return; // already activated! cannot activate again!
- if(this.enemy.health < ASSAULT_VALUE_INACTIVE)
+ float hlth = GetResourceAmount(this.enemy, RESOURCE_HEALTH);
+ if (hlth < ASSAULT_VALUE_INACTIVE)
{
- if(this.enemy.health - this.dmg > 0.5)
+ if (hlth - 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, hlth);
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); });
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;
{
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
void assault_wall_think(entity this)
{
- if(this.enemy.health < 0)
+ if(GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 0)
{
this.model = "";
this.solid = SOLID_NOT;
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;
}
// 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);
+ float hlth = GetResourceAmount(targ, RESOURCE_HEALTH);
+ if (hlth <= 0 || hlth >= 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)
{
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)
entity destr = it;
IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target,
{
- if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE)
+ float hlth = GetResourceAmount(it.enemy, RESOURCE_HEALTH);
+ if (hlth > 0 && hlth < ASSAULT_VALUE_INACTIVE)
{
found = true;
break;
void CA_RoundStart()
{
- allowed_to_spawn = boolean(warmup_stage);
+ allowed_to_spawn = boolean(warmup_stage);
}
bool CA_CheckTeams()
MUTATOR_HOOKFUNCTION(ca, PlayerSpawn)
{
- entity player = M_ARGV(0, entity);
+ entity player = M_ARGV(0, entity);
player.caplayer = 1;
if (!warmup_stage)
MUTATOR_HOOKFUNCTION(ca, ClientConnect)
{
- entity player = M_ARGV(0, entity);
+ entity player = M_ARGV(0, entity);
TRANSMUTE(Observer, player);
return true;
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)
}
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);
MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver)
{
- entity player = M_ARGV(0, entity);
+ entity player = M_ARGV(0, entity);
if (!IS_DEAD(player))
ca_LastPlayerForTeam_Notify(player);
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)
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))
MUTATOR_HOOKFUNCTION(ca, SpectateNext)
{
- entity client = M_ARGV(0, entity);
+ entity client = M_ARGV(0, entity);
if (!autocvar_g_ca_spectate_enemies && client.caplayer)
{
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)
{
MUTATOR_HOOKFUNCTION(ca, ClientCommand_Spectate)
{
- entity player = M_ARGV(0, entity);
+ entity player = M_ARGV(0, entity);
if (player.caplayer)
{
{
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))
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;
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;
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;
}
{
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)
{
this.ctf_flagdamaged_byworld = true;
else
{
- this.health = 0;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0);
ctf_CheckFlagReturn(this, RETURN_NEEDKILL);
}
return;
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;
}
{
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;
}
{
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
{
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; }
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;
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;
{
// gather health and armor only
if (it.solid)
- if (it.health || it.armorvalue)
+ if (GetResourceAmount(it, RESOURCE_HEALTH) || GetResourceAmount(it, RESOURCE_ARMOR))
if (vdist(it.origin - org, <, sradius))
{
// get the value of the item
else
havocbot_goalrating_ctf_ourbase(this, 50000);
- if(this.health<100)
+ if(GetResourceAmount(this, RESOURCE_HEALTH) < 100)
havocbot_goalrating_ctf_carrieritems(this, 1000, this.origin, 1000);
navigation_goalrating_end(this);
}
// About to fail, switch to middlefield
- if(this.health<50)
+ if(GetResourceAmount(this, RESOURCE_HEALTH) < 50)
{
havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE);
return;
// 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
}
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;
// 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
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;
}
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;
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())
// TODO: sv_freezetag
#ifdef SVQC
+#include <server/resources.qh>
+
float autocvar_g_freezetag_frozen_maxtime;
float autocvar_g_freezetag_revive_clearspeed;
float autocvar_g_freezetag_round_timelimit;
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(STAT(FROZEN, it) != 1 && GetResourceAmount(it, RESOURCE_HEALTH) >= 1) ++redalive; break;
+ case NUM_TEAM_2: ++total_players; if(STAT(FROZEN, it) != 1 && GetResourceAmount(it, RESOURCE_HEALTH) >= 1) ++bluealive; break;
+ case NUM_TEAM_3: ++total_players; if(STAT(FROZEN, it) != 1 && GetResourceAmount(it, RESOURCE_HEALTH) >= 1) ++yellowalive; break;
+ case NUM_TEAM_4: ++total_players; if(STAT(FROZEN, it) != 1 && GetResourceAmount(it, RESOURCE_HEALTH) >= 1) ++pinkalive; break;
}
});
FOREACH_CLIENT(IS_REAL_CLIENT(it), {
entity freezetag_LastPlayerForTeam(entity this)
{
entity last_pl = NULL;
- FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
- if(it.health >= 1)
- if(!STAT(FROZEN, it))
- if(SAME_TEAM(it, this))
- if(!last_pl)
- last_pl = it;
- else
- return NULL;
+ FOREACH_CLIENT(IS_PLAYER(it) && it != this && SAME_TEAM(it, this), {
+ if (!STAT(FROZEN, it) && GetResourceAmount(it, RESOURCE_HEALTH) >= 1)
+ {
+ if (!last_pl)
+ last_pl = it;
+ else
+ return NULL;
+ }
});
return last_pl;
}
// else nothing - got frozen by the game type rules themselves
}
+// to be called when the player is frozen by freezetag (on death, spectator join etc), gives the score
void freezetag_Freeze(entity targ, entity attacker)
{
if(STAT(FROZEN, targ))
freezetag_Add_Score(targ, attacker);
}
-void freezetag_Unfreeze(entity this)
-{
- this.freezetag_frozen_time = 0;
- this.freezetag_frozen_timeout = 0;
-
- Unfreeze(this);
-}
-
float freezetag_isEliminated(entity e)
{
if(IS_PLAYER(e) && (STAT(FROZEN, e) == 1 || IS_DEAD(e)))
{
// 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);
}
});
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);
+ Unfreeze(this);
freezetag_count_alive_players();
}
if(round_handler_CountdownRunning())
{
if(STAT(FROZEN, frag_target))
- freezetag_Unfreeze(frag_target);
+ Unfreeze(frag_target);
freezetag_count_alive_players();
return true; // let the player die so that he can respawn whenever he wants
}
freezetag_LastPlayerForTeam_Notify(frag_target);
}
else
- freezetag_Unfreeze(frag_target); // remove ice
- frag_target.health = 0; // Unfreeze resets health
+ Unfreeze(frag_target); // remove ice
+ SetResourceAmountExplicit(frag_target, RESOURCE_HEALTH, 0); // Unfreeze resets health
frag_target.freezetag_frozen_timeout = -2; // freeze on respawn
return true;
}
return true;
}
+MUTATOR_HOOKFUNCTION(ft, Unfreeze)
+{
+ entity targ = M_ARGV(0, entity);
+ targ.freezetag_frozen_time = 0;
+ targ.freezetag_frozen_timeout = 0;
+
+ freezetag_count_alive_players();
+}
+
MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
{
if(game_stopped)
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)
{
- freezetag_Unfreeze(player);
+ Unfreeze(player);
freezetag_count_alive_players();
if(n == -1)
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))
{
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;
}
freezetag_teams = BITS(bound(2, freezetag_teams, 4));
GameRules_scoring(freezetag_teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, {
- field(SP_FREEZETAG_REVIVALS, "revivals", 0);
+ field(SP_FREEZETAG_REVIVALS, "revivals", 0);
});
round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null);
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;
// 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.
}
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;
}
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);
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)
{
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);
_tmp = ReadByte();
- if(_tmp != this.health)
+ if(_tmp != GetResourceAmount(this, RESOURCE_HEALTH))
cpicon_damage(this, _tmp);
- this.health = _tmp;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp);
}
}
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);
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();
_tmp = ReadByte();
- if(_tmp != this.health)
+ if(_tmp != GetResourceAmount(this, RESOURCE_HEALTH))
generator_damage(this, _tmp);
- this.health = _tmp;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp);
}
}
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;
{
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);
{
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;
{
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);
{
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;
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);
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);
this.SendFlags |= CPSF_STATUS;
}
+bool ons_ControlPoint_Icon_Heal(entity targ, entity inflictor, float amount, float limit)
+{
+ float hlth = GetResourceAmount(targ, RESOURCE_HEALTH);
+ float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+ if (hlth <= 0 || hlth >= true_limit)
+ return false;
+
+ GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+ hlth = GetResourceAmount(targ, RESOURCE_HEALTH);
+ if(targ.owner.iscaptured)
+ WaypointSprite_UpdateHealth(targ.owner.sprite, hlth);
+ else
+ WaypointSprite_UpdateBuildFinished(targ.owner.sprite, time + (targ.max_health - hlth) / (targ.count / ONS_CP_THINKRATE));
+ targ.SendFlags |= CPSF_STATUS;
+ return true;
+}
+
void ons_ControlPoint_Icon_Think(entity this)
{
this.nextthink = time + ONS_CP_THINKRATE;
_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;
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));
}
}
}
// 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);
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);
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))
{
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);
}
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);
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;
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)
play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK));
}
}
- this.health = this.health - damage;
- WaypointSprite_UpdateHealth(this.sprite, this.health);
+ TakeResource(this, RESOURCE_HEALTH, damage);
+ float hlth = GetResourceAmount(this, RESOURCE_HEALTH);
+ WaypointSprite_UpdateHealth(this.sprite, hlth);
// 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 - hlth / this.max_health), 1);
// see if the generator is still functional, or dying
- if (this.health > 0)
+ if (hlth > 0)
{
- this.lasthealth = this.health;
+ this.lasthealth = hlth;
}
else
{
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;
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);
+ float hlth = GetResourceAmount(targ, RESOURCE_HEALTH);
+ if (hlth <= 0 || hlth >= true_limit)
+ return false;
+
+ GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+ hlth = GetResourceAmount(targ, RESOURCE_HEALTH);
+ WaypointSprite_UpdateHealth(targ.sprite, hlth);
+ targ.frame = 10 * bound(0, (1 - hlth / targ.max_health), 1);
+ targ.lasthealth = hlth;
+ 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))
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;
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();
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;
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);
}
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);
}
}
bool needarmor = false, needweapons = false;
// Needs armor/health?
- if(this.health<100)
+ if(GetResourceAmount(this, RESOURCE_HEALTH) < 100)
needarmor = true;
// Needs weapons?
{
// 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);
{
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;
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;
{
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; }
}
}
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__)
#pragma once
#include "pickup.qh"
+#include <common/items/all.qh>
#ifdef SVQC
#include <common/t_items.qh>
+ #include <server/resources.qh>
#endif
+#if 1
.int ammo_none;
.int ammo_shells;
.int ammo_nails;
.int ammo_plasma;
.int ammo_fuel;
#endif
+#endif
+
+#ifdef GAMEQC
+.int spawnflags;
+#endif
#ifdef SVQC
PROPERTY(float, g_pickup_ammo_anyway);
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
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";
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";
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";
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";
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
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";
{
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
{
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
{
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
{
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
{
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
{
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
{
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
{
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
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
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;
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";
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)
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);
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)
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))
{
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);
}
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;
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;
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
}
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)
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
}
return;
if (this.spawnflags & BUTTON_DONTACCUMULATEDMG)
{
- if (this.health <= damage)
+ if (GetResourceAmount(this, RESOURCE_HEALTH) <= damage)
{
this.enemy = attacker;
button_fire(this);
}
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);
// 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;
}
if (this.max_health)
{
this.takedamage = DAMAGE_YES;
- this.health = this.max_health;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health);
}
this.state = STATE_DOWN;
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)
{
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);
}
}
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)
{
this.owner = this.enemy = this;
- if (this.health)
+ if (GetResourceAmount(this, RESOURCE_HEALTH))
return;
IFTARGETED
return;
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 == ""))
// 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)
// 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;
// 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 == "")
this.state = STATE_BOTTOM;
- if (this.health)
+ if (GetResourceAmount(this, RESOURCE_HEALTH))
{
//this.canteamdamage = true; // TODO
this.takedamage = DAMAGE_YES;
if (this.max_health)
{
this.takedamage = DAMAGE_YES;
- this.health = this.max_health;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health);
}
this.state = STATE_DOWN;
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;
{
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;
}
{
if (this.spawnflags & DOOR_SECRET_YES_SHOOT)
{
- this.health = 10000;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000);
this.takedamage = DAMAGE_YES;
}
setorigin(this, this.oldorigin);
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;
}
if (!toucher.iscreature)
return;
- if (toucher.health <= 0)
+ if (GetResourceAmount(toucher, RESOURCE_HEALTH) <= 0)
return;
#elif defined(CSQC)
if (!IS_PLAYER(toucher))
if (!toucher.iscreature)
return;
- if (toucher.health <= 0)
+ if (GetResourceAmount(toucher, RESOURCE_HEALTH) <= 0)
return;
#elif defined(CSQC)
if (!IS_PLAYER(toucher))
// to mean no restrictions, so use a yaw of 360 instead.
SetMovedir(this);
this.solid = SOLID_TRIGGER;
- SetBrushEntityModel(this);
+ SetBrushEntityModelNoLOD(this);
set_movetype(this, MOVETYPE_NONE);
this.modelindex = 0;
this.model = "";
// to mean no restrictions, so use a yaw of 360 instead.
SetMovedir(this);
this.solid = SOLID_BSP;
- SetBrushEntityModel(this);
+ SetBrushEntityModelNoLOD(this);
set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0
// this.modelindex = 0;
this.model = "";
{
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');
}
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);
}
}
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 == "")
{
if (this.max_health)
{
- this.health = this.max_health;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health);
this.takedamage = DAMAGE_YES;
this.solid = SOLID_BBOX;
}
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;
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;
}
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;
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;
.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
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);
// 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;
//toucher.in_swamp = 1;
//Revitalize players swampslug
- toucher.swampslug.health = 2;
+ toucher.swampslug.swamp_lifetime = 2;
}
REGISTER_NET_LINKED(ENT_CLIENT_SWAMP)
{
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)
|| (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;
// 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);
}
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;
}
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));
}
});
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;
});
}
- 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);
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); }
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);
}
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;
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); }
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);
});
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);
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); }
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); }
void M_Zombie_Attack_Leap_Touch(entity this, entity toucher)
{
- if (this.health <= 0)
+ if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
return;
vector angles_face;
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];
{
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();
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;
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); }
|| (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))
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
// 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;
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
// 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)
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;
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);
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);
_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;
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;
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;
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
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);
}
}
+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)
{
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);
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');
}
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;
}
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; }
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))
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));
}
}
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;
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));
REGISTER_MUTATOR(damagetext, true);
-#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0 || MUTATOR_IS_ENABLED(mutator_instagib))
+#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0)
#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1)
#define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2)
#define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3)
const float armor = M_ARGV(3, float);
const int deathtype = M_ARGV(5, int);
const float potential_damage = M_ARGV(6, float);
+ if(DEATH_WEAPONOF(deathtype) == WEP_VAPORIZER) return;
FOREACH_CLIENT(IS_REAL_CLIENT(it), {
if (
(SV_DAMAGETEXT_ALL()) ||
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) {
{
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);
}
}
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);
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;
.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;
}
// 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!
{
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;
}
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;
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;
}
}
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;
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);
}
}
}
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);
}
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);
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;
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)));
}
}
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?!
}
}
{
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
{
int t = ReadByte();
if (t < 192)
{
- this.health = t / 191.0;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, t / 191.0);
this.build_finished = 0;
}
else
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;
}
else
{
- this.health = -1;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, -1);
this.build_finished = 0;
}
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);
}
draw_beginBoldFont();
- if (this.health >= 0)
+ if (GetResourceAmount(this, RESOURCE_HEALTH) >= 0)
{
float align = 0, marg;
if (this.build_finished)
drawhealthbar(
o,
0,
- this.health,
+ GetResourceAmount(this, RESOURCE_HEALTH),
'0 0 0',
'0 0 0',
SPRITE_HEALTHBAR_WIDTH * t,
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;
}
{
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;
}
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"), "")
});
}
+bool autocvar__movetype_debug = false;
void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict
{
- vector mi, ma;
- if(this.solid == SOLID_BSP)
+ if(autocvar__movetype_debug)
{
- // TODO set the absolute bbox
- mi = this.mins;
- ma = this.maxs;
- }
- else
- {
- mi = this.mins;
- ma = this.maxs;
- }
- mi += this.origin;
- ma += this.origin;
+ vector mi, ma;
+ if(this.solid == SOLID_BSP)
+ {
+ // TODO set the absolute bbox
+ mi = this.mins;
+ ma = this.maxs;
+ }
+ else
+ {
+ mi = this.mins;
+ ma = this.maxs;
+ }
+ mi += this.origin;
+ ma += this.origin;
- if(this.flags & FL_ITEM)
- {
- mi -= '15 15 1';
- ma += '15 15 1';
+ if(this.flags & FL_ITEM)
+ {
+ mi -= '15 15 1';
+ ma += '15 15 1';
+ }
+ else
+ {
+ mi -= '1 1 1';
+ ma += '1 1 1';
+ }
+
+ this.absmin = mi;
+ this.absmax = ma;
}
else
{
- mi -= '1 1 1';
- ma += '1 1 1';
+ setorigin(this, this.origin); // calls SV_LinkEdict
+ #ifdef CSQC
+ // NOTE: CSQC's version of setorigin doesn't expand
+ this.absmin -= '1 1 1';
+ this.absmax += '1 1 1';
+ #endif
}
- this.absmin = mi;
- this.absmax = ma;
-
if(touch_triggers)
_Movetype_LinkEdict_TouchAreaGrid(this);
}
case MOVETYPE_FLY:
case MOVETYPE_FLY_WORLDONLY:
_Movetype_Physics_Toss(this, movedt);
+ if(wasfreed(this))
+ return;
_Movetype_LinkEdict(this, true);
break;
case MOVETYPE_PHYSICS:
#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;
/// \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
{
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);
ClientData_Attach(this);
accuracy_init(this);
entcs_attach(this);
- playerdemo_init(this);
anticheat_init(this);
W_HitPlotOpen(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);
bot_clientdisconnect(this);
anticheat_report_to_eventlog(this);
- playerdemo_shutdown(this);
entcs_detach(this);
}
{
return false;
}
- GiveResourceWithLimit(player, resource_type, amount, ammomax);
+ GiveOrTakeResourceWithLimit(player, resource_type, amount, ammomax);
return true;
}
if (g_weapon_stay != 2)
{
return false;
}
- GiveResourceWithLimit(player, resource_type, amount, min(amount, ammomax));
+ GiveOrTakeResourceWithLimit(player, resource_type, amount, min(amount, ammomax));
return true;
}
{
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)
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;
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)
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)
{
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;
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;
}
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));
}
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;
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)
{
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);
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,
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)
#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)
this.tur_head.angles += dt * this.tur_head.avelocity;
- if (this.health < 127)
+ if (GetResourceAmount(this, RESOURCE_HEALTH) < 127)
{
dt = random();
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);
drawhealthbar(
o,
0,
- this.health / 255,
+ GetResourceAmount(this, RESOURCE_HEALTH) / 255,
'0 0 0',
'0 0 0',
0.5 * SPRITE_HEALTHBAR_WIDTH * t,
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);
}
_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;
}
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);
return;
}
- this.health -= damage;
+ TakeResource(this, RESOURCE_HEALTH, damage);
// thorw head slightly off aim when hit?
if (this.damage_flags & TFL_DMG_HEADSHAKE)
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);
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)
{
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;
{
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;
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;
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);
}
PROJECTILE_MAKETRIGGER(proj);
if(_health)
{
- proj.health = _health;
+ SetResourceAmountExplicit(proj, RESOURCE_HEALTH, _health);
proj.takedamage = DAMAGE_YES;
proj.event_damage = turret_projectile_damage;
}
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;
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; }
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;
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;
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
//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.
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
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);
}
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;
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
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;
{
proj.takedamage = DAMAGE_AIM;
proj.event_damage = vehicles_projectile_damage;
- proj.health = _health;
+ SetResourceAmountExplicit(proj, RESOURCE_HEALTH, _health);
}
else
proj.flags |= FL_NOTARGET;
}
}
+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)
vehicles_exit_running = true;
- // TODO: this was in an IS_CLIENT check, make sure it isn't actually needed!
if(vehic.vehicle_flags & VHF_PLAYERSLOT)
{
vehic.vehicle_exit(vehic, eject);
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;
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
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;
.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;
.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
.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;
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);
}
}
+ }
}
}
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;
{
#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;
}
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;
_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);
}
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);
}
}
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);
#include "arc.qh"
#ifdef SVQC
+#include <common/gamemodes/gamemode/onslaught/sv_onslaught.qh>
+#include <common/gamemodes/gamemode/onslaught/sv_generator.qh>
bool W_Arc_Beam_Send(entity this, entity to, int sf)
{
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));
}
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;
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
||
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"
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)
{
}
else if(fire & 2)
{
- if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(arc, bolt_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(arc, bolt_refire)))
{
W_Arc_Attack_Bolt(thiswep, actor, weaponentity);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready);
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);
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);
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);
// 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))
// 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");
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;
}
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;
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
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;
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);
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);
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);
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;
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)
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));
}
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;
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;
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;
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(time < game_starttime || time < actor.race_penalty || timeout_status == TIMEOUT_ACTIVE)
return;
bool loaded = actor.(weaponentity).hagar_load >= WEP_CVAR_SEC(hagar, load_max);
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;
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);
}
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;
{
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;
}
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);
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))
{
// 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;
{
float machinegun_spread;
- if(!(fire & 1))
+ if(!(fire & 1) || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1))
{
w_ready(thiswep, actor, weaponentity, fire);
return;
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;
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);
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);
}
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);
// 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");
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);
}
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;
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;
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);
}
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);
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);
}
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);
+ (v_up * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_up))
+ (v_right * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_side)));
- WarpZone_traceline_antilag(this, this.realowner.origin + this.realowner.view_ofs, targpos, false, this.realowner, ((IS_CLIENT(this.realowner)) ? ANTILAG_LATENCY(this.realowner) : 0));
+ WarpZone_traceline_antilag(this.realowner, this.realowner.origin + this.realowner.view_ofs, targpos, false, this.realowner, ((IS_CLIENT(this.realowner)) ? ANTILAG_LATENCY(this.realowner) : 0));
// draw lightning beams for debugging
//te_lightning2(NULL, targpos, this.realowner.origin + this.realowner.view_ofs + v_forward * 5 - v_up * 5);
is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || IS_MONSTER(trace_ent));
if((trace_fraction < 1) // if trace is good, apply the damage and remove this
- && (trace_ent.takedamage == DAMAGE_AIM)
+ && (trace_ent.takedamage != DAMAGE_NO)
&& (trace_ent != this.swing_alreadyhit)
&& (is_player || WEP_CVAR_SEC(shotgun, melee_nonplayerdamage)))
{
} 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)))
{
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)
{
void sys_phys_simulate(entity this, float dt);
void sys_phys_simulate_simple(entity this, float dt);
+void sys_phys_postupdate(entity this);
+
void sys_phys_update(entity this, float dt)
{
if (!IS_CLIENT(this)) {
// if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
// { this.velocity_z = 70; }
}
- goto end;
+ sys_phys_postupdate(this);
+ return;
}
PM_check_slick(this);
this.com_phys_air = false;
}
- LABEL(end)
+ sys_phys_postupdate(this);
+}
+
+void sys_phys_postupdate(entity this)
+{
if (IS_ONGROUND(this)) { this.lastground = time; }
// conveyors: then break velocity again
if (this.conveyor.active) { this.velocity += this.conveyor.movedir; }
void sys_phys_ai(entity this)
{
if (!IS_BOT_CLIENT(this)) { return; }
- if (playerdemo_read(this)) { return; }
bot_think(this);
}
// 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
{
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"))
_("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);
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;
}
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"));
#include <server/matrix.qc>
#include <server/miscfunctions.qc>
#include <server/player.qc>
-#include <server/playerdemo.qc>
#include <server/portals.qc>
#include <server/race.qc>
#include <server/resources.qc>
#include <server/matrix.qh>
#include <server/miscfunctions.qh>
#include <server/player.qh>
-#include <server/playerdemo.qh>
#include <server/portals.qh>
#include <server/race.qh>
#include <server/resources.qh>
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';
name = bot_name;
// number bots with identical names
- int j = 0;
- FOREACH_CLIENT(IS_BOT_CLIENT(it), {
- if(it.cleanname == name)
- ++j;
- });
- if (j)
- this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
- else
+ if (name == "")
+ {
+ name = ftos(etof(this));
this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
-
+ }
+ else
+ {
+ int j = 0;
+ FOREACH_CLIENT(IS_BOT_CLIENT(it), {
+ if(it.cleanname == name)
+ ++j;
+ });
+ if (j)
+ this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
+ else
+ this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
+ }
this.cleanname = strzone(name);
// pick the model and skin
// 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)
{
{
// Calculate brake distance in xy
float d = vlen(vec2(this.origin - (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5));
- float v = vlen(vec2(this.velocity));
- float db = ((v ** 2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
+ float vel2 = vlen2(vec2(this.velocity));
+ float db = (vel2 / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
//LOG_INFOF("distance %d, velocity %d, brake at %d ", ceil(d), ceil(v), ceil(db));
if(d < db || d < 500)
{
// Brake
- if(v > maxspeed * 0.3)
+ if (vel2 > (maxspeed * 0.3) ** 2)
{
CS(this).movement_x = dir * v_forward * -maxspeed;
return;
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)
{
else
PHYS_INPUT_BUTTON_JUMP(this) = false;
makevectors(this.v_angle.y * '0 1 0');
- CS(this).movement_x = dir * v_forward * maxspeed;
- CS(this).movement_y = dir * v_right * maxspeed;
- CS(this).movement_z = dir * v_up * maxspeed;
+ vector v = dir * maxspeed;
+ CS(this).movement.x = v * v_forward;
+ CS(this).movement.y = v * v_right;
+ CS(this).movement.z = v * v_up;
}
// if there is nowhere to go, exit
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;
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;
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)
{
{
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;
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
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":
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;
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;
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)
#include "miscfunctions.qh"
#include "portals.qh"
#include "teamplay.qh"
-#include "playerdemo.qh"
#include "spawnpoints.qh"
#include "resources.qh"
#include "g_damage.qh"
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);
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;
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)
{
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);
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)
{
STAT(HUD, this) = HUD_NORMAL;
this.event_damage = PlayerDamage;
+ this.event_heal = PlayerHeal;
if(!this.bot_attack)
IL_PUSH(g_bot_targets, this);
MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
+ if (CS(this).impulse) ImpulseCommands(this);
+
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ W_WeaponFrame(this, weaponentity);
+ }
+
if (!warmup_stage && !this.alivetime)
this.alivetime = time;
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;
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;
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);
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;
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);
}
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;
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)
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);
}
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);
}
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;
}
#include "../player.qh"
#include "../g_world.qh"
#include "../ipban.qh"
-#include "../playerdemo.qh"
#include "../teamplay.qh"
#include "../bot/api.qh"
}
}
-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)
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); }
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;
.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;
this.nextthink = time;
}
-void Freeze (entity targ, float revivespeed, float frozen_type, float show_waypoint)
+void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint)
{
- if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // only specified entities can be freezed
+ if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // TODO: only specified entities can be freezed
return;
if(STAT(FROZEN, targ))
});
// add waypoint
- if(show_waypoint)
+ if(MUTATOR_CALLHOOK(Freeze, targ, revivespeed, frozen_type) || show_waypoint)
WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT);
}
if(targ.iceblock)
delete(targ.iceblock);
targ.iceblock = NULL;
+
+ MUTATOR_CALLHOOK(Unfreeze, targ);
}
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
// 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;
}
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;
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;
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);
void Ice_Think(entity this);
-void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint);
+void Freeze(entity targ, float freeze_time, int frozen_type, bool show_waypoint);
void Unfreeze (entity targ);
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;
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);
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)
{
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;
//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
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;
BADCVAR("sv_stepheight");
BADCVAR("sv_timeout");
BADCVAR("sv_weapons_modeloverride");
+ BADCVAR("w_prop_interval");
BADPREFIX("crypto_");
BADPREFIX("gameversion_");
BADPREFIX("g_chat_");
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)
{
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;
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;
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;
/**/
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) \
MUT_VOTEPARSE_INVALID, // return -1 (vote parsed but counted as invalid, no action or vote)
MUT_VOTEPARSE_UNACCEPTABLE // return 0 (vote parameter counted as unacceptable, warns caller)
};
+
+/**
+ * Called when freezing an entity (monster or player), return true to force showing a waypoint
+ */
+#define EV_Freeze(i, o) \
+ /** targ */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** revive speed */ i(float, MUTATOR_ARGV_1_float) \
+ /** frozen type */ i(int, MUTATOR_ARGV_2_int) \
+ /**/
+MUTATOR_HOOKABLE(Freeze, EV_Freeze);
+
+/**
+ * Called when an entity (monster or player) is defrosted
+ */
+#define EV_Unfreeze(i, o) \
+ /** targ */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(Unfreeze, EV_Unfreeze);
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;
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;
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;
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);
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;
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))
{
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;
}
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))
{
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);
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);
// 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;
this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake;
this.v_angle_x = bound(-90, this.v_angle.x, 90);
}
+
+ if (this != attacker) {
+ float realdmg = damage - excess;
+ if (IS_PLAYER(attacker)) {
+ GameRules_scoring_add(attacker, DMG, realdmg);
+ }
+ if (IS_PLAYER(this)) {
+ GameRules_scoring_add(this, DMGTAKEN, realdmg);
+ }
+ }
}
else
this.max_armorvalue += (save + take);
this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
this.dmg_inflictor = inflictor;
- if (this != attacker) {
- float realdmg = damage - excess;
- if (IS_PLAYER(attacker)) {
- GameRules_scoring_add(attacker, DMG, realdmg);
- }
- if (IS_PLAYER(this)) {
- GameRules_scoring_add(this, DMGTAKEN, realdmg);
- }
- }
-
bool abot = (IS_BOT_CLIENT(attacker));
bool vbot = (IS_BOT_CLIENT(this));
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);
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;
// 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
// 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);
// 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);
}
}
+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
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);
+++ /dev/null
-#include "playerdemo.qh"
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "defs.qh"
- #include "playerdemo.qh"
- #include <common/state.qh>
-#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;
-}
+++ /dev/null
-#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);
// 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;
}
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);
}
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);
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)
{
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);
}
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)
void GiveResource(entity receiver, int resource_type, float amount)
{
- if (amount == 0)
+ if (amount <= 0)
{
return;
}
void GiveResourceWithLimit(entity receiver, int resource_type, float amount,
float limit)
{
- if (amount == 0)
+ if (amount <= 0)
{
return;
}
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);
+}
+
+void GiveOrTakeResource(entity receiver, int resource_type, float amount)
+{
+ if(amount < 0)
+ {
+ TakeResource(receiver, resource_type, amount * -1);
+ }
+ else
+ {
+ GiveResource(receiver, resource_type, amount);
+ }
+}
+
+void GiveOrTakeResourceWithLimit(entity receiver, int resource_type, float amount,
+ float limit)
+{
+ if(amount < 0)
+ {
+ TakeResourceWithLimit(receiver, resource_type, amount * -1, limit);
+ }
+ else
+ {
+ GiveResourceWithLimit(receiver, resource_type, amount, limit);
+ }
+}
+
int GetResourceType(.float resource_field)
{
switch (resource_field)
#include <common/resources.qh>
-/// \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.
/// \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).
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);
+
+/// \brief Gives to or takes from an entity resource.
+/// \param[in,out] receiver Entity to give or take resource.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \param[in] amount Amount of resource to give or take.
+/// \return No return.
+void GiveOrTakeResource(entity receiver, int resource_type, float amount);
+
+/// \brief Gives to or takes from an entity resource but not more/less than a limit.
+/// \param[in,out] receiver Entity to give or take resource.
+/// \param[in] resource_type Type of the resource (a RESOURCE_* constant).
+/// \param[in] amount Amount of resource to give or take.
+/// \param[in] limit Limit of resources to give or take.
+/// \return No return.
+void GiveOrTakeResourceWithLimit(entity receiver, int resource_type, float amount,
+ float limit);
+
// ===================== Legacy and/or internal API ===========================
/// \brief Converts an entity field to resource type.
});
if (result.x == 0 && strict)
- result.x = etof(t1.owner) - etof(t2.owner);
+ result.x = t1.owner.playerid - t2.owner.playerid;
return result.x;
}
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;
// 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)
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)
void test_weapons_hurt(entity this)
{
- EXPECT_NE(100, this.health);
+ EXPECT_NE(100, GetResourceAmount(this, RESOURCE_HEALTH));
delete(this.enemy);
delete(this);
}
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
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);
--- /dev/null
+explosive_ammo
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/items/explosiveammo.tga
+ rgbgen lightingDiffuse
+ }
+}
+explosive_ammo_icons
+{
+ {
+
+ animMap 2 textures/items/explosiveammo_icon_01.tga textures/items/explosiveammo_icon_01.tga textures/items/explosiveammo_icon_01.tga textures/items/explosiveammo_icon_blank.tga textures/items/explosiveammo_icon_02.tga textures/items/explosiveammo_icon_02.tga textures/items/explosiveammo_icon_02.tga textures/items/explosiveammo_icon_blank.tga textures/items/explosiveammo_icon_03.tga textures/items/explosiveammo_icon_03.tga textures/items/explosiveammo_icon_03.tga textures/items/explosiveammo_icon_blank.tga
+ blendFunc GL_ONE GL_ONE
+ rgbGen wave sawtooth 0 1 0 10
+ }
+
+}
+
+
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)"
set g_maplist_votable_abstain 0 "when 1, you can abstain from your vote"
set g_maplist_votable_screenshot_dir "maps levelshots" "where to look for map screenshots"
-set sv_vote_gametype 0 "show a vote screen for gametypes before map vote screen"
+set sv_vote_gametype 1 "show a vote screen for gametypes before map vote screen"
set sv_vote_gametype_keeptwotime 10 "show only 2 options after this amount of time during gametype vote screen"
-set sv_vote_gametype_options "dm tdm ctf"
+set sv_vote_gametype_options "dm tdm ctf" "Keep the identifiers short, otherwise you'll run into issues with too long alias names (max is 31 characters) when using sv_vote_gametype_hook_*"
set sv_vote_gametype_timeout 20
set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes"