c9ab81b4e42ebc8a85a3c11389261cc12e4641e7
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / physical_items / physical_items.qc
1 #ifdef IMPLEMENTATION
2 int autocvar_g_physical_items;
3 float autocvar_g_physical_items_damageforcescale;
4 float autocvar_g_physical_items_reset;
5
6 REGISTER_MUTATOR(physical_items, cvar("g_physical_items"))
7 {
8         // check if we have a physics engine
9         MUTATOR_ONADD
10         {
11                 if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")))
12                 {
13                         LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n");
14                         return -1;
15                 }
16         }
17
18         MUTATOR_ONROLLBACK_OR_REMOVE
19         {
20                 // nothing to roll back
21         }
22
23         MUTATOR_ONREMOVE
24         {
25                 LOG_INFO("This cannot be removed at runtime\n");
26                 return -1;
27         }
28
29         return 0;
30 }
31
32 .vector spawn_origin, spawn_angles;
33
34 void physical_item_think()
35 {SELFPARAM();
36         self.nextthink = time;
37
38         self.alpha = self.owner.alpha; // apply fading and ghosting
39
40         if(!self.cnt) // map item, not dropped
41         {
42                 // copy ghost item properties
43                 self.colormap = self.owner.colormap;
44                 self.colormod = self.owner.colormod;
45                 self.glowmod = self.owner.glowmod;
46
47                 // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there
48                 if(autocvar_g_physical_items_reset)
49                 {
50                         if(self.owner.wait > time) // awaiting respawn
51                         {
52                                 setorigin(self, self.spawn_origin);
53                                 self.angles = self.spawn_angles;
54                                 self.solid = SOLID_NOT;
55                                 self.alpha = -1;
56                                 self.movetype = MOVETYPE_NONE;
57                         }
58                         else
59                         {
60                                 self.alpha = 1;
61                                 self.solid = SOLID_CORPSE;
62                                 self.movetype = MOVETYPE_PHYSICS;
63                         }
64                 }
65         }
66
67         if(!self.owner.modelindex)
68                 remove(self); // the real item is gone, remove this
69 }
70
71 void physical_item_touch()
72 {SELFPARAM();
73         if(!self.cnt) // not for dropped items
74         if (ITEM_TOUCH_NEEDKILL())
75         {
76                 setorigin(self, self.spawn_origin);
77                 self.angles = self.spawn_angles;
78         }
79 }
80
81 void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
82 {
83         if(!this.cnt) // not for dropped items
84         if(ITEM_DAMAGE_NEEDKILL(deathtype))
85         {
86                 setorigin(this, this.spawn_origin);
87                 this.angles = this.spawn_angles;
88         }
89 }
90
91 MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn)
92 {SELFPARAM();
93         if(self.owner == world && autocvar_g_physical_items <= 1)
94                 return false;
95         if (self.spawnflags & 1) // floating item
96                 return false;
97
98         // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics.
99         // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed.
100         entity wep;
101         wep = spawn();
102         _setmodel(wep, self.model);
103         setsize(wep, self.mins, self.maxs);
104         setorigin(wep, self.origin);
105         wep.angles = self.angles;
106         wep.velocity = self.velocity;
107
108         wep.owner = self;
109         wep.solid = SOLID_CORPSE;
110         wep.movetype = MOVETYPE_PHYSICS;
111         wep.takedamage = DAMAGE_AIM;
112         wep.effects |= EF_NOMODELFLAGS; // disable the spinning
113         wep.colormap = self.owner.colormap;
114         wep.glowmod = self.owner.glowmod;
115         wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
116         wep.dphitcontentsmask = self.dphitcontentsmask;
117         wep.cnt = (self.owner != world);
118
119         setthink(wep, physical_item_think);
120         wep.nextthink = time;
121         settouch(wep, physical_item_touch);
122         wep.event_damage = physical_item_damage;
123
124         if(!wep.cnt)
125         {
126                 // fix the spawn origin
127                 setorigin(wep, wep.origin + '0 0 1');
128                 entity oldself;
129                 oldself = self;
130                 WITHSELF(wep, builtin_droptofloor());
131         }
132
133         wep.spawn_origin = wep.origin;
134         wep.spawn_angles = self.angles;
135
136         self.effects |= EF_NODRAW; // hide the original weapon
137         self.movetype = MOVETYPE_FOLLOW;
138         self.aiment = wep; // attach the original weapon
139         self.SendEntity3 = func_null;
140
141         return false;
142 }
143 #endif