Cleanup
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / trigger / impulse.qc
1 // targeted (directional) mode
2 void trigger_impulse_touch1()
3 {SELFPARAM();
4         entity targ;
5         float pushdeltatime;
6         float str;
7
8         if (self.active != ACTIVE_ACTIVE)
9                 return;
10
11         if (!isPushable(other))
12                 return;
13
14         EXACTTRIGGER_TOUCH;
15
16         targ = find(world, targetname, self.target);
17         if(!targ)
18         {
19                 objerror("trigger_force without a (valid) .target!\n");
20                 remove(self);
21                 return;
22         }
23
24         str = min(self.radius, vlen(self.origin - other.origin));
25
26         if(self.falloff == 1)
27                 str = (str / self.radius) * self.strength;
28         else if(self.falloff == 2)
29                 str = (1 - (str / self.radius)) * self.strength;
30         else
31                 str = self.strength;
32
33         pushdeltatime = time - other.lastpushtime;
34         if (pushdeltatime > 0.15) pushdeltatime = 0;
35         other.lastpushtime = time;
36         if(!pushdeltatime) return;
37
38         if(self.spawnflags & 64)
39         {
40                 float addspeed = str - other.velocity * normalize(targ.origin - self.origin);
41                 if (addspeed > 0)
42                 {
43                         float accelspeed = min(8 * pushdeltatime * str, addspeed);
44                         other.velocity += accelspeed * normalize(targ.origin - self.origin);
45                 }
46         }
47         else
48                 other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
49         other.flags &= ~FL_ONGROUND;
50 #ifdef SVQC
51         UpdateCSQCProjectile(other);
52 #endif
53 }
54
55 // Directionless (accelerator/decelerator) mode
56 void trigger_impulse_touch2()
57 {SELFPARAM();
58         float pushdeltatime;
59
60         if (self.active != ACTIVE_ACTIVE)
61                 return;
62
63         if (!isPushable(other))
64                 return;
65
66         EXACTTRIGGER_TOUCH;
67
68         pushdeltatime = time - other.lastpushtime;
69         if (pushdeltatime > 0.15) pushdeltatime = 0;
70         other.lastpushtime = time;
71         if(!pushdeltatime) return;
72
73         // div0: ticrate independent, 1 = identity (not 20)
74         other.velocity = other.velocity * pow(self.strength, pushdeltatime);
75 #ifdef SVQC
76         UpdateCSQCProjectile(other);
77 #endif
78 }
79
80 // Spherical (gravity/repulsor) mode
81 void trigger_impulse_touch3()
82 {SELFPARAM();
83         float pushdeltatime;
84         float str;
85
86         if (self.active != ACTIVE_ACTIVE)
87                 return;
88
89         if (!isPushable(other))
90                 return;
91
92         EXACTTRIGGER_TOUCH;
93
94         pushdeltatime = time - other.lastpushtime;
95         if (pushdeltatime > 0.15) pushdeltatime = 0;
96         other.lastpushtime = time;
97         if(!pushdeltatime) return;
98
99         setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
100
101         str = min(self.radius, vlen(self.origin - other.origin));
102
103         if(self.falloff == 1)
104                 str = (1 - str / self.radius) * self.strength; // 1 in the inside
105         else if(self.falloff == 2)
106                 str = (str / self.radius) * self.strength; // 0 in the inside
107         else
108                 str = self.strength;
109
110         other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
111 #ifdef SVQC
112         UpdateCSQCProjectile(other);
113 #endif
114 }
115
116 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
117 -------- KEYS --------
118 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
119                  If not, this trigger acts like a damper/accelerator field.
120
121 strength : This is how mutch force to add in the direction of .target each second
122                    when .target is set. If not, this is hoe mutch to slow down/accelerate
123                    someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
124
125 radius   : If set, act as a spherical device rather then a liniar one.
126
127 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
128
129 -------- NOTES --------
130 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
131 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
132 */
133 #ifdef SVQC
134 bool trigger_impulse_send(entity to, int sf)
135 {SELFPARAM();
136         WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE);
137
138         WriteCoord(MSG_ENTITY, self.radius);
139         WriteCoord(MSG_ENTITY, self.strength);
140         WriteByte(MSG_ENTITY, self.falloff);
141         WriteByte(MSG_ENTITY, self.active);
142
143         trigger_common_write(true);
144
145         return true;
146 }
147
148 void trigger_impulse_link()
149 {
150         //Net_LinkEntity(self, 0, false, trigger_impulse_send);
151 }
152
153 spawnfunc(trigger_impulse)
154 {
155         self.active = ACTIVE_ACTIVE;
156
157         EXACTTRIGGER_INIT;
158         if(self.radius)
159         {
160                 if(!self.strength) self.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier;
161                 setorigin(self, self.origin);
162                 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
163                 self.touch = trigger_impulse_touch3;
164         }
165         else
166         {
167                 if(self.target)
168                 {
169                         if(!self.strength) self.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier;
170                         self.touch = trigger_impulse_touch1;
171                 }
172                 else
173                 {
174                         if(!self.strength) self.strength = 0.9;
175                         self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
176                         self.touch = trigger_impulse_touch2;
177                 }
178         }
179
180         trigger_impulse_link();
181 }
182 #elif defined(CSQC)
183 NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew)
184 {
185         self.radius = ReadCoord();
186         self.strength = ReadCoord();
187         self.falloff = ReadByte();
188         self.active = ReadByte();
189
190         trigger_common_read(true);
191         return = true;
192
193         self.classname = "trigger_impulse";
194         self.solid = SOLID_TRIGGER;
195         self.entremove = trigger_remove_generic;
196         self.draw = trigger_draw_generic;
197         self.drawmask = MASK_NORMAL;
198         self.move_time = time;
199
200         if(self.radius) { self.trigger_touch = trigger_impulse_touch3; }
201         else if(self.target) { self.trigger_touch = trigger_impulse_touch1; }
202         else { self.trigger_touch = trigger_impulse_touch2; }
203 }
204 #endif