#ifdef IMPLEMENTATION int autocvar_g_physical_items; float autocvar_g_physical_items_damageforcescale; float autocvar_g_physical_items_reset; REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) { // check if we have a physics engine MUTATOR_ONADD { if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) { LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n"); return -1; } } MUTATOR_ONROLLBACK_OR_REMOVE { // nothing to roll back } MUTATOR_ONREMOVE { LOG_INFO("This cannot be removed at runtime\n"); return -1; } return 0; } .vector spawn_origin, spawn_angles; void physical_item_think() {SELFPARAM(); self.nextthink = time; self.alpha = self.owner.alpha; // apply fading and ghosting if(!self.cnt) // map item, not dropped { // copy ghost item properties self.colormap = self.owner.colormap; self.colormod = self.owner.colormod; self.glowmod = self.owner.glowmod; // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there if(autocvar_g_physical_items_reset) { if(self.owner.wait > time) // awaiting respawn { setorigin(self, self.spawn_origin); self.angles = self.spawn_angles; self.solid = SOLID_NOT; self.alpha = -1; self.movetype = MOVETYPE_NONE; } else { self.alpha = 1; self.solid = SOLID_CORPSE; self.movetype = MOVETYPE_PHYSICS; } } } if(!self.owner.modelindex) remove(self); // the real item is gone, remove this } void physical_item_touch() {SELFPARAM(); if(!self.cnt) // not for dropped items if (ITEM_TOUCH_NEEDKILL()) { setorigin(self, self.spawn_origin); self.angles = self.spawn_angles; } } void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) { if(!this.cnt) // not for dropped items if(ITEM_DAMAGE_NEEDKILL(deathtype)) { setorigin(this, this.spawn_origin); this.angles = this.spawn_angles; } } MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) {SELFPARAM(); if(self.owner == world && autocvar_g_physical_items <= 1) return false; if (self.spawnflags & 1) // floating item return false; // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. entity wep; wep = spawn(); _setmodel(wep, self.model); setsize(wep, self.mins, self.maxs); setorigin(wep, self.origin); wep.angles = self.angles; wep.velocity = self.velocity; wep.owner = self; wep.solid = SOLID_CORPSE; wep.movetype = MOVETYPE_PHYSICS; wep.takedamage = DAMAGE_AIM; wep.effects |= EF_NOMODELFLAGS; // disable the spinning wep.colormap = self.owner.colormap; wep.glowmod = self.owner.glowmod; wep.damageforcescale = autocvar_g_physical_items_damageforcescale; wep.dphitcontentsmask = self.dphitcontentsmask; wep.cnt = (self.owner != world); setthink(wep, physical_item_think); wep.nextthink = time; settouch(wep, physical_item_touch); wep.event_damage = physical_item_damage; if(!wep.cnt) { // fix the spawn origin setorigin(wep, wep.origin + '0 0 1'); entity oldself; oldself = self; WITHSELF(wep, builtin_droptofloor()); } wep.spawn_origin = wep.origin; wep.spawn_angles = self.angles; self.effects |= EF_NODRAW; // hide the original weapon self.movetype = MOVETYPE_FOLLOW; self.aiment = wep; // attach the original weapon self.SendEntity3 = func_null; return false; } #endif