X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Ft_items.qc;h=2e7b9bfbaf11bdf64403edfe663838d465cd2b2b;hb=c3ec1741fd95b47daee57588acd067a1eefc3239;hp=46c7342f4ead3431f4a67995024da8339dab4740;hpb=a7b24450e4c7ca75636df1375d01d2266bd00c8c;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/t_items.qc b/qcsrc/server/t_items.qc index 46c7342f4e..2e7b9bfbaf 100644 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@ -1,3 +1,238 @@ +#define ISF_LOCATION 2 +#define ISF_MODEL 4 +#define ISF_STATUS 8 + #define ITS_STAYWEP 1 + #define ITS_ANIMATE1 2 + #define ITS_ANIMATE2 4 + #define ITS_AVAILABLE 8 + #define ITS_ALLOWFB 16 + #define ITS_ALLOWSI 32 + #define ITS_POWERUP 64 +#define ISF_COLORMAP 16 +#define ISF_DROP 32 + +.float ItemStatus; + +#ifdef CSQC + +var float autocvar_cl_ghost_items = 1; +var vector autocvar_cl_ghost_items_color = '-1 -1 -1'; +float autocvar_cl_fullbright_items; +vector autocvar_cl_staywep_color; +float autocvar_cl_staywep_alpha; +float autocvar_cl_simple_items; +float cl_simple_items; +float cl_ghost_items_alpha; + +.float spawntime; +.float gravity; +.vector colormod; +void ItemDraw() +{ + if(self.ItemStatus & ITS_ANIMATE1) + { + self.angles += '0 180 0' * frametime; + setorigin(self, '0 0 10' + self.oldorigin + '0 0 8' * sin(time * 2)); + } + + if(self.ItemStatus & ITS_ANIMATE2) + { + self.angles += '0 -90 0' * frametime; + setorigin(self, '0 0 8' + self.oldorigin + '0 0 4' * sin(time * 3)); + } + + if(self.gravity) + Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); +} + +void ItemDrawSimple() +{ + if(self.gravity) + Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); +} + +float csqcitems_started; // remove this after a release or two +void csqcitems_start() +{ + if(autocvar_cl_ghost_items == 1) + cl_ghost_items_alpha = 0.55; + else + cl_ghost_items_alpha = bound(0, autocvar_cl_ghost_items, 1); + + csqcitems_started = TRUE; +} + +void ItemRead(float _IsNew) +{ + if(!csqcitems_started) + csqcitems_start(); + + float sf = ReadByte(); + + if(sf & ISF_LOCATION) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + self.oldorigin = self.origin; + } + + if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc. + { + self.ItemStatus = ReadByte(); + + if(self.ItemStatus & ITS_AVAILABLE) + { + self.alpha = 1; + self.colormod = self.glowmod = '1 1 1'; + } + else + { + if (cl_ghost_items_alpha) + { + self.alpha = autocvar_cl_ghost_items; + self.colormod = self.glowmod = autocvar_cl_ghost_items_color; + } + else + self.alpha = -1; + } + + if(autocvar_cl_fullbright_items) + if(self.ItemStatus & ITS_ALLOWFB) + self.effects |= EF_FULLBRIGHT; + + if(self.ItemStatus & ITS_STAYWEP) + { + self.colormod = self.glowmod = autocvar_cl_staywep_color; + self.alpha = autocvar_cl_staywep_alpha; + + } + + if(self.ItemStatus & ITS_POWERUP) + { + if(self.ItemStatus & ITS_AVAILABLE) + self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); + else + self.effects &~= (EF_ADDITIVE | EF_FULLBRIGHT); + } + } + + if(sf & ISF_MODEL) + { + self.drawmask = MASK_NORMAL; + self.movetype = MOVETYPE_NOCLIP; + self.draw = ItemDraw; + + if(self.mdl) + strunzone(self.mdl); + + self.mdl = ""; + string _fn = ReadString(); + + if(cl_simple_items && (self.ItemStatus & ITS_ALLOWSI)) + { + string _fn2 = substring(_fn, 0 , strlen(_fn) -4); + self.draw = ItemDrawSimple; + + if(fexists(strcat(_fn2, "_simple.md3"))) + self.mdl = strzone(strcat(_fn2, "_simple.md3")); + else if(fexists(strcat(_fn2, "_simple.dpm"))) + self.mdl = strzone(strcat(_fn2, "_simple.dpm")); + else if(fexists(strcat(_fn2, "_simple.iqm"))) + self.mdl = strzone(strcat(_fn2, "_simple.iqm")); + else if(fexists(strcat(_fn2, "_simple.obj"))) + self.mdl = strzone(strcat(_fn2, "_simple.obj")); + else + { + self.draw = ItemDraw; + dprint("Simple item requested for ", _fn, " but no model exsist for it\n"); + } + } + + if(self.draw != ItemDrawSimple) + self.mdl = strzone(_fn); + + + if(self.mdl == "") + dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, " tell tZork aboute this!\n"); + + precache_model(self.mdl); + setmodel(self, self.mdl); + } + + if(sf & ISF_COLORMAP) + self.colormap = ReadShort(); + + if(sf & ISF_DROP) + { + self.gravity = 1; + self.move_movetype = MOVETYPE_TOSS; + self.move_velocity_x = ReadCoord(); + self.move_velocity_y = ReadCoord(); + self.move_velocity_z = ReadCoord(); + self.velocity = self.move_velocity; + self.move_origin = self.oldorigin; + + if(!self.move_time) + { + self.move_time = time; + self.spawntime = time; + } + else + self.move_time = max(self.move_time, time); + } +} +#endif + +#ifdef SVQC +float autocvar_sv_simple_items; +float ItemSend(entity to, float sf) +{ + if(self.gravity) + sf |= ISF_DROP; + else + sf &~= ISF_DROP; + + WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM); + WriteByte(MSG_ENTITY, sf); + + + //WriteByte(MSG_ENTITY, self.cnt); + if(sf & ISF_LOCATION) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + } + + if(sf & ISF_STATUS) + WriteByte(MSG_ENTITY, self.ItemStatus); + + if(sf & ISF_MODEL) + { + + if(self.mdl == "") + dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, "exspect a crash just aboute now\n"); + + WriteString(MSG_ENTITY, self.mdl); + } + + + if(sf & ISF_COLORMAP) + WriteShort(MSG_ENTITY, self.colormap); + + if(sf & ISF_DROP) + { + WriteCoord(MSG_ENTITY, self.velocity_x); + WriteCoord(MSG_ENTITY, self.velocity_y); + WriteCoord(MSG_ENTITY, self.velocity_z); + } + + return TRUE; +} + + float have_pickup_item(void) { // minstagib: only allow filtered items @@ -29,7 +264,7 @@ float have_pickup_item(void) if(g_ca) return FALSE; if(g_weaponarena) - if((self.weapons & WEPBIT_ALL) || (self.items & IT_AMMO)) + if(!WEPSET_EMPTY_E(self) || (self.items & IT_AMMO)) return FALSE; } return TRUE; @@ -78,12 +313,12 @@ string Item_CounterFieldName(float it) .float max_armorvalue; .float pickup_anyway; - +/* float Item_Customize() { if(self.spawnshieldtime) return TRUE; - if(self.weapons != (self.weapons & other.weapons)) + if(!WEPSET_CONTAINS_ALL_EE(other, self)) { self.colormod = '0 0 0'; self.glowmod = self.colormod; @@ -103,80 +338,63 @@ float Item_Customize() return FALSE; } } +*/ void Item_Show (entity e, float mode) -{ +{ e.effects &~= EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST; + e.ItemStatus &~= ITS_STAYWEP; if (mode > 0) { // make the item look normal, and be touchable e.model = e.mdl; e.solid = SOLID_TRIGGER; - e.colormod = '0 0 0'; - self.glowmod = self.colormod; - e.alpha = 0; - e.customizeentityforclient = func_null; - e.spawnshieldtime = 1; + e.ItemStatus |= ITS_AVAILABLE; } else if (mode < 0) { // hide the item completely e.model = string_null; e.solid = SOLID_NOT; - e.colormod = '0 0 0'; - self.glowmod = self.colormod; - e.alpha = 0; - e.customizeentityforclient = func_null; - e.spawnshieldtime = 1; + e.ItemStatus &~= ITS_AVAILABLE; } - else if((e.flags & FL_WEAPON) && g_weapon_stay) + else if((e.flags & FL_WEAPON) && !(e.flags & FL_NO_WEAPON_STAY) && g_weapon_stay) { // make the item translucent and not touchable e.model = e.mdl; e.solid = SOLID_TRIGGER; // can STILL be picked up! - e.colormod = '0 0 0'; - self.glowmod = self.colormod; e.effects |= EF_STARDUST; - e.customizeentityforclient = Item_Customize; - e.spawnshieldtime = 0; // field indicates whether picking it up may give you anything other than the weapon - } - else if(g_ghost_items) - { - // make the item translucent and not touchable - e.model = e.mdl; - e.solid = SOLID_NOT; - e.colormod = stov(autocvar_g_ghost_items_color); - e.glowmod = e.colormod; - e.alpha = g_ghost_items; - e.customizeentityforclient = func_null; - - e.spawnshieldtime = 1; + e.ItemStatus |= (ITS_AVAILABLE | ITS_STAYWEP); } else { - // hide the item completely - e.model = string_null; + //setmodel(e, "null"); e.solid = SOLID_NOT; e.colormod = '0 0 0'; e.glowmod = e.colormod; - e.alpha = 0; - e.customizeentityforclient = func_null; - e.spawnshieldtime = 1; + e.ItemStatus &~= ITS_AVAILABLE; } - - if (e.strength_finished || e.invincible_finished) - e.effects |= EF_ADDITIVE | EF_FULLBRIGHT; + + if (e.items & IT_STRENGTH || e.items & IT_INVINCIBLE) + e.ItemStatus |= ITS_POWERUP; + if (autocvar_g_nodepthtestitems) e.effects |= EF_NODEPTHTEST; - if (autocvar_g_fullbrightitems) - e.effects |= EF_FULLBRIGHT; + + + if (autocvar_g_fullbrightitems) + e.ItemStatus |= ITS_ALLOWFB; + + if (autocvar_sv_simple_items) + e.ItemStatus |= ITS_ALLOWSI; // relink entity (because solid may have changed) setorigin(e, e.origin); + e.SendFlags |= ISF_STATUS; } void Item_Respawn (void) @@ -209,7 +427,7 @@ void Item_RespawnCountdown (void) if(self.count == 1) { string name; - vector rgb; + vector rgb = '1 0 1'; name = string_null; if(g_minstagib) { @@ -233,6 +451,15 @@ void Item_RespawnCountdown (void) case IT_FUEL_REGEN: name = "item-fuelregen"; rgb = '1 0.5 0'; break; case IT_JETPACK: name = "item-jetpack"; rgb = '0.5 0.5 0.5'; break; } + if(self.flags & FL_WEAPON) + { + entity wi = get_weaponinfo(self.weapon); + if(wi) + { + name = wi.model2; + rgb = '1 0 0'; + } + } if(!name) { print("Unknown powerup-marked item is wanting to respawn\n"); @@ -256,7 +483,7 @@ void Item_RespawnCountdown (void) void Item_ScheduleRespawnIn(entity e, float t) { - if(e.flags & FL_POWERUP) + if((e.flags & FL_POWERUP) || WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) { e.think = Item_RespawnCountdown; e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS); @@ -339,7 +566,6 @@ float Item_GiveTo(entity item, entity player) float pickedup; float it; float i; - entity e; // if nothing happens to player, just return without taking the item pickedup = FALSE; @@ -368,10 +594,8 @@ float Item_GiveTo(entity item, entity player) // else if(item.items == IT_CELLS) // AnnounceTo(player, "ammo"); - if (item.weapons & WEPBIT_MINSTANEX) + if (WEPSET_CONTAINS_EW(item, WEP_MINSTANEX)) W_GiveWeapon (player, WEP_MINSTANEX, item.netname); - if (item.ammo_cells) - player.ammo_cells = bound(player.ammo_cells, 999, player.ammo_cells + autocvar_g_minstagib_ammo_drop); player.health = 100; } @@ -419,7 +643,7 @@ float Item_GiveTo(entity item, entity player) if (player.switchweapon == w_getbestweapon(player)) _switchweapon = TRUE; - if not(player.weapons & W_WeaponBit(player.switchweapon)) + if not(WEPSET_CONTAINS_EW(player, player.switchweapon)) _switchweapon = TRUE; pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL); @@ -431,14 +655,17 @@ float Item_GiveTo(entity item, entity player) pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR); if (item.flags & FL_WEAPON) - if ((it = item.weapons - (item.weapons & player.weapons)) || (item.spawnshieldtime && g_pickup_weapons_anyway)) { - pickedup = TRUE; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) + WEPSET_DECLARE_A(it); + WEPSET_COPY_AE(it, item); + WEPSET_ANDNOT_AE(it, player); + + if (!WEPSET_EMPTY_A(it) || (item.spawnshieldtime && self.pickup_anyway)) { - e = get_weaponinfo(i); - if(it & e.weapons) - W_GiveWeapon (player, e.weapon, item.netname); + pickedup = TRUE; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(WEPSET_CONTAINS_AW(it, i)) + W_GiveWeapon (player, i, item.netname); } } @@ -452,12 +679,17 @@ float Item_GiveTo(entity item, entity player) if (item.strength_finished) { pickedup = TRUE; - player.strength_finished = max(player.strength_finished, time) + autocvar_g_balance_powerup_strength_time; + player.strength_finished = max(player.strength_finished, time) + item.strength_finished; } if (item.invincible_finished) { pickedup = TRUE; - player.invincible_finished = max(player.invincible_finished, time) + autocvar_g_balance_powerup_invincible_time; + player.invincible_finished = max(player.invincible_finished, time) + item.invincible_finished; + } + if (item.superweapons_finished) + { + pickedup = TRUE; + player.superweapons_finished = max(player.superweapons_finished, time) + item.superweapons_finished; } } @@ -469,7 +701,6 @@ float Item_GiveTo(entity item, entity player) if (!pickedup) return 0; - sound (player, CH_TRIGGER, item.item_pickupsound, VOL_BASE, ATTN_NORM); if (_switchweapon) if (player.switchweapon != w_getbestweapon(player)) W_SwitchWeapon_Force(player, w_getbestweapon(player)); @@ -482,11 +713,15 @@ void Item_Touch (void) entity e, head; // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky) - if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + if(self.classname == "droppedweapon") { - remove(self); - return; + if (ITEM_TOUCH_NEEDKILL()) + { + remove(self); + return; + } } + if (other.classname != "player") return; if (other.deadflag) @@ -496,12 +731,29 @@ void Item_Touch (void) if (self.owner == other) return; + if (self.classname == "droppedweapon") + { + self.strength_finished = max(0, self.strength_finished - time); + self.invincible_finished = max(0, self.invincible_finished - time); + self.superweapons_finished = max(0, self.superweapons_finished - time); + } + if(!Item_GiveTo(self, other)) + { + if (self.classname == "droppedweapon") + { + // undo what we did above + self.strength_finished += time; + self.invincible_finished += time; + self.superweapons_finished += time; + } return; + } other.last_pickup = time; pointparticles(particleeffectnum("item_pickup"), self.origin, '0 0 0', 1); + sound (other, CH_TRIGGER, self.item_pickupsound, VOL_BASE, ATTN_NORM); if (self.classname == "droppedweapon") remove (self); @@ -521,6 +773,7 @@ void Item_Touch (void) } } e = RandomSelection_chosen_ent; + } else e = self; @@ -528,6 +781,24 @@ void Item_Touch (void) } } +void Item_Reset() +{ + Item_Show(self, !self.state); + setorigin (self, self.origin); + + if(self.classname != "droppedweapon") + { + self.think = SUB_Null; + self.nextthink = 0; + + if(self.waypointsprite_attached) + WaypointSprite_Kill(self.waypointsprite_attached); + + if((self.flags & FL_POWERUP) | WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) // do not spawn powerups initially! + Item_ScheduleInitialRespawn(self); + } +} + void Item_FindTeam() { entity head, e; @@ -554,22 +825,10 @@ void Item_FindTeam() head.effects &~= EF_NODRAW; } - if(e.flags & FL_POWERUP) // do not spawn powerups initially! - Item_ScheduleInitialRespawn(e); + Item_Reset(); } } -void Item_Reset() -{ - Item_Show(self, !self.state); - setorigin (self, self.origin); - self.think = SUB_Null; - self.nextthink = 0; - - if(self.flags & FL_POWERUP) // do not spawn powerups initially! - Item_ScheduleInitialRespawn(self); -} - // Savage: used for item garbage-collection // TODO: perhaps nice special effect? void RemoveItem(void) @@ -584,10 +843,10 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup float weapon_pickupevalfunc(entity player, entity item) { - float c, i, j, position; + float c, j, position; // See if I have it already - if(player.weapons & item.weapons == item.weapons) + if(!WEPSET_CONTAINS_ALL_EE(player, item)) { // If I can pick it up if(!item.spawnshieldtime) @@ -606,34 +865,27 @@ float weapon_pickupevalfunc(entity player, entity item) // If custom weapon priorities for bots is enabled rate most wanted weapons higher if( bot_custom_weapon && c ) { - for(i = WEP_FIRST; i <= WEP_LAST ; ++i) - { - // Find weapon - if( (get_weaponinfo(i)).weapons & item.weapons != item.weapons ) - continue; - - // Find the highest position on any range - position = -1; - for(j = 0; j < WEP_LAST ; ++j){ - if( - bot_weapons_far[j] == i || - bot_weapons_mid[j] == i || - bot_weapons_close[j] == i - ) - { - position = j; - break; - } - } - - // Rate it - if (position >= 0 ) + // Find the highest position on any range + position = -1; + for(j = 0; j < WEP_LAST ; ++j){ + if( + bot_weapons_far[j] == item.weapon || + bot_weapons_mid[j] == item.weapon || + bot_weapons_close[j] == item.weapon + ) { - position = WEP_LAST - position; - // item.bot_pickupbasevalue is overwritten here - return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c; + position = j; + break; } } + + // Rate it + if (position >= 0 ) + { + position = WEP_LAST - position; + // item.bot_pickupbasevalue is overwritten here + return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c; + } } return item.bot_pickupbasevalue * c; @@ -641,7 +893,8 @@ float weapon_pickupevalfunc(entity player, entity item) float commodity_pickupevalfunc(entity player, entity item) { - float c, i, need_shells, need_nails, need_rockets, need_cells; + float c, i; + float need_shells = FALSE, need_nails = FALSE, need_rockets = FALSE, need_cells = FALSE, need_fuel = FALSE; entity wi; c = 0; @@ -650,7 +903,7 @@ float commodity_pickupevalfunc(entity player, entity item) { wi = get_weaponinfo(i); - if not(wi.weapons & player.weapons) + if not(WEPSET_CONTAINS_EW(player, i)) continue; if(wi.items & IT_SHELLS) @@ -661,6 +914,8 @@ float commodity_pickupevalfunc(entity player, entity item) need_rockets = TRUE; else if(wi.items & IT_CELLS) need_cells = TRUE; + else if(wi.items & IT_FUEL) + need_cells = TRUE; } // TODO: figure out if the player even has the weapon this ammo is for? @@ -682,6 +937,10 @@ float commodity_pickupevalfunc(entity player, entity item) if (item.ammo_cells) if (player.ammo_cells < g_pickup_cells_max) c = c + max(0, 1 - player.ammo_cells / g_pickup_cells_max); + if (need_fuel) + if (item.ammo_fuel) + if (player.ammo_fuel < g_pickup_fuel_max) + c = c + max(0, 1 - player.ammo_fuel / g_pickup_fuel_max); if (item.armorvalue) if (player.armorvalue < item.max_armorvalue) c = c + max(0, 1 - player.armorvalue / item.max_armorvalue); @@ -692,25 +951,81 @@ float commodity_pickupevalfunc(entity player, entity item) return item.bot_pickupbasevalue * c; } +void Item_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + RemoveItem(); +} .float is_item; void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue) { startitem_failed = FALSE; + if(self.model == "") + self.model = itemmodel; + + if(self.model == "") + { + error(strcat("^1Tried to spawn ", itemname, " with no model!\n")); + return; + } + + if(self.item_pickupsound == "") + self.item_pickupsound = pickupsound; + + if(!self.respawntime) // both need to be set + { + self.respawntime = defaultrespawntime; + self.respawntimejitter = defaultrespawntimejitter; + } + self.items = itemid; - self.weapons = weaponid; + self.weapon = weaponid; + + if(weaponid) + WEPSET_COPY_EW(self, weaponid); + self.flags = FL_ITEM | itemflags; + if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item + { + startitem_failed = TRUE; + remove(self); + return; + } + // is it a dropped weapon? if (self.classname == "droppedweapon") { self.reset = SUB_Remove; // it's a dropped weapon self.movetype = MOVETYPE_TOSS; + // Savage: remove thrown items after a certain period of time ("garbage collection") self.think = RemoveItem; - self.nextthink = time + 60; + self.nextthink = time + 20; + + self.takedamage = DAMAGE_YES; + self.event_damage = Item_Damage; + + if(self.strength_finished || self.invincible_finished || self.superweapons_finished) + /* + if(self.items == 0) + if(WEPSET_CONTAINS_ALL_AE(WEPBIT_SUPERWEAPONS, self)) // only superweapons + if(self.ammo_nails == 0) + if(self.ammo_cells == 0) + if(self.ammo_rockets == 0) + if(self.ammo_shells == 0) + if(self.ammo_fuel == 0) + if(self.health == 0) + if(self.armorvalue == 0) + */ + { + // if item is worthless after a timer, have it expire then + self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished); + } + // don't drop if in a NODROP zone (such as lava) traceline(self.origin, self.origin, MOVE_NORMAL, self); if (trace_dpstartcontents & DPCONTENTS_NODROP) @@ -722,13 +1037,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, } else { - if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item - { - startitem_failed = TRUE; - remove(self); - return; - } - if(!have_pickup_item()) { startitem_failed = TRUE; @@ -736,11 +1044,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, return; } - if(self.model != "") - itemmodel = self.model; - if(self.item_pickupsound != "") - pickupsound = self.item_pickupsound; - self.reset = Item_Reset; // it's a level item if(self.spawnflags & 1) @@ -783,6 +1086,7 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, entity otheritem; for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain) { + // why not flags & fl_item? if(otheritem.is_item) { dprint("XXX Found duplicated item: ", itemname, vtos(self.origin)); @@ -793,10 +1097,10 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.is_item = TRUE; } - weaponsInMap |= weaponid; + WEPSET_OR_AW(weaponsInMap, weaponid); - precache_model (itemmodel); - precache_sound (pickupsound); + precache_model (self.model); + precache_sound (self.item_pickupsound); precache_sound ("misc/itemrespawncountdown.wav"); if(!g_minstagib && itemid == IT_STRENGTH) @@ -813,43 +1117,54 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.bot_pickup = TRUE; self.bot_pickupevalfunc = pickupevalfunc; self.bot_pickupbasevalue = pickupbasevalue; - self.mdl = itemmodel; - self.item_pickupsound = pickupsound; - // let mappers override respawntime - if(!self.respawntime) // both set - { - self.respawntime = defaultrespawntime; - self.respawntimejitter = defaultrespawntimejitter; - } + self.mdl = self.model; self.netname = itemname; self.touch = Item_Touch; - setmodel (self, self.mdl); // precision set below - self.effects |= EF_LOWPRECISION; + setmodel(self, "null"); // precision set below + //self.effects |= EF_LOWPRECISION; + if((itemflags & FL_POWERUP) || self.health || self.armorvalue) - setsize (self, '-16 -16 0', '16 16 48'); + { + self.pos1 = '-16 -16 0'; + self.pos2 = '16 16 48'; + } else - setsize (self, '-16 -16 0', '16 16 32'); + { + self.pos1 = '-16 -16 0'; + self.pos2 = '16 16 32'; + } + setsize (self, self.pos1, self.pos2); + + if(itemflags & FL_POWERUP) + self.ItemStatus |= ITS_ANIMATE1; + + if(self.armorvalue || self.health) + self.ItemStatus |= ITS_ANIMATE2; + if(itemflags & FL_WEAPON) - self.modelflags |= MF_ROTATE; - - if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely - if (itemflags & FL_WEAPON) { - // neutral team color for pickup weapons - self.colormap = 1024; // color shirt=0 pants=0 grey + if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely + self.colormap = 1024; // color shirt=0 pants=0 grey + else + self.gravity = 1; + + self.ItemStatus |= ITS_ANIMATE1; + self.ItemStatus |= ISF_COLORMAP; } - Item_Show(self, 1); self.state = 0; - if(self.team) + if(self.team) // broken, no idea why. { if(!self.cnt) self.cnt = 1; // item probability weight - self.effects = self.effects | EF_NODRAW; // marker for item team search + + self.effects |= EF_NODRAW; // marker for item team search InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET); } - else if(self.flags & FL_POWERUP) // do not spawn powerups initially! - Item_ScheduleInitialRespawn(self); + else + Item_Reset(); + + Net_LinkEntity(self, FALSE, 0, ItemSend); } /* replace items in minstagib @@ -857,10 +1172,10 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, * IT_NAILS = extra lives * IT_INVINCIBLE = speed */ -void minstagib_items (float itemid) +void minstagib_items (float itemid) // will be deleted soon. { float rnd; - self.classname = "minstagib"; + self.classname = "minstagib"; // ...? // replace rocket launchers and nex guns with ammo cells if (itemid == IT_CELLS) @@ -884,7 +1199,8 @@ void minstagib_items (float itemid) // replace with invis if (itemid == IT_STRENGTH) { - self.strength_finished = 30; + if(!self.strength_finished) + self.strength_finished = autocvar_g_balance_powerup_strength_time; StartItem ("models/items/g_strength.md3", "misc/powerup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Invisibility", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_MID); @@ -900,7 +1216,8 @@ void minstagib_items (float itemid) // replace with speed if (itemid == IT_INVINCIBLE) { - self.invincible_finished = 30; + if(!self.invincible_finished) + self.invincible_finished = autocvar_g_balance_powerup_invincible_time; StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Speed", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_MID); @@ -924,29 +1241,28 @@ void weapon_defaultspawnfunc(float wpn) string s; entity oldself; float i, j; + float f; - // set the respawntime in advance (so replaced weapons can copy it) - - if(!self.respawntime) + if(self.classname != "droppedweapon" && self.classname != "replacedweapon") { e = get_weaponinfo(wpn); - if(e.items == IT_SUPERWEAPON) - { - self.respawntime = g_pickup_respawntime_powerup; - self.respawntimejitter = g_pickup_respawntimejitter_powerup; - } - else + + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) { - self.respawntime = g_pickup_respawntime_weapon; - self.respawntimejitter = g_pickup_respawntimejitter_weapon; + print("Attempted to spawn a mutator-blocked weapon; these guns will in the future require a mutator\n"); + /* + objerror("Attempted to spawn a mutator-blocked weapon rejected"); + startitem_failed = TRUE; + return; + */ } - } - if(self.classname != "droppedweapon" && self.classname != "replacedweapon") - { - e = get_weaponinfo(wpn); - s = cvar_string(strcat("g_weaponreplace_", e.netname)); - if(s == "0") + s = W_Apply_Weaponreplace(e.netname); + ret_string = s; + other = e; + MUTATOR_CALLHOOK(SetWeaponreplace); + s = ret_string; + if(s == "") { remove(self); startitem_failed = TRUE; @@ -979,7 +1295,7 @@ void weapon_defaultspawnfunc(float wpn) } self = oldself; } - if(t >= 1) + if(t >= 1) // always the case! { s = argv(0); wpn = 0; @@ -1007,7 +1323,25 @@ void weapon_defaultspawnfunc(float wpn) e = get_weaponinfo(wpn); - if(e.items && e.items != IT_SUPERWEAPON) + if(!self.respawntime) + { + if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) + { + self.respawntime = g_pickup_respawntime_superweapon; + self.respawntimejitter = g_pickup_respawntimejitter_superweapon; + } + else + { + self.respawntime = g_pickup_respawntime_weapon; + self.respawntimejitter = g_pickup_respawntimejitter_weapon; + } + } + + if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) + if(!self.superweapons_finished) + self.superweapons_finished = autocvar_g_balance_superweapons_time; + + if(e.items) { for(i = 0, j = 1; i < 24; ++i, j *= 2) { @@ -1019,16 +1353,27 @@ void weapon_defaultspawnfunc(float wpn) } } } - else - { - self.flags |= FL_NO_WEAPON_STAY; - } + + // pickup anyway + if(g_pickup_weapons_anyway) + self.pickup_anyway = TRUE; + + f = FL_WEAPON; + + // no weapon-stay on superweapons + if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) + f |= FL_NO_WEAPON_STAY; // weapon stay isn't supported for teamed weapons if(self.team) - self.flags |= FL_NO_WEAPON_STAY; + f |= FL_NO_WEAPON_STAY; + + // stupid minstagib hack, don't ask + if(g_minstagib) + if(self.ammo_cells) + self.ammo_cells = autocvar_g_minstagib_ammo_drop; - StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapons, FL_WEAPON, weapon_pickupevalfunc, e.bot_pickupbasevalue); + StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue); if (self.modelindex) // don't precache if self was removed weapon_action(e.weapon, WR_PRECACHE); } @@ -1239,7 +1584,8 @@ void spawnfunc_item_strength (void) { minstagib_items(IT_STRENGTH); } else { precache_sound("weapons/strength_fire.wav"); - self.strength_finished = 30; + if(!self.strength_finished) + self.strength_finished = autocvar_g_balance_powerup_strength_time; StartItem ("models/items/g_strength.md3", "misc/powerup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Strength Powerup", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 100000); } } @@ -1248,7 +1594,8 @@ void spawnfunc_item_invincible (void) { if(g_minstagib) { minstagib_items(IT_INVINCIBLE); } else { - self.invincible_finished = 30; + if(!self.invincible_finished) + self.invincible_finished = autocvar_g_balance_powerup_invincible_time; StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Shield", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 100000); } } @@ -1256,7 +1603,7 @@ void spawnfunc_item_invincible (void) { void spawnfunc_item_minst_cells (void) { if (g_minstagib) { - minst_no_auto_cells = 1; + minst_no_auto_cells = TRUE; minstagib_items(IT_CELLS); } else @@ -1301,6 +1648,8 @@ void spawnfunc_target_items (void) self.strength_finished = autocvar_g_balance_powerup_strength_time; if(!self.invincible_finished) self.invincible_finished = autocvar_g_balance_powerup_invincible_time; + if(!self.superweapons_finished) + self.superweapons_finished = autocvar_g_balance_superweapons_time; precache_sound("misc/itempickup.wav"); precache_sound("misc/megahealth.wav"); @@ -1323,22 +1672,25 @@ void spawnfunc_target_items (void) else if(argv(i) == "unlimited_superweapons") self.items |= IT_UNLIMITED_SUPERWEAPONS; else if(argv(i) == "strength") self.items |= IT_STRENGTH; else if(argv(i) == "invincible") self.items |= IT_INVINCIBLE; + else if(argv(i) == "superweapons") self.items |= IT_SUPERWEAPON; else if(argv(i) == "jetpack") self.items |= IT_JETPACK; else if(argv(i) == "fuel_regen") self.items |= IT_FUEL_REGEN; else - for(j = WEP_FIRST; j <= WEP_LAST; ++j) { - e = get_weaponinfo(j); - if(argv(i) == e.netname) + for(j = WEP_FIRST; j <= WEP_LAST; ++j) { - self.weapons |= e.weapons; - if(self.spawnflags == 0 || self.spawnflags == 2) - weapon_action(e.weapon, WR_PRECACHE); - break; + e = get_weaponinfo(j); + if(argv(i) == e.netname) + { + WEPSET_OR_EW(self, j); + if(self.spawnflags == 0 || self.spawnflags == 2) + weapon_action(e.weapon, WR_PRECACHE); + break; + } } + if(j > WEP_LAST) + print("target_items: invalid item ", argv(i), "\n"); } - if(j > WEP_LAST) - print("target_items: invalid item ", argv(i), "\n"); } string itemprefix, valueprefix; @@ -1370,6 +1722,7 @@ void spawnfunc_target_items (void) self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons"); self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.strength_finished * !!(self.items & IT_STRENGTH), "strength"); self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.invincible_finished * !!(self.items & IT_INVINCIBLE), "invincible"); + self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.superweapons_finished * !!(self.items & IT_SUPERWEAPON), "superweapons"); self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_JETPACK), "jetpack"); self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_FUEL_REGEN), "fuel_regen"); if(self.ammo_shells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_shells), "shells"); @@ -1382,8 +1735,8 @@ void spawnfunc_target_items (void) for(j = WEP_FIRST; j <= WEP_LAST; ++j) { e = get_weaponinfo(j); - if(e.weapons) - self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.weapons & e.weapons), e.netname); + if(e.weapon) + self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, WEPSET_CONTAINS_EW(self, j), e.netname); } } self.netname = strzone(self.netname); @@ -1444,6 +1797,36 @@ void spawnfunc_item_jetpack(void) #define OP_PLUS 3 #define OP_MINUS 4 +float GiveWeapon(entity e, float wpn, float op, float val) +{ + float v0, v1; + v0 = WEPSET_CONTAINS_EW(e, wpn); + switch(op) + { + case OP_SET: + if(val > 0) + WEPSET_OR_EW(e, wpn); + else + WEPSET_ANDNOT_EW(e, wpn); + break; + case OP_MIN: + case OP_PLUS: + if(val > 0) + WEPSET_OR_EW(e, wpn); + break; + case OP_MAX: + if(val <= 0) + WEPSET_ANDNOT_EW(e, wpn); + break; + case OP_MINUS: + if(val > 0) + WEPSET_ANDNOT_EW(e, wpn); + break; + } + v1 = WEPSET_CONTAINS_EW(e, wpn); + return (v0 != v1); +} + float GiveBit(entity e, .float fld, float bit, float op, float val) { float v0, v1; @@ -1524,7 +1907,9 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa e.regenfield = max(e.regenfield, time + regentime); } +#define PREGIVE_WEAPONS(e) WEPSET_DECLARE_A(save_weapons); WEPSET_COPY_AE(save_weapons, e) #define PREGIVE(e,f) float save_##f; save_##f = (e).f +#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), WEPSET_CONTAINS_AW(save_weapons, b), WEPSET_CONTAINS_EW(e, 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_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) @@ -1548,11 +1933,13 @@ float GiveItems(entity e, float beginarg, float endarg) e.strength_finished = max(0, e.strength_finished - time); e.invincible_finished = max(0, e.invincible_finished - time); + e.superweapons_finished = max(0, e.superweapons_finished - time); PREGIVE(e, items); - PREGIVE(e, weapons); + PREGIVE_WEAPONS(e); PREGIVE(e, strength_finished); PREGIVE(e, invincible_finished); + PREGIVE(e, superweapons_finished); PREGIVE(e, ammo_nails); PREGIVE(e, ammo_cells); PREGIVE(e, ammo_shells); @@ -1590,8 +1977,9 @@ float GiveItems(entity e, float beginarg, float endarg) continue; case "ALL": got += GiveBit(e, items, IT_FUEL_REGEN, op, val); - got += GiveValue(e, strength_finished, op, time); - got += GiveValue(e, invincible_finished, op, time); + got += GiveValue(e, strength_finished, op, val); + got += GiveValue(e, invincible_finished, op, val); + got += GiveValue(e, superweapons_finished, op, val); got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); case "all": got += GiveBit(e, items, IT_JETPACK, op, val); @@ -1601,8 +1989,9 @@ float GiveItems(entity e, float beginarg, float endarg) for(j = WEP_FIRST; j <= WEP_LAST; ++j) { wi = get_weaponinfo(j); - if(wi.weapons) - got += GiveBit(e, weapons, wi.weapons, op, val); + if(wi.weapon) + if not(wi.spawnflags & WEP_FLAG_MUTATORBLOCKED) + got += GiveWeapon(e, j, op, val); } case "allammo": got += GiveValue(e, ammo_cells, op, val); @@ -1632,6 +2021,9 @@ float GiveItems(entity e, float beginarg, float endarg) case "invincible": got += GiveValue(e, invincible_finished, op, val); break; + case "superweapons": + got += GiveValue(e, superweapons_finished, op, val); + break; case "cells": got += GiveValue(e, ammo_cells, op, val); break; @@ -1660,7 +2052,7 @@ float GiveItems(entity e, float beginarg, float endarg) wi = get_weaponinfo(j); if(cmd == wi.netname) { - got += GiveBit(e, weapons, wi.weapons, op, val); + got += GiveWeapon(e, j, op, val); break; } } @@ -1679,11 +2071,11 @@ float GiveItems(entity e, float beginarg, float endarg) for(j = WEP_FIRST; j <= WEP_LAST; ++j) { wi = get_weaponinfo(j); - if(wi.weapons) + if(wi.weapon) { - POSTGIVE_BIT(e, weapons, wi.weapons, "weapons/weaponpickup.wav", string_null); - if not(save_weapons & wi.weapons) - if(e.weapons & wi.weapons) + POSTGIVE_WEAPON(e, j, "weapons/weaponpickup.wav", string_null); + if not(WEPSET_CONTAINS_AW(save_weapons, j)) + if(WEPSET_CONTAINS_EW(e, j)) weapon_action(wi.weapon, WR_PRECACHE); } } @@ -1697,6 +2089,10 @@ float GiveItems(entity e, float beginarg, float endarg) POSTGIVE_VALUE_ROT(e, armorvalue, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/armor25.wav", string_null); POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/megahealth.wav", string_null); + if(e.superweapons_finished <= 0) + if(WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) + e.superweapons_finished = autocvar_g_balance_superweapons_time; + if (g_minstagib) { e.health = bound(0, e.health, 100); @@ -1711,11 +2107,16 @@ float GiveItems(entity e, float beginarg, float endarg) e.invincible_finished = 0; else e.invincible_finished += time; + if(e.superweapons_finished <= 0) + e.superweapons_finished = 0; + else + e.superweapons_finished += time; - if not(e.weapons & W_WeaponBit(e.switchweapon)) + if not(WEPSET_CONTAINS_EW(e, e.switchweapon)) _switchweapon = TRUE; if(_switchweapon) W_SwitchWeapon_Force(e, w_getbestweapon(e)); return got; } +#endif