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