]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/physics/movetypes/push.qc
Merge branch 'Mario/movetype_push' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / physics / movetypes / push.qc
1 #include "push.qh"
2 void _Movetype_PushMove(entity this, float dt) // SV_PushMove
3 {
4         if(this.velocity == '0 0 0' && this.avelocity == '0 0 0')
5         {
6                 this.ltime += dt;
7                 return;
8         }
9
10         switch(this.solid)
11         {
12                 // LadyHavoc: valid pusher types
13                 case SOLID_BSP:
14                 case SOLID_BBOX:
15                 case SOLID_SLIDEBOX:
16                 case SOLID_CORPSE: // LadyHavoc: this would be weird...
17                         break;
18                 // LadyHavoc: no collisions
19                 case SOLID_NOT:
20                 case SOLID_TRIGGER:
21                 {
22                         this.origin = this.origin + dt * this.velocity;
23                         this.angles = this.angles + dt * this.avelocity;
24                         this.angles_x -= 360.0 * floor(this.angles_x * (1.0 / 360.0));
25                         this.angles_y -= 360.0 * floor(this.angles_y * (1.0 / 360.0));
26                         this.angles_z -= 360.0 * floor(this.angles_z * (1.0 / 360.0));
27                         this.ltime += dt;
28                         _Movetype_LinkEdict(this, false);
29                         return;
30                 }
31                 default:
32                 {
33                         LOG_INFOF("_Movetype_Physics_Push: entity #%d, unrecognized solid type %d", etof(this), this.solid);
34                         return;
35                 }
36         }
37         if(!this.modelindex)
38         {
39                 LOG_INFOF("_Movetype_Physics_Push: entity #%d has an invalid modelindex %d", etof(this), this.modelindex);
40                 return;
41         }
42
43         bool rotated = ((vlen2(this.angles) + vlen2(this.avelocity)) > 0);
44
45         vector move1 = this.velocity * dt;
46         vector moveangle = this.avelocity * dt;
47
48         vector a = -moveangle;
49         vector forward, left, up;
50         MAKE_VECTORS(a, forward, left, up);
51         left *= -1; // actually make it left!
52
53         vector pushorig = this.origin;
54         vector pushang = this.angles;
55         float pushltime = this.ltime;
56
57         // move the pusher to its final position
58
59         this.origin = this.origin + dt * this.velocity;
60         this.angles = this.angles + dt * this.avelocity;
61         this.ltime += dt;
62         _Movetype_LinkEdict(this, false); // pulls absmin/absmax from the engine
63
64         if(this.move_movetype == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
65         {
66                 this.angles_x -= 360.0 * floor(this.angles_x * (1.0 / 360.0));
67                 this.angles_y -= 360.0 * floor(this.angles_y * (1.0 / 360.0));
68                 this.angles_z -= 360.0 * floor(this.angles_z * (1.0 / 360.0));
69                 return;
70         }
71
72         IL_CLEAR(g_pushmove_moved); // make sure it's not somehow uncleared
73
74         for(entity check = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); check; check = check.chain)
75         {
76                 switch(check.move_movetype)
77                 {
78                         case MOVETYPE_NONE:
79                         case MOVETYPE_PUSH:
80                         case MOVETYPE_FOLLOW:
81                         case MOVETYPE_NOCLIP:
82                         case MOVETYPE_FLY_WORLDONLY:
83                                 continue;
84                         default:
85                                 break;
86                 }
87
88                 if(check.owner == this || this.owner == check)
89                         continue;
90
91                 // if the entity is standing on the pusher, it will definitely be moved
92                 // if the entity is not standing on the pusher, but is in the pusher's
93                 // final position, move it
94                 if (!IS_ONGROUND(check) || check.groundentity != this)
95                 {
96                         tracebox(check.origin, check.mins, check.maxs, check.origin, MOVE_NOMONSTERS, check);
97                         if(!trace_startsolid)
98                                 continue;
99                 }
100                 vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
101                 vector move;
102
103                 if(rotated)
104                 {
105                         vector org = check.origin - this.origin;
106                         org = org + pivot;
107
108                         vector org2;
109                         org2.x = (org * forward);
110                         org2.y = (org * left);
111                         org2.z = (org * up);
112                         move = org2 - org;
113                         move = move + move1;
114                 }
115                 else
116                         move = move1;
117
118                 check.moved_from = check.origin;
119                 check.moved_fromangles = check.angles;
120                 IL_PUSH(g_pushmove_moved, check);
121
122                 // physics objects need better collisions than this code can do
123                 if(check.move_movetype == MOVETYPE_PHYSICS)
124                 {
125                         check.origin = check.origin + move;
126                         _Movetype_LinkEdict(check, true);
127                         continue;
128                 }
129
130                 // try moving the contacted entity
131                 int savesolid = this.solid;
132                 this.solid = SOLID_NOT;
133                 if(!_Movetype_PushEntity(check, move, true, true))
134                 {
135                         // entity "check" got teleported
136                         check.angles_y += trace_fraction * moveangle.y;
137                         this.solid = savesolid;
138                         continue; // pushed enough
139                 }
140                 // FIXME: turn players specially
141                 check.angles_y += trace_fraction * moveangle.y;
142                 this.solid = savesolid;
143
144                 // this trace.fraction < 1 check causes items to fall off of pushers
145                 // if they pass under or through a wall
146                 // the groundentity check causes items to fall off of ledges
147                 if(check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.groundentity != this))
148                         UNSET_ONGROUND(check);
149
150                 // if it is still inside the pusher, block
151                 tracebox(check.origin, check.mins, check.maxs, check.origin, MOVE_NOMONSTERS, check);
152                 if(trace_startsolid)
153                 {
154                         if(_Movetype_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
155                         {
156                                 // hack to invoke all necessary movement triggers
157                                 _Movetype_PushEntity(check, '0 0 0', true, true);
158                                 // we could fix it or entity "check" was telported
159                                 continue;
160                         }
161
162                         // still inside pusher, so it's really blocked
163
164                         // fail the move
165                         if(check.mins_x == check.maxs_x)
166                                 continue;
167                         if(check.solid == SOLID_NOT || check.solid == SOLID_TRIGGER)
168                         {
169                                 // corpse
170                                 check.mins_x = check.mins_y = 0;
171                                 check.maxs = check.mins;
172                                 continue;
173                         }
174
175                         this.origin = pushorig;
176                         this.angles = pushang;
177                         this.ltime = pushltime;
178                         _Movetype_LinkEdict(this, false);
179
180                         // move back any entities we already moved
181                         IL_EACH(g_pushmove_moved, true,
182                         {
183                                 check.origin = check.moved_from;
184                                 check.angles = check.moved_fromangles;
185                                 _Movetype_LinkEdict(check, false);
186                         });
187
188                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
189                         if(getblocked(this))
190                                 getblocked(this)(this, check);
191                         break;
192                 }
193         }
194         this.angles_x -= 360.0 * floor(this.angles_x * (1.0 / 360.0));
195         this.angles_y -= 360.0 * floor(this.angles_y * (1.0 / 360.0));
196         this.angles_z -= 360.0 * floor(this.angles_z * (1.0 / 360.0));
197         IL_CLEAR(g_pushmove_moved); // clean up
198 }
199
200 void _Movetype_Physics_Push(entity this, float dt) // SV_Physics_Pusher
201 {
202         float oldltime = this.ltime;
203         float movetime = dt;
204         if(this.nextthink < this.ltime + dt)
205         {
206                 movetime = this.nextthink - this.ltime;
207                 if(movetime < 0)
208                         movetime = 0;
209         }
210
211         if(movetime)
212         {
213                 // advances this.ltime if not blocked
214                 _Movetype_PushMove(this, movetime);
215         }
216
217         if(this.nextthink > oldltime && this.nextthink <= this.ltime)
218         {
219                 this.nextthink = 0;
220                 getthink(this)(this);
221         }
222 }