X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fitems%2Fitems.qc;h=e68d858a8e72e55d9ad4a156cd0b81400cafbd8e;hb=d77d35f12d537628d021b946d5fd6aa20734042d;hp=8a8e6eca44718182210f7fac67cbf319d71a9dba;hpb=0ce97ac6e23a4bfa907cd5a25990b4abe90656cf;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/items/items.qc b/qcsrc/server/items/items.qc index 8a8e6eca4..e68d858a8 100644 --- a/qcsrc/server/items/items.qc +++ b/qcsrc/server/items/items.qc @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -56,16 +58,16 @@ bool ItemSend(entity this, entity to, int sf) if(sf & ISF_MODEL) { - WriteShort(MSG_ENTITY, this.fade_end); - WriteShort(MSG_ENTITY, this.fade_start); + WriteShort(MSG_ENTITY, bound(0, this.fade_end, 32767)); + WriteShort(MSG_ENTITY, bound(0, this.fade_start, 32767)); if(this.mdl == "") LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now"); WriteString(MSG_ENTITY, this.mdl); + WriteByte(MSG_ENTITY, bound(0, this.skin, 255)); } - if(sf & ISF_COLORMAP) { WriteShort(MSG_ENTITY, this.colormap); @@ -198,7 +200,6 @@ void Item_Respawn(entity this) { Item_Show(this, 1); 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) || (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)) { @@ -247,13 +248,13 @@ void Item_RespawnCountdown(entity this) } } while (0); bool mutator_returnvalue = MUTATOR_CALLHOOK(Item_RespawnCountdown, this); - if(this.waypointsprite_attached) - { - GameItem def = this.itemdef; - if (Item_ItemsTime_SpectatorOnly(def) && !mutator_returnvalue) - WaypointSprite_UpdateRule(this.waypointsprite_attached, 0, SPRITERULE_SPECTATOR); - WaypointSprite_UpdateBuildFinished(this.waypointsprite_attached, time + ITEM_RESPAWN_TICKS); - } + if(this.waypointsprite_attached) + { + GameItem def = this.itemdef; + if (Item_ItemsTime_SpectatorOnly(def) && !mutator_returnvalue) + WaypointSprite_UpdateRule(this.waypointsprite_attached, 0, SPRITERULE_SPECTATOR); + WaypointSprite_UpdateBuildFinished(this.waypointsprite_attached, time + ITEM_RESPAWN_TICKS); + } } if(this.waypointsprite_attached) @@ -447,7 +448,7 @@ void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, } } -bool Item_GiveAmmoTo(entity item, entity player, int res_type, float ammomax) +bool Item_GiveAmmoTo(entity item, entity player, Resource res_type, float ammomax) { float amount = GetResource(item, res_type); if (amount == 0) @@ -475,6 +476,15 @@ bool Item_GiveAmmoTo(entity item, entity player, int res_type, float ammomax) return true; } +void Item_NotifyWeapon(entity player, int wep) +{ + FOREACH_CLIENT(IS_REAL_CLIENT(it) && (it == player || (IS_SPEC(it) && it.enemy == player)), { + msg_entity = it; + WriteHeader(MSG_ONE, TE_CSQC_WEAPONPICKUP); + WriteByte(MSG_ONE, wep); + }); +} + bool Item_GiveTo(entity item, entity player) { // if nothing happens to player, just return without taking the item @@ -483,7 +493,7 @@ bool Item_GiveTo(entity item, entity player) // if the player is using their best weapon before items are given, they // probably want to switch to an even better weapon after items are given - if(CS_CVAR(player).autoswitch) + if(CS_CVAR(player).cvar_cl_autoswitch) { for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -509,15 +519,18 @@ bool Item_GiveTo(entity item, entity player) pickedup |= Item_GiveAmmoTo(item, player, RES_FUEL, g_pickup_fuel_max); if (item.itemdef.instanceOfWeaponPickup) { - WepSet w; + WepSet w, wp; w = STAT(WEAPONS, item); - w &= ~STAT(WEAPONS, player); + wp = w & ~STAT(WEAPONS, player); - if (w || (item.spawnshieldtime && item.pickup_anyway > 0)) + if (wp || (item.spawnshieldtime && item.pickup_anyway > 0)) { pickedup = true; FOREACH(Weapons, it != WEP_Null, { if(w & (it.m_wepset)) + Item_NotifyWeapon(player, it.m_id); + + if(wp & (it.m_wepset)) { for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -551,22 +564,42 @@ bool Item_GiveTo(entity item, entity player) if (item.strength_finished) { pickedup = true; - StatusEffects_apply(STATUSEFFECT_Strength, player, max(StatusEffects_gettime(STATUSEFFECT_Strength, player), time) + item.strength_finished, 0); + float t = max(StatusEffects_gettime(STATUSEFFECT_Strength, player), time); + if (autocvar_g_powerups_stack) + t += item.strength_finished; + else + t = max(t, time + item.strength_finished); + StatusEffects_apply(STATUSEFFECT_Strength, player, t, 0); } if (item.invincible_finished) { pickedup = true; - StatusEffects_apply(STATUSEFFECT_Shield, player, max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time) + item.invincible_finished, 0); + float t = max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time); + if (autocvar_g_powerups_stack) + t += item.invincible_finished; + else + t = max(t, time + item.invincible_finished); + StatusEffects_apply(STATUSEFFECT_Shield, player, t, 0); } if (item.speed_finished) { pickedup = true; - StatusEffects_apply(STATUSEFFECT_Speed, player, max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time) + item.speed_finished, 0); + float t = max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time); + if (autocvar_g_powerups_stack) + t += item.speed_finished; + else + t = max(t, time + item.speed_finished); + StatusEffects_apply(STATUSEFFECT_Speed, player, t, 0); } if (item.invisibility_finished) { pickedup = true; - StatusEffects_apply(STATUSEFFECT_Invisibility, player, max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time) + item.invisibility_finished, 0); + float t = max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time); + if (autocvar_g_powerups_stack) + t += item.invisibility_finished; + else + t = max(t, time + item.invisibility_finished); + StatusEffects_apply(STATUSEFFECT_Invisibility, player, t, 0); } if (item.superweapons_finished) { @@ -614,7 +647,7 @@ void Item_Touch(entity this, entity toucher) { if (ITEM_TOUCH_NEEDKILL()) { - delete(this); + RemoveItem(this); return; } } @@ -707,7 +740,6 @@ LABEL(pickup) void Item_Reset(entity this) { Item_Show(this, !this.state); - setorigin(this, this.origin); if (Item_IsLoot(this)) { @@ -727,45 +759,48 @@ void Item_Reset(entity this) void Item_FindTeam(entity this) { - entity e; + if(!(this.effects & EF_NOGUNBOB)) // marker for item team search + return; - if(this.effects & EF_NODRAW) + LOG_TRACE("Initializing item team ", ftos(this.team)); + RandomSelection_Init(); + IL_EACH(g_items, it.team == this.team, { - // marker for item team search - LOG_TRACE("Initializing item team ", ftos(this.team)); - RandomSelection_Init(); - IL_EACH(g_items, it.team == this.team, - { - if(it.itemdef) // is a registered item - RandomSelection_AddEnt(it, it.cnt, 0); - }); + if(it.itemdef) // is a registered item + RandomSelection_AddEnt(it, it.cnt, 0); + }); - e = RandomSelection_chosen_ent; - if (!e) - return; + entity e = RandomSelection_chosen_ent; + if (!e) + return; - IL_EACH(g_items, it.team == this.team, + IL_EACH(g_items, it.team == this.team, + { + if(it.itemdef) // is a registered item { - if(it.itemdef) // is a registered item + if(it != e) { - if(it != e) - { - // make it non-spawned - Item_Show(it, -1); - it.state = 1; // state 1 = initially hidden item, apparently - } - else - Item_Reset(it); - it.effects &= ~EF_NODRAW; + Item_Show(it, -1); // make it non-spawned + if (it.waypointsprite_attached) + WaypointSprite_Kill(it.waypointsprite_attached); + it.nextthink = 0; // disable any scheduled powerup spawn } - }); - } + else + Item_Reset(it); + + // leave 'this' marked so Item_FindTeam() works when called again via this.reset + if(it != this) + it.effects &= ~EF_NOGUNBOB; + } + }); } // Savage: used for item garbage-collection void RemoveItem(entity this) { if(wasfreed(this) || !this) { return; } + if(this.waypointsprite_attached) + WaypointSprite_Kill(this.waypointsprite_attached); Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); delete(this); } @@ -799,24 +834,21 @@ float weapon_pickupevalfunc(entity player, entity item) float ammo_pickupevalfunc(entity player, entity item) { - bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false; + entity item_resource = NULL; // pointer to the resource that may be associated with the given item entity wpn = NULL; float c = 0; float rating = 0; - // Detect needed ammo + // detect needed ammo if(item.itemdef.instanceOfWeaponPickup) { - entity ammo = NULL; - 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; } - + entity res = item.itemdef.m_weapon.ammo_type; + entity ammo = (res != RES_NONE) ? GetAmmoItem(res) : NULL; if(!ammo) return 0; + if(res != RES_NONE && GetResource(item, res)) + item_resource = res; + wpn = item; rating = ammo.m_botvalue; } @@ -825,15 +857,13 @@ float ammo_pickupevalfunc(entity player, entity item) FOREACH(Weapons, it != WEP_Null, { if(!(STAT(WEAPONS, player) & (it.m_wepset))) continue; + if(it.ammo_type == RES_NONE) + continue; - switch(it.ammo_type) + if(GetResource(item, it.ammo_type)) { - 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; + item_resource = it.ammo_type; + break; } }); rating = item.bot_pickupbasevalue; @@ -841,23 +871,8 @@ float ammo_pickupevalfunc(entity player, entity item) float noammorating = 0.5; - 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) && 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) && 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) && 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) && 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) && GetResource(item, RES_FUEL) && (GetResource(player, RES_FUEL) < g_pickup_fuel_max)) - c = GetResource(item, RES_FUEL) / max(noammorating, GetResource(player, RES_FUEL)); + if(item_resource && (GetResource(player, item_resource) < GetResourceLimit(player, item_resource))) + c = GetResource(item, item_resource) / max(noammorating, GetResource(player, item_resource)); rating *= min(c, 2); if(wpn) @@ -954,17 +969,27 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default precache_model(this.model); precache_sound(this.item_pickupsound); + if(q3compat && !this.team) + { + string t = GetField_fullspawndata(this, "team", false); + // bones_was_here: this hack is cheaper than changing to a .string strcmp() + if(t) this.team = crc16(false, t); + } + if (Item_IsLoot(this)) { - this.reset = SUB_Remove; + this.reset = RemoveItem; set_movetype(this, MOVETYPE_TOSS); // Savage: remove thrown items after a certain period of time ("garbage collection") setthink(this, RemoveItem); - this.nextthink = time + 20; + this.nextthink = time + autocvar_g_items_dropped_lifetime; this.takedamage = DAMAGE_YES; this.event_damage = Item_Damage; + // enable this to have thrown items burn in lava + //this.damagedbycontents = true; + //IL_PUSH(g_damagedbycontents, this); if (Item_IsExpiring(this)) { @@ -993,7 +1018,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(this.angles != '0 0 0') this.SendFlags |= ISF_ANGLES; - this.reset = Item_Reset; + this.reset = this.team ? Item_FindTeam : Item_Reset; // it's a level item if(this.spawnflags & 1) this.noalign = 1; @@ -1034,19 +1059,19 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(autocvar_spawn_debug >= 2) { - // why not flags & fl_item? - FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, { - LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin)); - LOG_TRACE(" vs ", it.netname, vtos(it.origin)); - error("Mapper sucks."); - }); + // why not flags & fl_item? + FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, { + LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin)); + LOG_TRACE(" vs ", it.netname, vtos(it.origin)); + error("Mapper sucks."); + }); this.is_item = true; } weaponsInMap |= WepSet_FromWeapon(REGISTRY_GET(Weapons, weaponid)); - if ( def.instanceOfPowerup - || def.instanceOfWeaponPickup + if ( def.instanceOfPowerup + || def.instanceOfWeaponPickup || (def.instanceOfHealth && def != ITEM_HealthSmall) || (def.instanceOfArmor && def != ITEM_ArmorSmall) || (itemid & (IT_KEY1 | IT_KEY2)) @@ -1068,6 +1093,10 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default setmodel(this, MDL_Null); // precision set below //this.effects |= EF_LOWPRECISION; + // support skinned models for powerups + if(!this.skin) + this.skin = def.m_skin; + setsize (this, this.pos1 = def.m_mins, this.pos2 = def.m_maxs); this.SendFlags |= ISF_SIZE; @@ -1082,6 +1111,8 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(Item_IsLoot(this)) this.gravity = 1; + else + this.glowmod = def.m_color; if(def.instanceOfWeaponPickup) { @@ -1098,7 +1129,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(!this.cnt) this.cnt = 1; // item probability weight - this.effects |= EF_NODRAW; // marker for item team search + this.effects |= EF_NOGUNBOB; // marker for item team search InitializeEntity(this, Item_FindTeam, INITPRIO_FINDTARGET); } else @@ -1119,18 +1150,21 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default void StartItem(entity this, GameItem def) { - def = def.m_spawnfunc_hookreplace(def, this); - if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED) - { - delete(this); - return; - } - this.classname = def.m_canonical_spawnfunc; - _StartItem( - this, - this.itemdef = def, - def.m_respawntime(), // defaultrespawntime - def.m_respawntimejitter() // defaultrespawntimejitter + def = def.m_spawnfunc_hookreplace(def, this); + + if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED) + { + delete(this); + return; // TODO does not set startitem_failed + } + + this.classname = def.m_canonical_spawnfunc; + + _StartItem( + this, + this.itemdef = def, + def.m_respawntime(), // defaultrespawntime + def.m_respawntimejitter() // defaultrespawntimejitter ); } @@ -1247,7 +1281,7 @@ spawnfunc(target_items) { FOREACH(StatusEffect, it.instanceOfBuff, { - string s = Buff_UndeprecateName(argv(j)); + string s = Buff_CompatName(argv(j)); if(s == it.netname) { this.buffdef = it; @@ -1257,8 +1291,8 @@ spawnfunc(target_items) } }); FOREACH(Weapons, it != WEP_Null, { - string s = W_UndeprecateName(argv(j)); - if(s == it.netname) + string s = argv(j); + if(s == it.netname || s == it.m_deprecated_netname) { STAT(WEAPONS, this) |= (it.m_wepset); if(this.spawnflags == 0 || this.spawnflags == 2) @@ -1323,7 +1357,8 @@ spawnfunc(target_items) n = tokenize_console(this.netname); for(int j = 0; j < n; ++j) { - FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(argv(j)) == it.netname, { + string cmd = argv(j); + FOREACH(Weapons, it != WEP_Null && (cmd == it.netname || cmd == it.m_deprecated_netname), { it.wr_init(it); break; }); @@ -1404,12 +1439,12 @@ void GiveSound(entity e, float v0, float v1, float t, Sound snd_incr, Sound snd_ if(v1 <= v0 - t) { if(snd_decr != NULL) - sound (e, CH_TRIGGER, snd_decr, VOL_BASE, ATTEN_NORM); + sound(e, CH_TRIGGER, snd_decr, VOL_BASE, ATTEN_NORM); } else if(v0 >= v0 + t) { if(snd_incr != NULL) - sound (e, CH_TRIGGER, snd_incr, VOL_BASE, ATTEN_NORM); + sound(e, ((snd_incr == SND_POWERUP) ? CH_TRIGGER_SINGLE : CH_TRIGGER), snd_incr, VOL_BASE, ATTEN_NORM); } } @@ -1420,7 +1455,7 @@ 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) +bool GiveResourceValue(entity e, Resource res_type, int op, int val) { int v0 = GetResource(e, res_type); float new_val = 0; @@ -1482,7 +1517,7 @@ float GiveItems(entity e, float beginarg, float endarg) int _switchweapon = 0; - if(CS_CVAR(e).autoswitch) + if(CS_CVAR(e).cvar_cl_autoswitch) { for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -1622,12 +1657,12 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveResourceValue(e, RES_FUEL, op, val); break; default: - FOREACH(StatusEffect, it.instanceOfBuff && buff_Available(it) && Buff_UndeprecateName(cmd) == it.netname, + FOREACH(StatusEffect, it.instanceOfBuff && buff_Available(it) && Buff_CompatName(cmd) == it.netname, { got += GiveBuff(e, it, op, val); break; }); - FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(cmd) == it.netname, { + FOREACH(Weapons, it != WEP_Null && (cmd == it.netname || cmd == it.m_deprecated_netname), { got += GiveWeapon(e, it.m_id, op, val); break; });