]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/trigger/viewloc.qc
Merge branch 'master' into terencehill/bot_waypoints
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / trigger / viewloc.qc
1 #include "viewloc.qh"
2 #if defined(CSQC)
3 #elif defined(MENUQC)
4 #elif defined(SVQC)
5     #include <lib/warpzone/util_server.qh>
6     #include <server/defs.qh>
7 #endif
8
9 REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC)
10 REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER)
11
12 #ifdef SVQC
13
14 void viewloc_think(entity this)
15 {
16         // we abuse this method, rather than using normal .touch, because touch isn't reliable with multiple clients inside the same trigger, and can't "untouch" entities
17
18         // set myself as current viewloc where possible
19 #if 1
20         FOREACH_CLIENT(it.viewloc == this,
21         {
22                 it.viewloc = NULL;
23         });
24 #else
25         entity e;
26         for(e = NULL; (e = findentity(e, viewloc, this)); )
27                 e.viewloc = NULL;
28 #endif
29
30 #if 1
31         FOREACH_CLIENT(!it.viewloc && IS_PLAYER(it),
32         {
33                 vector emin = it.absmin;
34                 vector emax = it.absmax;
35                 if(this.solid == SOLID_BSP)
36                 {
37                         emin -= '1 1 1';
38                         emax += '1 1 1';
39                 }
40                 if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
41                 {
42                         if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
43                                 it.viewloc = this;
44                 }
45         });
46 #else
47
48                 for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain)
49                         if(!e.viewloc)
50                                 if(IS_PLAYER(e)) // should we support non-player entities with this?
51                                 //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet
52                                 {
53                                         vector emin = e.absmin;
54                                         vector emax = e.absmax;
55                                         if(this.solid == SOLID_BSP)
56                                         {
57                                                 emin -= '1 1 1';
58                                                 emax += '1 1 1';
59                                         }
60                                         if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
61                                                 if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate
62                                                         e.viewloc = this;
63                                 }
64 #endif
65
66         this.nextthink = time;
67 }
68
69 bool trigger_viewloc_send(entity this, entity to, int sf)
70 {
71         // CSQC doesn't need to know our origin (yet), as we're only available for referencing
72         WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER);
73
74         WriteByte(MSG_ENTITY, this.spawnflags);
75
76         WriteEntity(MSG_ENTITY, this.enemy);
77         WriteEntity(MSG_ENTITY, this.goalentity);
78
79         WriteCoord(MSG_ENTITY, this.origin_x);
80         WriteCoord(MSG_ENTITY, this.origin_y);
81         WriteCoord(MSG_ENTITY, this.origin_z);
82
83         return true;
84 }
85
86 void viewloc_init(entity this)
87 {
88         entity e;
89         for(e = NULL; (e = find(e, targetname, this.target)); )
90                 if(e.classname == "target_viewlocation_start")
91                 {
92                         this.enemy = e;
93                         break;
94                 }
95         for(e = NULL; (e = find(e, targetname, this.target2)); )
96                 if(e.classname == "target_viewlocation_end")
97                 {
98                         this.goalentity = e;
99                         break;
100                 }
101
102         if(!this.enemy) { LOG_INFO("^1FAIL!"); delete(this); return; }
103
104         if(!this.goalentity)
105                 this.goalentity = this.enemy; // make them match so CSQC knows what to do
106
107         Net_LinkEntity(this, false, 0, trigger_viewloc_send);
108
109         setthink(this, viewloc_think);
110         this.nextthink = time;
111 }
112
113 spawnfunc(trigger_viewlocation)
114 {
115         // we won't check target2 here yet, as it may not even need to exist
116         if(this.target == "") { LOG_INFO("^1FAIL!"); delete(this); return; }
117
118         EXACTTRIGGER_INIT;
119         InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET);
120 }
121
122 bool viewloc_send(entity this, entity to, int sf)
123 {
124         WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC);
125
126         WriteByte(MSG_ENTITY, this.cnt);
127
128         WriteCoord(MSG_ENTITY, this.origin_x);
129         WriteCoord(MSG_ENTITY, this.origin_y);
130         WriteCoord(MSG_ENTITY, this.origin_z);
131
132         WriteAngle(MSG_ENTITY, this.angles_x);
133         WriteAngle(MSG_ENTITY, this.angles_y);
134         WriteAngle(MSG_ENTITY, this.angles_z);
135
136         return true;
137 }
138
139 .float angle;
140 void viewloc_link(entity this)
141 {
142         if(this.angle)
143                 this.angles_y = this.angle;
144         Net_LinkEntity(this, false, 0, viewloc_send);
145 }
146
147 spawnfunc(target_viewlocation_start)
148 {
149         this.classname = "target_viewlocation_start";
150         this.cnt = 1;
151         viewloc_link(this);
152 }
153 spawnfunc(target_viewlocation_end)
154 {
155         this.classname = "target_viewlocation_end";
156         this.cnt = 2;
157         viewloc_link(this);
158 }
159
160 // compatibility
161 spawnfunc(target_viewlocation) { spawnfunc_target_viewlocation_start(this); }
162
163 #elif defined(CSQC)
164
165 void trigger_viewloc_updatelink(entity this)
166 {
167         this.enemy = findfloat(NULL, entnum, this.cnt);
168         this.goalentity = findfloat(NULL, entnum, this.count);
169 }
170
171 NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew)
172 {
173         this.spawnflags = ReadByte();
174
175         float point1 = ReadShort();
176         float point2 = ReadShort();
177
178         this.enemy = findfloat(NULL, entnum, point1);
179         this.goalentity = findfloat(NULL, entnum, point2);
180
181         this.origin_x = ReadCoord();
182         this.origin_y = ReadCoord();
183         this.origin_z = ReadCoord();
184
185         return = true;
186
187         setorigin(this, this.origin);
188
189         this.cnt = point1;
190         this.count = point2;
191
192         setthink(this, trigger_viewloc_updatelink);
193         this.nextthink = time + 1; // we need to delay this or else
194
195         this.classname = "trigger_viewlocation";
196         this.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive
197 }
198
199 NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew)
200 {
201         this.cnt = ReadByte();
202
203         this.origin_x = ReadCoord();
204         this.origin_y = ReadCoord();
205         this.origin_z = ReadCoord();
206         setorigin(this, this.origin);
207
208         this.movedir_x = ReadAngle();
209         this.movedir_y = ReadAngle();
210         this.movedir_z = ReadAngle();
211
212         return = true;
213
214         this.classname = ((this.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start");
215         this.drawmask = MASK_NORMAL; // don't cull it
216 }
217
218 #endif