Cleanse the touch functions of the other evil
[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(entity this)
35 {
36         this.nextthink = time;
37
38         this.alpha = this.owner.alpha; // apply fading and ghosting
39
40         if(!this.cnt) // map item, not dropped
41         {
42                 // copy ghost item properties
43                 this.colormap = this.owner.colormap;
44                 this.colormod = this.owner.colormod;
45                 this.glowmod = this.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(this.owner.wait > time) // awaiting respawn
51                         {
52                                 setorigin(this, this.spawn_origin);
53                                 this.angles = this.spawn_angles;
54                                 this.solid = SOLID_NOT;
55                                 this.alpha = -1;
56                                 this.movetype = MOVETYPE_NONE;
57                         }
58                         else
59                         {
60                                 this.alpha = 1;
61                                 this.solid = SOLID_CORPSE;
62                                 this.movetype = MOVETYPE_PHYSICS;
63                         }
64                 }
65         }
66
67         if(!this.owner.modelindex)
68                 remove(this); // the real item is gone, remove this
69 }
70
71 void physical_item_touch(entity this, entity toucher)
72 {
73         if(!this.cnt) // not for dropped items
74         if (ITEM_TOUCH_NEEDKILL())
75         {
76                 setorigin(this, this.spawn_origin);
77                 this.angles = this.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 {
93         entity item = M_ARGV(0, entity);
94
95         if(item.owner == NULL && autocvar_g_physical_items <= 1)
96                 return;
97         if (item.spawnflags & 1) // floating item
98                 return;
99
100         // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics.
101         // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed.
102         entity wep;
103         wep = spawn();
104         _setmodel(wep, item.model);
105         setsize(wep, item.mins, item.maxs);
106         setorigin(wep, item.origin);
107         wep.angles = item.angles;
108         wep.velocity = item.velocity;
109
110         wep.owner = item;
111         wep.solid = SOLID_CORPSE;
112         wep.movetype = MOVETYPE_PHYSICS;
113         wep.takedamage = DAMAGE_AIM;
114         wep.effects |= EF_NOMODELFLAGS; // disable the spinning
115         wep.colormap = item.owner.colormap;
116         wep.glowmod = item.owner.glowmod;
117         wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
118         wep.dphitcontentsmask = item.dphitcontentsmask;
119         wep.cnt = (item.owner != NULL);
120
121         setthink(wep, physical_item_think);
122         wep.nextthink = time;
123         settouch(wep, physical_item_touch);
124         wep.event_damage = physical_item_damage;
125
126         if(!wep.cnt)
127         {
128                 // fix the spawn origin
129                 setorigin(wep, wep.origin + '0 0 1');
130                 droptofloor(wep);
131         }
132
133         wep.spawn_origin = wep.origin;
134         wep.spawn_angles = item.angles;
135
136         item.effects |= EF_NODRAW; // hide the original weapon
137         item.movetype = MOVETYPE_FOLLOW;
138         item.aiment = wep; // attach the original weapon
139         setSendEntity(item, func_null);
140 }
141 #endif