1 float waypointsprite_initialized;
2 float waypointsprite_fadedistance;
3 float waypointsprite_normdistance;
4 float waypointsprite_minscale;
5 float waypointsprite_minalpha;
6 float waypointsprite_distancealphaexponent;
7 float waypointsprite_timealphaexponent;
8 float waypointsprite_scale;
9 float waypointsprite_fontsize;
10 float waypointsprite_edgefadealpha;
11 float waypointsprite_edgefadescale;
12 float waypointsprite_edgefadedistance;
13 float waypointsprite_edgeoffset_bottom;
14 float waypointsprite_edgeoffset_left;
15 float waypointsprite_edgeoffset_right;
16 float waypointsprite_edgeoffset_top;
17 float waypointsprite_crosshairfadealpha;
18 float waypointsprite_crosshairfadescale;
19 float waypointsprite_crosshairfadedistance;
20 float waypointsprite_distancefadealpha;
21 float waypointsprite_distancefadescale;
22 float waypointsprite_distancefadedistance;
23 float waypointsprite_alpha;
27 .string netname; // primary picture
28 .string netname2; // secondary picture
29 .string netname3; // tertiary picture
30 .float team; // team that gets netname2
38 .float build_starthealth;
39 .float build_finished;
41 const float SPRITE_HEALTHBAR_WIDTH = 144;
42 const float SPRITE_HEALTHBAR_HEIGHT = 9;
43 const float SPRITE_HEALTHBAR_MARGIN = 6;
44 const float SPRITE_HEALTHBAR_BORDER = 2;
45 const float SPRITE_HEALTHBAR_BORDERALPHA = 1;
46 const float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
47 const float SPRITE_ARROW_SCALE = 1.0;
48 const float SPRITE_HELPME_BLINK = 2;
50 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
52 vector v1, v2, v3, v4;
54 hotspot = -1 * hotspot;
56 // hotspot-relative coordinates of the corners
58 v2 = hotspot + '1 0 0' * sz_x;
59 v3 = hotspot + '1 0 0' * sz_x + '0 1 0' * sz_y;
60 v4 = hotspot + '0 1 0' * sz_y;
62 // rotate them, and make them absolute
63 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
64 v1 = rotate(v1, rot) + org;
65 v2 = rotate(v2, rot) + org;
66 v3 = rotate(v3, rot) + org;
67 v4 = rotate(v4, rot) + org;
70 R_BeginPolygon(pic, f);
71 R_PolygonVertex(v1, '0 0 0', rgb, a);
72 R_PolygonVertex(v2, '1 0 0', rgb, a);
73 R_PolygonVertex(v3, '1 1 0', rgb, a);
74 R_PolygonVertex(v4, '0 1 0', rgb, a);
78 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
80 R_BeginPolygon(pic, f);
81 R_PolygonVertex(o, '0 0 0', rgb, a);
82 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
83 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
84 R_PolygonVertex(o + up, '0 1 0', rgb, a);
88 void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
91 float owidth; // outer width
93 hotspot = -1 * hotspot;
95 // hotspot-relative coordinates of the healthbar corners
100 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
101 o = rotate(o, rot) + org;
102 ri = rotate(ri, rot);
103 up = rotate(up, rot);
105 owidth = width + 2 * border;
106 o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
108 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
109 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
110 drawquad(o, ri * border, up * height, "", rgb, a, f);
111 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
112 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
115 // returns location of sprite text
116 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
118 float size = 9.0 * t;
119 float border = 1.5 * t;
120 float margin = 4.0 * t;
122 float borderDiag = border * 1.414;
123 vector arrowX = eX * size;
124 vector arrowY = eY * (size+borderDiag);
125 vector borderX = eX * (size+borderDiag);
126 vector borderY = eY * (size+borderDiag+border);
128 R_BeginPolygon("", DRAWFLAG_NORMAL);
129 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
130 R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
131 R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
132 R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
133 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
136 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
137 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
138 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
139 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
142 return o + rotate(eY * (borderDiag+size+margin), ang);
145 // returns location of sprite healthbar
146 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
150 float aspect, sa, ca;
152 sw = stringwidth(s, FALSE, fontsize);
159 // how do corners work?
160 aspect = vid_conwidth / vid_conheight;
162 ca = cos(ang) * aspect;
163 if(fabs(sa) > fabs(ca))
166 algny = 0.5 - 0.5 * ca / fabs(sa);
170 algnx = 0.5 - 0.5 * sa / fabs(ca);
178 // we want to be onscreen
183 if(o_x > vid_conwidth - w)
184 o_x = vid_conwidth - w;
185 if(o_y > vid_conheight - h)
186 o_x = vid_conheight - h;
188 o_x += 0.5 * (w - sw);
190 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
198 float spritelookupblinkvalue(string s)
202 case "ons-cp-atck-neut": return 2;
203 case "ons-cp-atck-red": return 2;
204 case "ons-cp-atck-blue": return 2;
205 case "ons-cp-dfnd-red": return 0.5;
206 case "ons-cp-dfnd-blue": return 0.5;
207 case "item_health_mega": return 2;
208 case "item_armor_large": return 2;
209 case "item-invis": return 2;
210 case "item-extralife": return 2;
211 case "item-speed": return 2;
212 case "item-strength": return 2;
213 case "item-shield": return 2;
214 case "item-fuelregen": return 2;
215 case "item-jetpack": return 2;
216 case "wpn-fireball": return 2; // superweapon
217 case "wpn-minstanex": return 2; // superweapon
218 case "wpn-porto": return 2; // superweapon
219 case "tagged-target": return 2;
223 vector spritelookupcolor(string s, vector def)
225 if(substring(s, 0, 4) == "wpn-")
226 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
230 case "keycarrier-friend": return '0 1 0';
234 string spritelookuptext(string s)
236 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
237 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
241 case "as-push": return _("Push");
242 case "as-destroy": return _("Destroy");
243 case "as-defend": return _("Defend");
244 case "bluebase": return _("Blue base");
245 case "danger": return _("DANGER");
246 case "enemyflagcarrier": return _("Enemy carrier");
247 case "flagcarrier": return _("Flag carrier");
248 case "flagdropped": return _("Dropped flag");
249 case "helpme": return _("Help me!");
250 case "here": return _("Here");
251 case "key-dropped": return _("Dropped key");
252 case "keycarrier-blue": return _("Key carrier");
253 case "keycarrier-finish": return _("Run here");
254 case "keycarrier-friend": return _("Key carrier");
255 case "keycarrier-pink": return _("Key carrier");
256 case "keycarrier-red": return _("Key carrier");
257 case "keycarrier-yellow": return _("Key carrier");
258 case "redbase": return _("Red base");
259 case "waypoint": return _("Waypoint");
260 case "ons-gen-red": return _("Generator");
261 case "ons-gen-blue": return _("Generator");
262 case "ons-gen-shielded": return _("Generator");
263 case "ons-cp-neut": return _("Control point");
264 case "ons-cp-red": return _("Control point");
265 case "ons-cp-blue": return _("Control point");
266 case "ons-cp-atck-neut": return _("Control point");
267 case "ons-cp-atck-red": return _("Control point");
268 case "ons-cp-atck-blue": return _("Control point");
269 case "ons-cp-dfnd-red": return _("Control point");
270 case "ons-cp-dfnd-blue": return _("Control point");
271 case "race-checkpoint": return _("Checkpoint");
272 case "race-finish": return _("Finish");
273 case "race-start": return _("Start");
274 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
275 case "goal": return _("Goal");
276 case "nb-ball": return _("Ball");
277 case "ka-ball": return _("Ball");
278 case "ka-ballcarrier": return _("Ball carrier");
279 case "dom-neut": return _("Control point");
280 case "dom-red": return _("Control point");
281 case "dom-blue": return _("Control point");
282 case "dom-yellow": return _("Control point");
283 case "dom-pink": return _("Control point");
284 case "item_health_mega": return _("Mega health");
285 case "item_armor_large": return _("Large armor");
286 case "item-invis": return _("Invisibility");
287 case "item-extralife": return _("Extra life");
288 case "item-speed": return _("Speed");
289 case "item-strength": return _("Strength");
290 case "item-shield": return _("Shield");
291 case "item-fuelregen": return _("Fuel regen");
292 case "item-jetpack": return _("Jet Pack");
293 case "frozen": return _("Frozen!");
294 case "tagged-target": return _("Tagged");
295 case "vehicle": return _("Vehicle");
300 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
302 vector yvec = '0.299 0.587 0.114';
303 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
305 vector fixrgbexcess(vector rgb)
309 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
312 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
318 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
325 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
328 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
334 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
341 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
344 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
350 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
358 float waypointsprite_count, waypointsprite_newcount;
359 void Draw_WaypointSprite()
365 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
369 if(self.hideflags & 2)
370 return; // radar only
372 if(autocvar_cl_hidewaypoints >= 2)
375 if(self.hideflags & 1)
376 if(autocvar_cl_hidewaypoints)
377 return; // fixed waypoint
379 InterpolateOrigin_Do();
381 t = GetPlayerColor(player_localnum) + 1;
388 case SPRITERULE_SPECTATOR:
389 if not((autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
390 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage)))
392 spriteimage = self.netname;
394 case SPRITERULE_DEFAULT:
398 spriteimage = self.netname;
403 spriteimage = self.netname;
405 case SPRITERULE_TEAMPLAY:
406 if(t == NUM_SPECTATOR + 1)
407 spriteimage = self.netname3;
408 else if(self.team == t)
409 spriteimage = self.netname2;
411 spriteimage = self.netname;
414 error("Invalid waypointsprite rule!");
418 if(spriteimage == "")
421 ++waypointsprite_newcount;
424 dist = vlen(self.origin - view_origin);
427 a = self.alpha * autocvar_hud_panel_fg_alpha;
429 if(self.maxdistance > waypointsprite_normdistance)
430 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
431 else if(self.maxdistance > 0)
432 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
435 rgb = self.teamradar_color;
436 rgb = spritelookupcolor(spriteimage, rgb);
439 self.teamradar_color = '1 0 1';
440 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
443 if(time - floor(time) > 0.5)
445 if(self.helpme && time < self.helpme)
446 a *= SPRITE_HELPME_BLINK;
448 a *= spritelookupblinkvalue(spriteimage);
460 rgb = fixrgbexcess(rgb);
465 o = project_3d_to_2d(self.origin);
467 || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
468 || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
469 || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
470 || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
472 // scale it to be just in view
476 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
477 ang = atan2(-d_x, -d_y);
481 f1 = d_x / vid_conwidth;
482 f2 = d_y / vid_conheight;
484 if(max(f1, -f1) > max(f2, -f2))
489 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
494 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
502 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
507 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
511 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
519 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
520 ang = atan2(-d_x, -d_y);
525 float edgedistance_min, crosshairdistance;
526 edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
527 (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
528 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
529 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
532 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
534 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
536 t = waypointsprite_scale * vidscale;
537 a *= waypointsprite_alpha;
540 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
541 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
543 if (edgedistance_min < waypointsprite_edgefadedistance) {
544 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
545 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
547 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
548 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
549 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
552 if(self.build_finished)
554 if(time < self.build_finished + 0.25)
556 if(time < self.build_started)
557 self.health = self.build_starthealth;
558 else if(time < self.build_finished)
559 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
567 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
570 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
573 txt = spritelookuptext(spriteimage);
574 if(self.helpme && time < self.helpme)
575 txt = sprintf(_("%s needing help!"), txt);
576 if(autocvar_g_waypointsprite_uppercase)
577 txt = strtoupper(txt);
579 draw_beginBoldFont();
582 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
585 if(self.build_finished)
590 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
592 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
599 SPRITE_HEALTHBAR_WIDTH * t,
600 SPRITE_HEALTHBAR_HEIGHT * t,
602 SPRITE_HEALTHBAR_BORDER * t,
605 a * SPRITE_HEALTHBAR_BORDERALPHA,
607 a * SPRITE_HEALTHBAR_HEALTHALPHA,
613 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
618 void Ent_RemoveWaypointSprite()
621 strunzone(self.netname);
623 strunzone(self.netname2);
625 strunzone(self.netname3);
628 void Ent_WaypointSprite()
630 float sendflags, f, t;
631 sendflags = ReadByte();
634 self.spawntime = time;
636 self.draw2d = Draw_WaypointSprite;
638 InterpolateOrigin_Undo();
639 self.iflags |= IFLAG_ORIGIN;
646 self.health = t / 191.0;
647 self.build_finished = 0;
651 t = (t - 192) * 256 + ReadByte();
652 self.build_started = servertime;
653 if(self.build_finished)
654 self.build_starthealth = bound(0, self.health, 1);
656 self.build_starthealth = 0;
657 self.build_finished = servertime + t / 32;
663 self.build_finished = 0;
668 // unfortunately, this needs to be exact (for the 3D display)
669 self.origin_x = ReadCoord();
670 self.origin_y = ReadCoord();
671 self.origin_z = ReadCoord();
672 setorigin(self, self.origin);
677 self.team = ReadByte();
678 self.rule = ReadByte();
684 strunzone(self.netname);
685 self.netname = strzone(ReadString());
691 strunzone(self.netname2);
692 self.netname2 = strzone(ReadString());
698 strunzone(self.netname3);
699 self.netname3 = strzone(ReadString());
704 self.lifetime = ReadCoord();
705 self.fadetime = ReadCoord();
706 self.maxdistance = ReadShort();
707 self.hideflags = ReadByte();
713 self.teamradar_icon = (f & 0x7F);
716 self.(teamradar_times[self.teamradar_time_index]) = time;
717 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
719 self.teamradar_color_x = ReadByte() / 255.0;
720 self.teamradar_color_y = ReadByte() / 255.0;
721 self.teamradar_color_z = ReadByte() / 255.0;
722 self.helpme = ReadByte() * 0.1;
724 self.helpme += servertime;
727 InterpolateOrigin_Note();
729 self.entremove = Ent_RemoveWaypointSprite;
732 void WaypointSprite_Load_Frames(string ext)
734 float dh, n, i, o, f;
735 string s, sname, sframes;
736 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
739 float ext_len = strlen(ext);
740 n = search_getsize(dh);
741 for(i = 0; i < n; ++i)
743 s = search_getfilename(dh, i);
744 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
746 o = strstrofs(s, "_frame", 0);
747 sname = strcat("/spriteframes/", substring(s, 0, o));
748 sframes = substring(s, o + 6, strlen(s) - o - 6);
749 f = stof(sframes) + 1;
750 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
755 void WaypointSprite_Load()
757 waypointsprite_fadedistance = vlen(mi_scale);
758 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
759 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
760 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
761 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
762 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
763 waypointsprite_scale = autocvar_g_waypointsprite_scale;
764 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
765 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
766 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
767 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
768 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
769 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
770 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
771 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
772 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
773 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
774 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
775 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
776 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
777 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
778 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
780 if(!waypointsprite_initialized)
782 WaypointSprite_Load_Frames(".tga");
783 WaypointSprite_Load_Frames(".jpg");
784 waypointsprite_initialized = true;
787 waypointsprite_count = waypointsprite_newcount;
788 waypointsprite_newcount = 0;