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