X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Ft_items.qc;h=9ea1925495fb16937da5ff8b225568f6ff5aefae;hb=991de5e6922cd3c283de56c3249624f0f1bfe767;hp=fa6d0f6f313510dd1bc6a8e1a6e921a425d7e1b0;hpb=2590bde8cc2b5bf0055224535ecd84fb2c02d091;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index fa6d0f6f3..9ea192549 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -15,7 +15,7 @@ #include "constants.qh" #include #include - #include "triggers/subs.qh" + #include "mapobjects/subs.qh" #include "util.qh" #include @@ -37,6 +37,7 @@ REGISTER_NET_LINKED(ENT_CLIENT_ITEM) #ifdef CSQC bool autocvar_cl_ghost_items_vehicle = true; .vector item_glowmod; +.bool item_simple; // probably not really needed, but better safe than sorry void Item_SetAlpha(entity this) { bool veh_hud = (hud && autocvar_cl_ghost_items_vehicle); @@ -49,13 +50,8 @@ void Item_SetAlpha(entity this) } else { - if (autocvar_cl_ghost_items_color) - { - this.alpha = autocvar_cl_ghost_items; - this.colormod = this.glowmod = autocvar_cl_ghost_items_color; - } - else - this.alpha = -1; + this.alpha = autocvar_cl_ghost_items; + this.colormod = this.glowmod = autocvar_cl_ghost_items_color; } if((!veh_hud) && (this.ItemStatus & ITS_STAYWEP)) @@ -96,14 +92,16 @@ void ItemDraw(entity this) { if(this.ItemStatus & ITS_ANIMATE1) { - this.angles += this.avelocity * frametime; + if(!this.item_simple) + this.angles += this.avelocity * frametime; float fade_in = bound(0, time - this.onground_time, 1); setorigin(this, this.oldorigin + fade_in * ('0 0 10' + '0 0 8' * sin((time - this.onground_time) * 2))); } if(this.ItemStatus & ITS_ANIMATE2) { - this.angles += this.avelocity * frametime; + if(!this.item_simple) + this.angles += this.avelocity * frametime; float fade_in = bound(0, time - this.onground_time, 1); setorigin(this, this.oldorigin + fade_in * ('0 0 8' + '0 0 4' * sin((time - this.onground_time) * 3))); } @@ -112,19 +110,6 @@ void ItemDraw(entity this) Item_SetAlpha(this); } -void ItemDrawSimple(entity this) -{ - if(this.gravity) - { - Movetype_Physics_MatchServer(this, false); - - if(IS_ONGROUND(this)) - this.gravity = 0; - } - - Item_SetAlpha(this); -} - void Item_PreDraw(entity this) { if(warpzone_warpzones_exist) @@ -158,8 +143,17 @@ void Item_PreDraw(entity this) void ItemRemove(entity this) { - if(this.mdl) - strunzone(this.mdl); + strfree(this.mdl); +} + +HashMap ENT_CLIENT_ITEM_simple; +STATIC_INIT(ENT_CLIENT_ITEM_simple) +{ + HM_NEW(ENT_CLIENT_ITEM_simple); +} +SHUTDOWN(ENT_CLIENT_ITEM_simple) +{ + HM_DELETE(ENT_CLIENT_ITEM_simple); } NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) @@ -168,9 +162,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(sf & ISF_LOCATION) { - this.origin_x = ReadCoord(); - this.origin_y = ReadCoord(); - this.origin_z = ReadCoord(); + this.origin = ReadVector(); setorigin(this, this.origin); this.oldorigin = this.origin; } @@ -223,38 +215,47 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(!warpzone_warpzones_exist && this.fade_start && !autocvar_cl_items_nofade) setpredraw(this, Item_PreDraw); - if(this.mdl) - strunzone(this.mdl); + strfree(this.mdl); - this.mdl = ""; string _fn = ReadString(); + this.item_simple = false; // reset it! if(autocvar_cl_simple_items && (this.ItemStatus & ITS_ALLOWSI)) { string _fn2 = substring(_fn, 0 , strlen(_fn) -4); - this.draw = ItemDrawSimple; - - if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".md3"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".md3")); - else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".dpm"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".dpm")); - else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".iqm"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".iqm")); - else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".mdl"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".mdl")); - else - { - this.draw = ItemDraw; - LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it"); - } + this.item_simple = true; + + #define extensions(x) \ + x(md3) \ + x(dpm) \ + x(iqm) \ + x(mdl) \ + /**/ + #define tryext(ext) { \ + string s = strcat(_fn2, autocvar_cl_simpleitems_postfix, "." #ext); \ + string cached = HM_gets(ENT_CLIENT_ITEM_simple, s); \ + if (cached == "") { \ + HM_sets(ENT_CLIENT_ITEM_simple, s, cached = fexists(s) ? "1" : "0"); \ + } \ + if (cached != "0") { \ + strcpy(this.mdl, s); \ + break; \ + } \ + } + do { + extensions(tryext); + this.item_simple = false; + LOG_TRACEF("Simple item requested for %s but no model exists for it", _fn); + } while (0); + #undef tryext + #undef extensions } - if(this.draw != ItemDrawSimple) - this.mdl = strzone(_fn); - + if(!this.item_simple) + strcpy(this.mdl, _fn); if(this.mdl == "") - LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!"); + LOG_WARNF("this.mdl is unset for item %s", this.classname); precache_model(this.mdl); _setmodel(this, this.mdl); @@ -276,9 +277,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) this.pushable = true; //this.angles = '0 0 0'; set_movetype(this, MOVETYPE_TOSS); - this.velocity_x = ReadCoord(); - this.velocity_y = ReadCoord(); - this.velocity_z = ReadCoord(); + this.velocity = ReadVector(); setorigin(this, this.oldorigin); if(!this.move_time) @@ -320,9 +319,7 @@ bool ItemSend(entity this, entity to, int sf) //WriteByte(MSG_ENTITY, this.cnt); if(sf & ISF_LOCATION) { - WriteCoord(MSG_ENTITY, this.origin.x); - WriteCoord(MSG_ENTITY, this.origin.y); - WriteCoord(MSG_ENTITY, this.origin.z); + WriteVector(MSG_ENTITY, this.origin); } if(sf & ISF_ANGLES) @@ -365,9 +362,7 @@ bool ItemSend(entity this, entity to, int sf) if(sf & ISF_DROP) { - WriteCoord(MSG_ENTITY, this.velocity.x); - WriteCoord(MSG_ENTITY, this.velocity.y); - WriteCoord(MSG_ENTITY, this.velocity.z); + WriteVector(MSG_ENTITY, this.velocity); } return true; @@ -401,40 +396,13 @@ bool have_pickup_item(entity this) if(autocvar_g_pickup_items == 0) return false; if(g_weaponarena) - if(this.weapons || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena + if(STAT(WEAPONS, this) || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena return false; } return true; } -/* -float Item_Customize() -{ - if(this.spawnshieldtime) - return true; - if(this.weapons & ~other.weapons) - { - this.colormod = '0 0 0'; - this.glowmod = this.colormod; - this.alpha = 0.5 + 0.5 * g_ghost_items; // halfway more alpha - return true; - } - else - { - if(g_ghost_items) - { - this.colormod = stov(autocvar_g_ghost_items_color); - this.glowmod = this.colormod; - this.alpha = g_ghost_items; - return true; - } - else - return false; - } -} -*/ - -void Item_Show (entity e, float mode) +void Item_Show (entity e, int mode) { e.effects &= ~(EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST); e.ItemStatus &= ~ITS_STAYWEP; @@ -457,7 +425,7 @@ void Item_Show (entity e, float mode) } else { - bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons + bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.m_wepset & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons || e.team // weapon stay isn't supported for teamed weapons ; if(def.instanceOfWeaponPickup && !nostay && g_weapon_stay) @@ -517,7 +485,7 @@ void Item_Respawn (entity this) sound(this, CH_TRIGGER, this.itemdef.m_respawnsound, VOL_BASE, ATTEN_NORM); // play respawn sound setorigin(this, this.origin); - if (Item_ItemsTime_Allow(this.itemdef) || (this.weapons & WEPSET_SUPERWEAPONS)) + if (Item_ItemsTime_Allow(this.itemdef) || (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)) { float t = Item_ItemsTime_UpdateTime(this, 0); Item_ItemsTime_SetTime(this, t); @@ -602,13 +570,13 @@ void Item_RespawnThink(entity this) void Item_ScheduleRespawnIn(entity e, float t) { // if the respawn time is longer than 10 seconds, show a waypoint, otherwise, just respawn normally - if ((Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS) || MUTATOR_CALLHOOK(Item_ScheduleRespawn, e, t)) && (t - ITEM_RESPAWN_TICKS) > 0) + if ((Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) || MUTATOR_CALLHOOK(Item_ScheduleRespawn, e, t)) && (t - ITEM_RESPAWN_TICKS) > 0) { setthink(e, Item_RespawnCountdown); e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS); e.scheduledrespawntime = e.nextthink + ITEM_RESPAWN_TICKS; e.item_respawncounter = 0; - if(Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS)) + if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS)) { t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime); Item_ItemsTime_SetTime(e, t); @@ -622,7 +590,7 @@ void Item_ScheduleRespawnIn(entity e, float t) e.scheduledrespawntime = time + t; e.wait = time + t; - if(Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS)) + if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS)) { t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime); Item_ItemsTime_SetTime(e, t); @@ -635,6 +603,7 @@ AUTOCVAR(g_pickup_respawntime_scaling_reciprocal, float, 0.0, "Multiply respawn AUTOCVAR(g_pickup_respawntime_scaling_offset, float, 0.0, "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `offset` offsets the curve left or right - the results are not intuitive and I recommend plotting the respawn time and the number of items per player to see what's happening"); AUTOCVAR(g_pickup_respawntime_scaling_linear, float, 1.0, "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `linear` can be used to simply scale the respawn time linearly"); +/// Adjust respawn time according to the number of players. float adjust_respawntime(float normal_respawntime) { float r = autocvar_g_pickup_respawntime_scaling_reciprocal; float o = autocvar_g_pickup_respawntime_scaling_offset; @@ -644,14 +613,18 @@ float adjust_respawntime(float normal_respawntime) { return normal_respawntime; } - CheckAllowedTeams(NULL); - GetTeamCounts(NULL); + entity balance = TeamBalance_CheckAllowedTeams(NULL); + TeamBalance_GetTeamCounts(balance, NULL); int players = 0; - if (c1 != -1) players += c1; - if (c2 != -1) players += c2; - if (c3 != -1) players += c3; - if (c4 != -1) players += c4; - + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (TeamBalance_IsTeamAllowed(balance, i)) + { + players += TeamBalance_GetNumberOfPlayers(balance, i); + } + } + TeamBalance_Destroy(balance); + if (players >= 2) { return normal_respawntime * (r / (players + o) + l); } else { @@ -669,17 +642,49 @@ void Item_ScheduleRespawn(entity e) //LOG_INFOF("item %s will respawn in %f", e.classname, adjusted_respawntime); // range: adjusted_respawntime - respawntimejitter .. adjusted_respawntime + respawntimejitter - float actual_time = adjusted_respawntime + crandom() * e.respawntimejitter; - Item_ScheduleRespawnIn(e, actual_time); + float respawn_in = adjusted_respawntime + crandom() * e.respawntimejitter; + Item_ScheduleRespawnIn(e, respawn_in); } else // if respawntime is -1, this item does not respawn Item_Show(e, -1); } +AUTOCVAR(g_pickup_respawntime_initial_random, int, 1, + "For items that don't start spawned: 0: spawn after their normal respawntime; 1: spawn after `random * respawntime` with the *same* random; 2: same as 1 but each item has separate random"); + void Item_ScheduleInitialRespawn(entity e) { Item_Show(e, 0); - Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : ITEM_RESPAWNTIME_INITIAL(e))); + + float spawn_in; + if (autocvar_g_pickup_respawntime_initial_random == 0) + { + // range: respawntime .. respawntime + respawntimejitter + spawn_in = e.respawntime + random() * e.respawntimejitter; + } + else + { + float rnd; + if (autocvar_g_pickup_respawntime_initial_random == 1) + { + static float shared_random = 0; + // NOTE this code works only if items are scheduled at the same time (normal case) + // NOTE2 random() can't return exactly 1 so this check always work as intended + if (!shared_random || floor(time) > shared_random) + shared_random = floor(time) + random(); + rnd = shared_random - floor(time); + } + else + rnd = random(); + + // range: + // if respawntime >= ITEM_RESPAWN_TICKS: ITEM_RESPAWN_TICKS .. respawntime + respawntimejitter + // else: 0 .. ITEM_RESPAWN_TICKS + // this is to prevent powerups spawning unexpectedly without waypoints + spawn_in = ITEM_RESPAWN_TICKS + rnd * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS); + } + + Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : spawn_in)); } void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, @@ -700,7 +705,7 @@ void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, FOREACH(Weapons, it != WEP_Null, { // Finding a weapon which player doesn't have. - if (!(receiver.weapons & it.m_wepset) && (it.netname == weapon)) + if (!(STAT(WEAPONS, receiver) & it.m_wepset) && (it.netname == weapon)) { RandomSelection_AddEnt(it, 1, 1); break; @@ -711,53 +716,49 @@ void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, { return; } - receiver.weapons |= RandomSelection_chosen_ent.m_wepset; - if (RandomSelection_chosen_ent.ammo_type == RESOURCE_NONE) + STAT(WEAPONS, receiver) |= RandomSelection_chosen_ent.m_wepset; + if (RandomSelection_chosen_ent.ammo_type == RES_NONE) { continue; } - if (GetResourceAmount(receiver, + if (GetResource(receiver, RandomSelection_chosen_ent.ammo_type) != 0) { continue; } GiveResource(receiver, RandomSelection_chosen_ent.ammo_type, - GetResourceAmount(ammo_entity, + GetResource(ammo_entity, RandomSelection_chosen_ent.ammo_type)); } } -float Item_GiveAmmoTo(entity item, entity player, int resource_type, float ammomax) +bool Item_GiveAmmoTo(entity item, entity player, int res_type, float ammomax) { - float amount = GetResourceAmount(item, resource_type); + float amount = GetResource(item, res_type); if (amount == 0) { return false; } - float player_amount = GetResourceAmount(player, resource_type); + float player_amount = GetResource(player, res_type); if (item.spawnshieldtime) { if ((player_amount >= ammomax) && (item.pickup_anyway <= 0)) - { return false; - } - GiveResourceWithLimit(player, resource_type, amount, ammomax); - return true; } - if (g_weapon_stay != 2) - { + else if (g_weapon_stay == 2) + ammomax = min(amount, ammomax); + else return false; - } - GiveResourceWithLimit(player, resource_type, amount, min(amount, ammomax)); + if (amount < 0) + TakeResourceWithLimit(player, res_type, -amount, ammomax); + else + GiveResourceWithLimit(player, res_type, amount, ammomax); return true; } -float Item_GiveTo(entity item, entity player) +bool Item_GiveTo(entity item, entity player) { - float pickedup; - // if nothing happens to player, just return without taking the item - pickedup = false; int _switchweapon = 0; // in case the player has autoswitch enabled do the following: // if the player is using their best weapon before items are given, they @@ -773,24 +774,25 @@ float Item_GiveTo(entity item, entity player) if(player.(weaponentity).m_switchweapon == w_getbestweapon(player, weaponentity)) _switchweapon |= BIT(slot); - if(!(player.weapons & WepSet_FromWeapon(player.(weaponentity).m_switchweapon))) + if(!(STAT(WEAPONS, player) & WepSet_FromWeapon(player.(weaponentity).m_switchweapon))) _switchweapon |= BIT(slot); } } } - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_HEALTH, item.max_health); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_ARMOR, item.max_armorvalue); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_SHELLS, g_pickup_shells_max); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_BULLETS, g_pickup_nails_max); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_ROCKETS, g_pickup_rockets_max); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_CELLS, g_pickup_cells_max); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_PLASMA, g_pickup_plasma_max); - pickedup |= Item_GiveAmmoTo(item, player, RESOURCE_FUEL, g_pickup_fuel_max); + bool pickedup = false; + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_HEALTH, item.max_health)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_ARMOR, item.max_armorvalue)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_SHELLS, g_pickup_shells_max)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_BULLETS, g_pickup_nails_max)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_ROCKETS, g_pickup_rockets_max)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_CELLS, g_pickup_cells_max)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_PLASMA, g_pickup_plasma_max)); + pickedup = (pickedup || Item_GiveAmmoTo(item, player, RES_FUEL, g_pickup_fuel_max)); if (item.itemdef.instanceOfWeaponPickup) { WepSet w; - w = item.weapons; - w &= ~player.weapons; + w = STAT(WEAPONS, item); + w &= ~STAT(WEAPONS, player); if (w || (item.spawnshieldtime && item.pickup_anyway > 0)) { @@ -823,7 +825,8 @@ float Item_GiveTo(entity item, entity player) { pickedup = true; player.items |= its; - Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); + // TODO: we probably want to show a message in the console, but not this one! + //Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); } if (item.strength_finished) @@ -847,10 +850,10 @@ float Item_GiveTo(entity item, entity player) pickedup = true; if (!pickedup) - return 0; + return false; // crude hack to enforce switching weapons - if(g_cts && item.itemdef.instanceOfWeaponPickup) + if(g_cts && item.itemdef.instanceOfWeaponPickup && !CS(player).cvar_cl_cts_noautoswitch) { for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -858,7 +861,7 @@ float Item_GiveTo(entity item, entity player) if(player.(weaponentity).m_weapon != WEP_Null || slot == 0) W_SwitchWeapon_Force(player, Weapons_from(item.weapon), weaponentity); } - return 1; + return true; } if(_switchweapon) @@ -872,7 +875,7 @@ float Item_GiveTo(entity item, entity player) } } - return 1; + return true; } void Item_Touch(entity this, entity toucher) @@ -924,7 +927,7 @@ void Item_Touch(entity this, entity toucher) LABEL(pickup) - toucher.last_pickup = time; + STAT(LAST_PICKUP, toucher) = time; Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); _sound (toucher, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM); @@ -980,7 +983,7 @@ void Item_Reset(entity this) { WaypointSprite_Kill(this.waypointsprite_attached); } - if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially! + if (this.itemdef.instanceOfPowerup || (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially! { Item_ScheduleInitialRespawn(this); } @@ -1039,7 +1042,7 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup float weapon_pickupevalfunc(entity player, entity item) { // See if I have it already - if(player.weapons & item.weapons) + if(STAT(WEAPONS, player) & STAT(WEAPONS, item)) { // If I can pick it up if(!item.spawnshieldtime) @@ -1050,7 +1053,7 @@ float weapon_pickupevalfunc(entity player, entity item) // reduce weapon value if bot already got a good arsenal float c = 1; int weapons_value = 0; - FOREACH(Weapons, it != WEP_Null && (player.weapons & it.m_wepset), { + FOREACH(Weapons, it != WEP_Null && (STAT(WEAPONS, player) & it.m_wepset), { weapons_value += it.bot_pickupbasevalue; }); c -= bound(0, weapons_value / 20000, 1) * 0.5; @@ -1069,12 +1072,12 @@ float ammo_pickupevalfunc(entity player, entity item) if(item.itemdef.instanceOfWeaponPickup) { entity ammo = NULL; - if(item.ammo_shells) { need_shells = true; ammo = ITEM_Shells; } - else if(item.ammo_nails) { need_nails = true; ammo = ITEM_Bullets; } - else if(item.ammo_rockets) { need_rockets = true; ammo = ITEM_Rockets; } - else if(item.ammo_cells) { need_cells = true; ammo = ITEM_Cells; } - else if(item.ammo_plasma) { need_plasma = true; ammo = ITEM_Plasma; } - else if(item.ammo_fuel) { need_fuel = true; ammo = ITEM_JetpackFuel; } + if(GetResource(item, RES_SHELLS)) { need_shells = true; ammo = ITEM_Shells; } + else if(GetResource(item, RES_BULLETS)) { need_nails = true; ammo = ITEM_Bullets; } + else if(GetResource(item, RES_ROCKETS)) { need_rockets = true; ammo = ITEM_Rockets; } + else if(GetResource(item, RES_CELLS)) { need_cells = true; ammo = ITEM_Cells; } + else if(GetResource(item, RES_PLASMA)) { need_plasma = true; ammo = ITEM_Plasma; } + else if(GetResource(item, RES_FUEL)) { need_fuel = true; ammo = ITEM_JetpackFuel; } if(!ammo) return 0; @@ -1084,17 +1087,17 @@ float ammo_pickupevalfunc(entity player, entity item) else { FOREACH(Weapons, it != WEP_Null, { - if(!(player.weapons & (it.m_wepset))) + if(!(STAT(WEAPONS, player) & (it.m_wepset))) continue; switch(it.ammo_type) { - case RESOURCE_SHELLS: need_shells = true; break; - case RESOURCE_BULLETS: need_nails = true; break; - case RESOURCE_ROCKETS: need_rockets = true; break; - case RESOURCE_CELLS: need_cells = true; break; - case RESOURCE_PLASMA: need_plasma = true; break; - case RESOURCE_FUEL: need_fuel = true; break; + case RES_SHELLS: need_shells = true; break; + case RES_BULLETS: need_nails = true; break; + case RES_ROCKETS: need_rockets = true; break; + case RES_CELLS: need_cells = true; break; + case RES_PLASMA: need_plasma = true; break; + case RES_FUEL: need_fuel = true; break; } }); rating = item.bot_pickupbasevalue; @@ -1102,23 +1105,23 @@ float ammo_pickupevalfunc(entity player, entity item) float noammorating = 0.5; - if ((need_shells) && (item.ammo_shells) && (player.ammo_shells < g_pickup_shells_max)) - c = item.ammo_shells / max(noammorating, player.ammo_shells); + if ((need_shells) && GetResource(item, RES_SHELLS) && (GetResource(player, RES_SHELLS) < g_pickup_shells_max)) + c = GetResource(item, RES_SHELLS) / max(noammorating, GetResource(player, RES_SHELLS)); - if ((need_nails) && (item.ammo_nails) && (player.ammo_nails < g_pickup_nails_max)) - c = item.ammo_nails / max(noammorating, player.ammo_nails); + if ((need_nails) && GetResource(item, RES_BULLETS) && (GetResource(player, RES_BULLETS) < g_pickup_nails_max)) + c = GetResource(item, RES_BULLETS) / max(noammorating, GetResource(player, RES_BULLETS)); - if ((need_rockets) && (item.ammo_rockets) && (player.ammo_rockets < g_pickup_rockets_max)) - c = item.ammo_rockets / max(noammorating, player.ammo_rockets); + if ((need_rockets) && GetResource(item, RES_ROCKETS) && (GetResource(player, RES_ROCKETS) < g_pickup_rockets_max)) + c = GetResource(item, RES_ROCKETS) / max(noammorating, GetResource(player, RES_ROCKETS)); - if ((need_cells) && (item.ammo_cells) && (player.ammo_cells < g_pickup_cells_max)) - c = item.ammo_cells / max(noammorating, player.ammo_cells); + if ((need_cells) && GetResource(item, RES_CELLS) && (GetResource(player, RES_CELLS) < g_pickup_cells_max)) + c = GetResource(item, RES_CELLS) / max(noammorating, GetResource(player, RES_CELLS)); - if ((need_plasma) && (item.ammo_plasma) && (player.ammo_plasma < g_pickup_plasma_max)) - c = item.ammo_plasma / max(noammorating, player.ammo_plasma); + if ((need_plasma) && GetResource(item, RES_PLASMA) && (GetResource(player, RES_PLASMA) < g_pickup_plasma_max)) + c = GetResource(item, RES_PLASMA) / max(noammorating, GetResource(player, RES_PLASMA)); - if ((need_fuel) && (item.ammo_fuel) && (player.ammo_fuel < g_pickup_fuel_max)) - c = item.ammo_fuel / max(noammorating, player.ammo_fuel); + if ((need_fuel) && GetResource(item, RES_FUEL) && (GetResource(player, RES_FUEL) < g_pickup_fuel_max)) + c = GetResource(item, RES_FUEL) / max(noammorating, GetResource(player, RES_FUEL)); rating *= min(c, 2); if(wpn) @@ -1126,15 +1129,13 @@ float ammo_pickupevalfunc(entity player, entity item) return rating; } -.int item_group; -.int item_group_count; float healtharmor_pickupevalfunc(entity player, entity item) { float c = 0; float rating = item.bot_pickupbasevalue; - float itemarmor = item.armorvalue; - float itemhealth = item.health; + float itemarmor = GetResource(item, RES_ARMOR); + float itemhealth = GetResource(item, RES_HEALTH); if(item.item_group) { @@ -1142,17 +1143,17 @@ float healtharmor_pickupevalfunc(entity player, entity item) itemhealth *= min(4, item.item_group_count); } - if (itemarmor && (player.armorvalue < item.max_armorvalue)) - c = itemarmor / max(1, player.armorvalue * 2/3 + player.health * 1/3); + if (itemarmor && (GetResource(player, RES_ARMOR) < item.max_armorvalue)) + c = itemarmor / max(1, GetResource(player, RES_ARMOR) * 2/3 + GetResource(player, RES_HEALTH) * 1/3); - if (itemhealth && (player.health < item.max_health)) - c = itemhealth / max(1, player.health); + if (itemhealth && (GetResource(player, RES_HEALTH) < item.max_health)) + c = itemhealth / max(1, GetResource(player, RES_HEALTH)); rating *= min(2, c); return rating; } -void Item_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +void Item_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { if(ITEM_DAMAGE_NEEDKILL(deathtype)) RemoveItem(this); @@ -1179,7 +1180,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default this.item_pickupsound_ent = pickupsound; if(def.m_iteminit) - def.m_iteminit(this); + def.m_iteminit(def, this); if(!this.respawntime) // both need to be set { @@ -1202,7 +1203,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default } if(weaponid) - this.weapons = WepSet_FromWeapon(Weapons_from(weaponid)); + STAT(WEAPONS, this) = WepSet_FromWeapon(Weapons_from(weaponid)); this.flags = FL_ITEM | itemflags; IL_PUSH(g_items, this); @@ -1313,7 +1314,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default || (def.instanceOfHealth && def != ITEM_HealthSmall) || (def.instanceOfArmor && def != ITEM_ArmorSmall) || (itemid & (IT_KEY1 | IT_KEY2)) - ) this.target = "###item###"; // for finding the nearest item using find() + ) this.target = "###item###"; // for finding the nearest item using findnearest Item_ItemsTime_SetTime(this, 0); } @@ -1335,7 +1336,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(def.instanceOfPowerup) this.ItemStatus |= ITS_ANIMATE1; - if(this.armorvalue || this.health) + if(GetResource(this, RES_ARMOR) || GetResource(this, RES_HEALTH)) this.ItemStatus |= ITS_ANIMATE2; } @@ -1397,7 +1398,7 @@ int group_count = 1; void setItemGroup(entity this) { - if(!IS_SMALL(this.itemdef)) + if(!IS_SMALL(this.itemdef) || Item_IsLoot(this)) return; FOREACH_ENTITY_RADIUS(this.origin, 120, (it != this) && IS_SMALL(it.itemdef), @@ -1503,7 +1504,7 @@ spawnfunc(target_items) s = Buff_UndeprecateName(argv(j)); if(s == it.m_name) { - this.buffs |= (it.m_itemid); + STAT(BUFFS, this) |= (it.m_itemid); break; } }); @@ -1511,7 +1512,7 @@ spawnfunc(target_items) s = W_UndeprecateName(argv(j)); if(s == it.netname) { - this.weapons |= (it.m_wepset); + STAT(WEAPONS, this) |= (it.m_wepset); if(this.spawnflags == 0 || this.spawnflags == 2) it.wr_init(it); break; @@ -1555,16 +1556,16 @@ spawnfunc(target_items) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, this.superweapons_finished * boolean(this.items & IT_SUPERWEAPON), "superweapons"); this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, boolean(this.items & ITEM_Jetpack.m_itemid), "jetpack"); this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, boolean(this.items & ITEM_JetpackRegen.m_itemid), "fuel_regen"); - if(this.ammo_shells != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_shells), "shells"); - if(this.ammo_nails != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_nails), "nails"); - if(this.ammo_rockets != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_rockets), "rockets"); - if(this.ammo_cells != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_cells), "cells"); - if(this.ammo_plasma != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_plasma), "plasma"); - if(this.ammo_fuel != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_fuel), "fuel"); - if(this.health != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.health), "health"); - if(this.armorvalue != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.armorvalue), "armor"); - FOREACH(Buffs, it != BUFF_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(this.buffs & (it.m_itemid)), it.m_name)); - FOREACH(Weapons, it != WEP_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(this.weapons & (it.m_wepset)), it.netname)); + if(GetResource(this, RES_SHELLS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_SHELLS)), "shells"); + if(GetResource(this, RES_BULLETS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_BULLETS)), "nails"); + if(GetResource(this, RES_ROCKETS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_ROCKETS)), "rockets"); + if(GetResource(this, RES_CELLS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_CELLS)), "cells"); + if(GetResource(this, RES_PLASMA) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_PLASMA)), "plasma"); + if(GetResource(this, RES_FUEL) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_FUEL)), "fuel"); + if(GetResource(this, RES_HEALTH) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_HEALTH)), "health"); + if(GetResource(this, RES_ARMOR) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_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)); } this.netname = strzone(this.netname); //print(this.netname, "\n"); @@ -1583,59 +1584,59 @@ float GiveWeapon(entity e, float wpn, float op, float val) { WepSet v0, v1; WepSet s = WepSet_FromWeapon(Weapons_from(wpn)); - v0 = (e.weapons & s); + v0 = (STAT(WEAPONS, e) & s); switch(op) { case OP_SET: if(val > 0) - e.weapons |= s; + STAT(WEAPONS, e) |= s; else - e.weapons &= ~s; + STAT(WEAPONS, e) &= ~s; break; case OP_MIN: case OP_PLUS: if(val > 0) - e.weapons |= s; + STAT(WEAPONS, e) |= s; break; case OP_MAX: if(val <= 0) - e.weapons &= ~s; + STAT(WEAPONS, e) &= ~s; break; case OP_MINUS: if(val > 0) - e.weapons &= ~s; + STAT(WEAPONS, e) &= ~s; break; } - v1 = (e.weapons & s); + v1 = (STAT(WEAPONS, e) & s); return (v0 != v1); } bool GiveBuff(entity e, Buff thebuff, int op, int val) { - bool had_buff = (e.buffs & thebuff.m_itemid); + bool had_buff = (STAT(BUFFS, e) & thebuff.m_itemid); switch(op) { case OP_SET: if(val > 0) - e.buffs |= thebuff.m_itemid; + STAT(BUFFS, e) |= thebuff.m_itemid; else - e.buffs &= ~thebuff.m_itemid; + STAT(BUFFS, e) &= ~thebuff.m_itemid; break; case OP_MIN: case OP_PLUS: if(val > 0) - e.buffs |= thebuff.m_itemid; + STAT(BUFFS, e) |= thebuff.m_itemid; break; case OP_MAX: if(val <= 0) - e.buffs &= ~thebuff.m_itemid; + STAT(BUFFS, e) &= ~thebuff.m_itemid; break; case OP_MINUS: if(val > 0) - e.buffs &= ~thebuff.m_itemid; + STAT(BUFFS, e) &= ~thebuff.m_itemid; break; } - bool have_buff = (e.buffs & thebuff.m_itemid); + bool have_buff = (STAT(BUFFS, e) & thebuff.m_itemid); return (had_buff != have_buff); } @@ -1662,6 +1663,22 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa else if(v0 > v1) e.(regenfield) = max(e.(regenfield), time + regentime); } +bool GiveResourceValue(entity e, int res_type, int op, int val) +{ + int v0 = GetResource(e, res_type); + switch (op) + { + // min 100 cells = at least 100 cells + case OP_SET: SetResource(e, res_type, val); break; + case OP_MIN: SetResource(e, res_type, max(v0, val)); break; + case OP_MAX: SetResource(e, res_type, min(v0, val)); break; + case OP_PLUS: SetResource(e, res_type, v0 + val); break; + case OP_MINUS: SetResource(e, res_type, v0 - val); break; + } + int v1 = GetResource(e, res_type); + return v0 != v1; +} + float GiveItems(entity e, float beginarg, float endarg) { float got, i, val, op; @@ -1694,14 +1711,14 @@ float GiveItems(entity e, float beginarg, float endarg) PREGIVE(e, strength_finished); PREGIVE(e, invincible_finished); PREGIVE(e, superweapons_finished); - PREGIVE(e, ammo_nails); - PREGIVE(e, ammo_cells); - PREGIVE(e, ammo_plasma); - PREGIVE(e, ammo_shells); - PREGIVE(e, ammo_rockets); - PREGIVE(e, ammo_fuel); - PREGIVE(e, armorvalue); - PREGIVE(e, health); + PREGIVE_RESOURCE(e, RES_BULLETS); + PREGIVE_RESOURCE(e, RES_CELLS); + PREGIVE_RESOURCE(e, RES_PLASMA); + PREGIVE_RESOURCE(e, RES_SHELLS); + PREGIVE_RESOURCE(e, RES_ROCKETS); + PREGIVE_RESOURCE(e, RES_FUEL); + PREGIVE_RESOURCE(e, RES_ARMOR); + PREGIVE_RESOURCE(e, RES_HEALTH); for(i = beginarg; i < endarg; ++i) { @@ -1738,19 +1755,19 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); case "all": got += GiveBit(e, items, ITEM_Jetpack.m_itemid, op, val); - got += GiveValue(e, health, op, val); - got += GiveValue(e, armorvalue, op, val); + got += GiveResourceValue(e, RES_HEALTH, op, val); + got += GiveResourceValue(e, RES_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, RES_CELLS, op, val); + got += GiveResourceValue(e, RES_PLASMA, op, val); + got += GiveResourceValue(e, RES_SHELLS, op, val); + got += GiveResourceValue(e, RES_BULLETS, op, val); + got += GiveResourceValue(e, RES_ROCKETS, op, val); + got += GiveResourceValue(e, RES_FUEL, op, val); break; case "unlimited_ammo": got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); @@ -1777,29 +1794,29 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveValue(e, superweapons_finished, op, val); break; case "cells": - got += GiveValue(e, ammo_cells, op, val); + got += GiveResourceValue(e, RES_CELLS, op, val); break; case "plasma": - got += GiveValue(e, ammo_plasma, op, val); + got += GiveResourceValue(e, RES_PLASMA, op, val); break; case "shells": - got += GiveValue(e, ammo_shells, op, val); + got += GiveResourceValue(e, RES_SHELLS, op, val); break; case "nails": case "bullets": - got += GiveValue(e, ammo_nails, op, val); + got += GiveResourceValue(e, RES_BULLETS, op, val); break; case "rockets": - got += GiveValue(e, ammo_rockets, op, val); + got += GiveResourceValue(e, RES_ROCKETS, op, val); break; case "health": - got += GiveValue(e, health, op, val); + got += GiveResourceValue(e, RES_HEALTH, op, val); break; case "armor": - got += GiveValue(e, armorvalue, op, val); + got += GiveResourceValue(e, RES_ARMOR, op, val); break; case "fuel": - got += GiveValue(e, ammo_fuel, op, val); + got += GiveResourceValue(e, RES_FUEL, op, val); break; default: FOREACH(Buffs, it != BUFF_Null && Buff_UndeprecateName(cmd) == it.m_name, @@ -1824,23 +1841,23 @@ float GiveItems(entity e, float beginarg, float endarg) FOREACH(Weapons, it != WEP_Null, { POSTGIVE_WEAPON(e, it, SND_WEAPONPICKUP, SND_Null); if(!(save_weapons & (it.m_wepset))) - if(e.weapons & (it.m_wepset)) + if(STAT(WEAPONS, e) & (it.m_wepset)) it.wr_init(it); }); 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, RES_BULLETS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RES_CELLS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RES_PLASMA, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RES_SHELLS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RES_ROCKETS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RES_ROT(e, RES_FUEL, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RES_ROT(e, RES_ARMOR, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_ARMOR25, SND_Null); + POSTGIVE_RES_ROT(e, RES_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(e.weapons & WEPSET_SUPERWEAPONS) + if(!g_weaponarena && STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) e.superweapons_finished = autocvar_g_balance_superweapons_time; if(e.strength_finished <= 0) @@ -1860,7 +1877,7 @@ float GiveItems(entity e, float beginarg, float endarg) { .entity weaponentity = weaponentities[slot]; if(e.(weaponentity).m_weapon != WEP_Null || slot == 0) - if(!(e.weapons & WepSet_FromWeapon(e.(weaponentity).m_switchweapon))) + if(!(STAT(WEAPONS, e) & WepSet_FromWeapon(e.(weaponentity).m_switchweapon))) _switchweapon |= BIT(slot); }