1 #include "3d_objects.h"
2 #include "actor_scripts.h"
3 #include "asc.h"
4 #include "cal.h"
5 #include "cal3d_wrapper.h"
6 #include "context_menu.h"
7 #include "e3d.h"
8 #include "elconfig.h"
9 #include "elwindows.h"
10 #include "errors.h"
11 #include "eye_candy_wrapper.h"
12 #include "gamewin.h"
13 #include "gl_init.h"
14 #ifdef MISSILES_DEBUG
15 #include "init.h"
16 #endif
17 #include "missiles.h"
18 #include "skeletons.h"
19 #include "stats.h"
20 #include "session.h"
21 #include "tiles.h"
22 #include "translate.h"
23 #include "vmath.h"
24 
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <time.h>
29 
30 #define MAX_LOST_MISSILES 512
31 #define LOST_MISSILE_MAX_LIFE 120000
32 #define EPSILON 1E-4
33 
34 typedef struct {
35 	int obj_3d_id;
36 	Uint32 end_time;
37 } lost_missile;
38 
39 const float arrow_color[4] = {0.8, 0.8, 0.8, 1.0};
40 const float arrow_border_color[4] = {0.8, 0.8, 0.8, 0.5};
41 const float miss_color[4] = {0.9, 0.6, 0.6, 1.0};
42 const float critical_color[4] = {0.6, 0.9, 1.0, 1.0};
43 const float critical_border1_color[4] = {0.3, 0.7, 1.0, 0.6};
44 const float critical_border2_color[4] = {0.0, 0.5, 1.0, 0.4};
45 
46 missile missiles_list[MAX_MISSILES];
47 missile_type missiles_defs[MAX_MISSILES_DEFS];
48 lost_missile lost_missiles_list[MAX_LOST_MISSILES];
49 
50 int missiles_count = 0;
51 int begin_lost_missiles = -1;
52 int end_lost_missiles = -1;
53 
54 int range_total_shots = 0;
55 int range_success_hits = 0;
56 int range_critical_hits = 0;
57 
58 #ifdef MISSILES_DEBUG
59 FILE *missiles_log = NULL;
60 
missiles_open_log()61 void missiles_open_log()
62 {
63 	char log_name[1024];
64 	char starttime[200], sttime[200];
65 	struct tm *l_time; time_t c_time;
66 
67 	safe_snprintf(log_name, 1024, "%smissiles_log.txt", configdir);
68 
69 	missiles_log = fopen(log_name, "w");
70 
71 	if (missiles_log == NULL)
72 	{
73 		fprintf (stderr, "Unable to open log file \"%s\"\n", log_name);
74 		exit (1);
75 	}
76 
77 	time (&c_time);
78 	l_time = localtime (&c_time);
79 	strftime(sttime, sizeof(sttime), "\n\nLog started at %Y-%m-%d %H:%M:%S localtime", l_time);
80 	safe_snprintf(starttime, sizeof(starttime), "%s (%s)\n\n", sttime, tzname[l_time->tm_isdst>0]);
81 	fwrite (starttime, strlen(starttime), 1, missiles_log);
82 }
83 
missiles_log_message_func(const char * format,...)84 void missiles_log_message_func(const char *format, ...)
85 {
86 	va_list ap;
87 	struct tm *l_time; time_t c_time;
88 	char logmsg[512];
89 	char errmsg[512];
90 
91 	va_start(ap, format);
92 	vsnprintf(errmsg, 512, format, ap);
93 	va_end(ap);
94 
95 	if (missiles_log == NULL)
96 		missiles_open_log();
97 
98 	time(&c_time);
99 	l_time = localtime(&c_time);
100 	strftime(logmsg, sizeof(logmsg), "[%H:%M:%S] ", l_time);
101 	strcat(logmsg, errmsg);
102 
103 	if(format[strlen(format)-1] != '\n') {
104 		strcat(logmsg, "\n");
105 	}
106 	fprintf(missiles_log, "%s", logmsg);
107 	fflush (missiles_log);
108 }
109 #endif // MISSILES_DEBUG
110 
missiles_clear()111 void missiles_clear()
112 {
113 	missiles_count = 0;
114 	begin_lost_missiles = end_lost_missiles = -1;
115 }
116 
missiles_add(int type,float origin[3],float target[3],float shift,MissileShotType shot_type)117 int missiles_add(int type,
118 				 float origin[3],
119 				 float target[3],
120 				 float shift,
121 				 MissileShotType shot_type)
122 {
123 	missile *mis;
124 	missile_type *mis_type = &missiles_defs[type];
125 	float direction[3];
126 	float dist;
127 
128 	if (missiles_count >= MAX_MISSILES) {
129 		LOG_ERROR("too many missiles, can't add the last one!");
130 		return MAX_MISSILES;
131 	}
132 
133 	missiles_log_message("missiles_add: origin=(%.2f,%.2f,%.2f), target=(%.2f,%.2f,%.2f) type %u",
134 						 origin[0], origin[1], origin[2], target[0], target[1], target[2], shot_type);
135 
136 	direction[0] = target[0] - origin[0];
137 	direction[1] = target[1] - origin[1];
138 	direction[2] = target[2] - origin[2];
139 	dist = sqrtf(direction[0]*direction[0] +
140 				 direction[1]*direction[1] +
141 				 direction[2]*direction[2]);
142 
143 	if (fabs(dist) < EPSILON) {
144 		LOG_ERROR("null length shot detected between (%f,%f,%f) and (%f,%f,%f), not adding the missile!",
145 				  origin[0], origin[1], origin[2],
146 				  target[0], target[1], target[2]);
147 		return MAX_MISSILES;
148 	}
149     else {
150         missiles_log_message("missiles_add: distance of the shot: %f", dist);
151     }
152 
153 	mis = &missiles_list[missiles_count++];
154 
155 	mis->type = type;
156 	mis->shot_type = shot_type;
157 	memcpy(mis->position, origin, sizeof(float)*3);
158 	memcpy(mis->direction, direction, sizeof(float)*3);
159 	mis->remaining_distance = dist;
160 	mis->direction[0] /= mis->remaining_distance;
161 	mis->direction[1] /= mis->remaining_distance;
162 	mis->direction[2] /= mis->remaining_distance;
163 	mis->speed = mis_type->speed;
164 	mis->trace_length = mis_type->trace_length;
165 	mis->covered_distance = 0;
166 	mis->remaining_distance += shift;
167 
168 	if (use_eye_candy == 1)
169 	{
170 		ec_create_missile_effect(missiles_count-1, (poor_man ? 6 : 10), shot_type);
171 	}
172 
173 	return missiles_count-1;
174 }
175 
missiles_add_lost(int obj_id)176 void missiles_add_lost(int obj_id)
177 {
178 	if (begin_lost_missiles < 0) {
179 		end_lost_missiles = begin_lost_missiles = 0;
180 	}
181 	else {
182 		end_lost_missiles = (end_lost_missiles + 1) % MAX_LOST_MISSILES;
183 		if (end_lost_missiles == begin_lost_missiles) {
184 			destroy_3d_object(lost_missiles_list[begin_lost_missiles].obj_3d_id);
185 			begin_lost_missiles = (begin_lost_missiles + 1) % MAX_LOST_MISSILES;
186 		}
187 	}
188 	lost_missiles_list[end_lost_missiles].obj_3d_id = obj_id;
189 	lost_missiles_list[end_lost_missiles].end_time = cur_time + LOST_MISSILE_MAX_LIFE;
190 }
191 
missiles_remove(int missile_id)192 void missiles_remove(int missile_id)
193 {
194 	missile *mis = get_missile_ptr_from_id(missile_id);
195 
196 	if (!mis) {
197 		LOG_ERROR("missile id %i is out of range!", missile_id);
198 		return;
199 	}
200 
201     /* if the shot is missed and if it has travel a distance which is under
202      * the distance used on server side (20.0), we display a stuck arrow
203      * where the shot has ended */
204 	if (mis->shot_type == MISSED_SHOT &&
205 		mis->covered_distance < 19.0) {
206         float x_rot = 0.0;
207 		float y_rot = -asinf(mis->direction[2])*180.0/M_PI;
208 		float z_rot = atan2f(mis->direction[1], mis->direction[0])*180.0/M_PI;
209 		float dist = -mis->remaining_distance;
210 		int obj_3d_id = -1;
211 		// don't change the missile position after it arrived at the target position
212 		//mis->position[0] -= mis->direction[0] * dist;
213 		//mis->position[1] -= mis->direction[1] * dist;
214 		//mis->position[2] -= mis->direction[2] * dist;
215 		missiles_log_message("adding a lost missile at (%f,%f,%f) with rotation (%f,%f,%f)",
216 							 mis->position[0] - mis->direction[0] * dist,
217 							 mis->position[1] - mis->direction[1] * dist,
218 							 mis->position[2] - mis->direction[2] * dist,
219                              x_rot, y_rot, z_rot);
220 		obj_3d_id = add_e3d(missiles_defs[mis->type].lost_mesh,
221 							mis->position[0] - mis->direction[0] * dist,
222 							mis->position[1] - mis->direction[1] * dist,
223 							mis->position[2] - mis->direction[2] * dist,
224 							x_rot, y_rot, z_rot, 0, 0, 1.0, 1.0, 1.0, 1);
225 		if (obj_3d_id >= 0)
226 			missiles_add_lost(obj_3d_id);
227 	}
228 
229 	ec_remove_missile(missile_id);
230 
231 	--missiles_count;
232 	if (missile_id < missiles_count) {
233 		memcpy(&missiles_list[missile_id],
234 			   &missiles_list[missiles_count],
235 			   sizeof(missile));
236 		ec_rename_missile(missiles_count, missile_id);
237 	}
238 }
239 
missiles_update()240 void missiles_update()
241 {
242 	int i;
243 	static int last_update = 0;
244 	float time_diff = (cur_time - last_update) / 1000.0;
245 
246 	for (i = 0; i < missiles_count; ) {
247 		missile *mis = &missiles_list[i];
248 		float dist = mis->speed * time_diff;
249 		mis->position[0] += mis->direction[0] * dist;
250 		mis->position[1] += mis->direction[1] * dist;
251 		mis->position[2] += mis->direction[2] * dist;
252 		mis->covered_distance += dist;
253 		mis->remaining_distance -= dist;
254 		if (mis->remaining_distance < -mis->trace_length)
255 			missiles_remove(i);
256 		else
257 			++i;
258 	}
259 
260 	while (begin_lost_missiles >= 0 &&
261 		   cur_time > lost_missiles_list[begin_lost_missiles].end_time) {
262 		destroy_3d_object(lost_missiles_list[begin_lost_missiles].obj_3d_id);
263 		if (begin_lost_missiles == end_lost_missiles)
264 			begin_lost_missiles = end_lost_missiles = -1;
265 		else
266 			begin_lost_missiles = (begin_lost_missiles + 1) % MAX_LOST_MISSILES;
267 	}
268 
269 	last_update = cur_time;
270 }
271 
missiles_draw_single(missile * mis,const float color[4])272 void missiles_draw_single(missile *mis, const float color[4])
273 {
274 	float z_shift = 0.0;
275 
276 /* 	if (mis->shot_type == MISSED_SHOT) */
277 /* 		z_shift = cosf(mis->covered_distance*M_PI/2.0)/10.0; */
278 
279     if (mis->covered_distance < mis->trace_length) {
280         glColor4f(color[0], color[1], color[2],
281                   color[3] * (mis->trace_length - mis->covered_distance) / mis->trace_length);
282         glVertex3f(mis->position[0] - mis->covered_distance * mis->direction[0],
283                    mis->position[1] - mis->covered_distance * mis->direction[1],
284                    mis->position[2] - mis->covered_distance * mis->direction[2]);
285     }
286     else {
287         glColor4f(color[0], color[1], color[2], 0.0);
288         glVertex3f(mis->position[0] - mis->trace_length * mis->direction[0],
289                    mis->position[1] - mis->trace_length * mis->direction[1],
290                    mis->position[2] - mis->trace_length * mis->direction[2]);
291     }
292     if (mis->remaining_distance < 0.0) {
293         glColor4f(color[0], color[1], color[2],
294                   color[3] * (mis->trace_length + mis->remaining_distance) / mis->trace_length);
295         glVertex3f(mis->position[0] + mis->remaining_distance * mis->direction[0],
296                    mis->position[1] + mis->remaining_distance * mis->direction[1],
297                    mis->position[2] + mis->remaining_distance * mis->direction[2] + z_shift);
298     }
299     else {
300         glColor4f(color[0], color[1], color[2], color[3]);
301         glVertex3f(mis->position[0], mis->position[1], mis->position[2] + z_shift);
302     }
303 }
304 
missiles_draw()305 void missiles_draw()
306 {
307 	int i;
308 
309 	glPushAttrib(GL_ALL_ATTRIB_BITS);
310 	glEnable(GL_BLEND);
311 	glEnable(GL_COLOR_MATERIAL);
312 	glDisable(GL_LIGHTING);
313 	glDisable(GL_TEXTURE_2D);
314 
315 	glLineWidth(7.0);
316 	glBegin(GL_LINES);
317 	for (i = missiles_count; i--;) {
318 		if (missiles_list[i].shot_type == CRITICAL_SHOT)
319 			missiles_draw_single(&missiles_list[i], critical_border2_color);
320 	}
321 	glEnd();
322 
323 	glLineWidth(3.0);
324 	glBegin(GL_LINES);
325 	for (i = missiles_count; i--;) {
326 		if (missiles_list[i].shot_type == NORMAL_SHOT)
327 			missiles_draw_single(&missiles_list[i], arrow_border_color);
328 		else if (missiles_list[i].shot_type == CRITICAL_SHOT)
329 			missiles_draw_single(&missiles_list[i], critical_border1_color);
330 	}
331 	glEnd();
332 
333 	glLineWidth(1.0);
334 	glBegin(GL_LINES);
335 	for (i = missiles_count; i--;) {
336 		if (missiles_list[i].shot_type == NORMAL_SHOT)
337 			missiles_draw_single(&missiles_list[i], arrow_color);
338 		else if (missiles_list[i].shot_type == CRITICAL_SHOT)
339 			missiles_draw_single(&missiles_list[i], critical_color);
340 	}
341 	glEnd();
342 
343 	glLineWidth(2.0);
344 	glLineStipple(1, 0x003F);
345 	glEnable(GL_LINE_STIPPLE);
346 	glBegin(GL_LINES);
347 	for (i = missiles_count; i--;)
348 		if (missiles_list[i].shot_type == MISSED_SHOT)
349 			missiles_draw_single(&missiles_list[i], miss_color);
350 	glEnd();
351 	glDisable(GL_LINE_STIPPLE);
352 
353 	glPopAttrib();
354 }
355 
missiles_compute_actor_rotation(float * out_h_rot,float * out_v_rot,actor * in_act,float * in_target)356 float missiles_compute_actor_rotation(float *out_h_rot, float *out_v_rot,
357 									  actor *in_act, float *in_target)
358 {
359 	float cz, sz;
360 	float from[3], to[3], tmp[3], origin[3];
361 	float actor_rotation = 0;
362 	float act_z_rot = in_act->z_rot;
363 
364 	if (in_act->rotating) {
365         missiles_log_message("%s (%d): already rotating so we get the final position first",
366                              in_act->actor_name, in_act->actor_id);
367 		act_z_rot += in_act->rotate_z_speed * in_act->rotate_time_left;
368 	}
369 
370 	// we first compute the global rotation
371 	cz = cosf((act_z_rot) * M_PI/180.0);
372 	sz = sinf((act_z_rot) * M_PI/180.0);
373 	tmp[0] = in_target[0] - in_act->x_pos - 0.25;
374 	tmp[1] = in_target[1] - in_act->y_pos - 0.25;
375 	tmp[2] = 0.0;
376 	Normalize(tmp, tmp);
377 
378 	actor_rotation = asinf(tmp[0] * cz - tmp[1] * sz) * 180.0/M_PI;
379 	if (tmp[0] * sz + tmp[1] * cz < 0.0) {
380 		if (actor_rotation < 0.0)
381 			actor_rotation = ((int)(-180-actor_rotation-22.5) / 45) * 45.0;
382 		else
383 			actor_rotation = ((int)( 180-actor_rotation+22.5) / 45) * 45.0;
384 	}
385 	else {
386 		if (actor_rotation < 0.0)
387 			actor_rotation = ((int)(actor_rotation-22.5) / 45) * 45.0;
388 		else
389 			actor_rotation = ((int)(actor_rotation+22.5) / 45) * 45.0;
390 	}
391 
392 	missiles_log_message("%s (%d): cos = %f ; sin = %f", in_act->actor_name, in_act->actor_id, cz, sz);
393 	missiles_log_message("%s (%d): direction = %f %f %f", in_act->actor_name, in_act->actor_id, tmp[0], tmp[1], tmp[2]);
394 	missiles_log_message("%s (%d): actor rotation = %f", in_act->actor_name, in_act->actor_id, actor_rotation);
395 
396 	// we then compute the fine rotation
397 	cz = cosf((act_z_rot + actor_rotation) * M_PI/180.0);
398 	sz = sinf((act_z_rot + actor_rotation) * M_PI/180.0);
399 
400 	origin[0] = in_act->x_pos + 0.25;
401 	origin[1] = in_act->y_pos + 0.25;
402 	origin[2] = get_actor_z(in_act) + 1.4 * get_actor_scale(in_act);
403 
404 	missiles_log_message("%s (%d): compute_actor_rotation: origin=(%.2f,%.2f,%.2f), target=(%.2f,%.2f,%.2f)",
405 						 in_act->actor_name, in_act->actor_id,
406                          origin[0], origin[1], origin[2], in_target[0], in_target[1], in_target[2]);
407 
408 	tmp[0] = in_target[1] - origin[1];
409 	tmp[1] = in_target[2] - origin[2];
410 	tmp[2] = in_target[0] - origin[0];
411 	from[0] = 0.0;
412 	from[1] = 0.0;
413 	from[2] = 1.0;
414 
415 	to[0] = tmp[0] * sz - tmp[2] * cz;
416 	to[1] = 0.0;
417 	to[2] = tmp[0] * cz + tmp[2] * sz;
418 	Normalize(tmp, to);
419 	*out_h_rot = asinf(-tmp[0]);
420 
421 	missiles_log_message("%s (%d): horizontal rotation: from=(%.2f,%.2f,%.2f), to=(%.2f,%.2f,%.2f), h_rot=%f",
422 						 in_act->actor_name, in_act->actor_id,
423 						 from[0], from[1], from[2], tmp[0], tmp[1], tmp[2], *out_h_rot);
424 
425 	from[0] = tmp[0];
426 	from[1] = tmp[1];
427 	from[2] = tmp[2];
428 	to[1] = in_target[2] - origin[2];
429 	Normalize(to, to);
430 	VCross(tmp, from, to);
431 	*out_v_rot = asinf(sqrtf(tmp[0]*tmp[0] + tmp[1]*tmp[1] + tmp[2]*tmp[2]));
432 	if (to[1] < from[1]) *out_v_rot = -*out_v_rot;
433 
434 	missiles_log_message("%s (%d): vertical rotation: from=(%.2f,%.2f,%.2f), to=(%.2f,%.2f,%.2f), v_rot=%f",
435 						 in_act->actor_name, in_act->actor_id,
436 						 from[0], from[1], from[2], to[0], to[1], to[2], *out_v_rot);
437 /*#ifdef MORE_ATTACHED_ACTORS
438 	//no horses can be processed here
439 	if(in_act->attached_actor>=0) actor_rotation-=HORSE_FIGHT_ROTATION;
440 #endif*/
441 	return actor_rotation;
442 
443 }
444 
missiles_fire_arrow(actor * a,float target[3],MissileShotType shot_type)445 int missiles_fire_arrow(actor *a, float target[3], MissileShotType shot_type)
446 {
447 	int mis_id;
448 	float origin[3];
449 	float shift[3] = {0.0, get_actor_scale(a), 0.0};
450 	missile_type *mis_type;
451 	int mis_type_id;
452 
453 	mis_type_id = actors_defs[a->actor_type].shield[a->cur_shield].missile_type;
454 
455 	if (mis_type_id < 0 || mis_type_id >= MAX_MISSILES_DEFS) {
456 		LOG_ERROR("%d is not a valid missile type for shield %d of actor type %d\n", mis_type_id, a->cur_shield, a->actor_type);
457 		mis_type_id = 0;
458 	}
459 
460 	mis_type = &missiles_defs[mis_type_id];
461 
462 	shift[1] *= mis_type->length;
463 
464 	cal_get_actor_bone_absolute_position(a, get_actor_bone_id(a, arrow_bone), shift, origin);
465 
466 /* 	if (shot_type != MISSED_SHOT) */
467 	mis_id = missiles_add(mis_type_id, origin, target, 0.0, shot_type);
468 
469 	if(a->actor_id == yourself)
470 	{
471 		range_total_shots++;
472 		if (shot_type == MISSED_SHOT)
473 		{
474 			add_floating_message(a->actor_id, "miss", FLOATINGMESSAGE_NORTH, 1.0, 0.55, 0.0, 1250);
475 		}
476 		else if (shot_type == NORMAL_SHOT)
477 		{
478 			range_success_hits++;
479 		}
480 		else if(shot_type == CRITICAL_SHOT)
481 		{
482 			range_critical_hits++;
483 			range_success_hits++;
484 		}
485 	}
486 	/* 	else */
487 /* 		mis_id = missiles_add(a->cur_shield, origin, target, arrow_speed*2.0/3.0, arrow_trace_length*2.0/3.0, 0.0, shot_type); */
488 
489 	return mis_id;
490 }
491 
missiles_rotate_actor_bones(actor * a)492 void missiles_rotate_actor_bones(actor *a)
493 {
494 	struct CalSkeleton *skel;
495 	struct CalBone *bone;
496 	struct CalQuaternion *bone_rot, *bone_rot_abs, *hrot_quat, *vrot_quat;
497 	struct CalVector *vect;
498 	skeleton_types *skt = &skeletons_defs[actors_defs[a->actor_type].skeleton_type];
499 	float *tmp_vect;
500 	float hrot, vrot, tmp;
501 
502 	if (a->cal_rotation_blend < 0.0)
503 		return;
504 
505 	skel = CalModel_GetSkeleton(a->calmodel);
506 
507 	if (a->cal_rotation_blend < 1.0) {
508 		a->cal_rotation_blend += a->cal_rotation_speed*(cur_time-a->cal_last_rotation_time);
509 
510 		hrot = (a->cal_h_rot_start * (1.0 - a->cal_rotation_blend) +
511 				a->cal_h_rot_end * a->cal_rotation_blend);
512 		vrot = (a->cal_v_rot_start * (1.0 - a->cal_rotation_blend) +
513 				a->cal_v_rot_end * a->cal_rotation_blend);
514 	}
515 	else {
516 		if (fabs(a->cal_h_rot_end) < EPSILON && fabs(a->cal_v_rot_end) < EPSILON) {
517 			a->cal_rotation_blend = -1.0; // stop rotating bones every frames
518 			a->cal_h_rot_start = 0.0;
519 			a->cal_v_rot_start = 0.0;
520 			missiles_log_message("%s (%d): stopping bones rotation",
521                                  a->actor_name, a->actor_id);
522 		}
523 		else
524 			a->cal_rotation_blend = 1.0;
525 
526 		hrot = a->cal_h_rot_end;
527 		vrot = a->cal_v_rot_end;
528 
529 		if (a->are_bones_rotating) {
530 			a->are_bones_rotating = 0;
531 			if (a->cur_anim.anim_index >= 0 &&
532 				a->anim_time >= a->cur_anim.duration)
533 				a->busy = 0;
534 		}
535 	}
536 
537 	vect = CalVector_New();
538 
539 	hrot_quat = CalQuaternion_New();
540 	vrot_quat = CalQuaternion_New();
541 
542 	// get the rotation of the parent bone
543 	bone = CalSkeleton_GetBone(skel, 0);
544 	bone_rot_abs = CalBone_GetRotationAbsolute(bone);
545 
546 	// getting the chest bone to rotate
547 	bone = CalSkeleton_GetBone(skel, skt->cal_bones_id[body_bottom_bone]);
548 	bone_rot = CalBone_GetRotation(bone);
549 
550 	// rotating the bone horizontally
551 	CalVector_Set(vect, 0.0, 0.0, 1.0);
552 	CalQuaternion_Invert(bone_rot_abs);
553 	CalVector_Transform(vect, bone_rot_abs);
554 	CalQuaternion_Invert(bone_rot_abs);
555 	tmp_vect = CalVector_Get(vect);
556 	tmp = sinf(hrot/2.0);
557 	CalQuaternion_Set(hrot_quat, tmp_vect[0]*tmp, tmp_vect[1]*tmp, tmp_vect[2]*tmp, cosf(hrot/2.0));
558 	CalQuaternion_Multiply(bone_rot, hrot_quat);
559 
560 	// rotating the bone vertically
561 	CalVector_Set(vect, cosf(hrot), -sinf(hrot), 0.0);
562 	CalQuaternion_Invert(bone_rot_abs);
563 	CalVector_Transform(vect, bone_rot_abs);
564 	CalQuaternion_Invert(bone_rot_abs);
565 	tmp_vect = CalVector_Get(vect);
566 	tmp = sinf(vrot/2.0);
567 	CalQuaternion_Set(vrot_quat, tmp_vect[0]*tmp, tmp_vect[1]*tmp, tmp_vect[2]*tmp, cosf(vrot/2.0));
568 	CalQuaternion_Multiply(bone_rot, vrot_quat);
569 
570 	// updating the bone state
571 	CalBone_CalculateState(bone);
572 
573 	// rotating the cape bones
574 	hrot = -hrot;
575 	vrot = -vrot;
576 
577 	// rotating the bone horizontally
578 	if (hrot > 0.0) {
579 		bone = CalSkeleton_GetBone(skel, skt->cal_bones_id[cape_top_bone]);
580 		CalQuaternion_Set(hrot_quat, 0.0, sinf(hrot/2.0), 0.0, cosf(hrot/2.0));
581 		bone_rot = CalBone_GetRotation(bone);
582 		CalQuaternion_Multiply(bone_rot, hrot_quat);
583 		CalBone_CalculateState(bone);
584 	}
585 
586 	// rotating the bone vertically
587 	if (vrot < 0.0) {
588 		bone = CalSkeleton_GetBone(skel, skt->cal_bones_id[body_top_bone]);
589 		bone_rot_abs = CalBone_GetRotationAbsolute(bone);
590 		bone = CalSkeleton_GetBone(skel, skt->cal_bones_id[cape_top_bone]);
591 		bone_rot = CalBone_GetRotation(bone);
592 	}
593 	else {
594 		bone = CalSkeleton_GetBone(skel, skt->cal_bones_id[cape_top_bone]);
595 		bone_rot_abs = CalBone_GetRotationAbsolute(bone);
596 		bone = CalSkeleton_GetBone(skel, skt->cal_bones_id[cape_middle_bone]);
597 		bone_rot = CalBone_GetRotation(bone);
598 	}
599 
600 	CalVector_Set(vect, cosf(hrot), sinf(hrot), 0.0);
601 	CalQuaternion_Invert(bone_rot_abs);
602 	CalVector_Transform(vect, bone_rot_abs);
603 	CalQuaternion_Invert(bone_rot_abs);
604 	tmp_vect = CalVector_Get(vect);
605 	tmp = sinf(vrot/2.0);
606 	CalQuaternion_Set(vrot_quat, tmp_vect[0]*tmp, tmp_vect[1]*tmp, tmp_vect[2]*tmp, cosf(vrot/2.0));
607 	CalQuaternion_Multiply(bone_rot, vrot_quat);
608 	CalBone_CalculateState(bone);
609 
610 	CalVector_Delete(vect);
611 	CalQuaternion_Delete(hrot_quat);
612 	CalQuaternion_Delete(vrot_quat);
613 
614     a->cal_last_rotation_time = cur_time;
615 }
616 
missiles_test_target_validity(float target[3],char * msg)617 void missiles_test_target_validity(float target[3], char *msg)
618 {
619 	if (target[0] < 0.0 || target[0] > tile_map_size_x*3.0 ||
620 		target[1] < 0.0 || target[1] > tile_map_size_y*3.0)
621 		LOG_ERROR("%s: target (%f,%f,%f) is out of the map!",
622 				  msg, target[0], target[1], target[2]);
623 }
624 
missiles_clean_range_actions_queue(actor * act)625 void missiles_clean_range_actions_queue(actor *act)
626 {
627 	int j = 0;
628 
629 	// we search the first action that is not finished
630 	while (j < act->range_actions_count &&
631 		   (act->range_actions[j].state == 1 ||
632 			act->range_actions[j].state == 3)) {
633 		++j;
634 	}
635 
636 	// we then remove all the finished actions
637 	if (j > 0) {
638         int i;
639 		for (i = j; i < act->range_actions_count; ++i) {
640 			memcpy(&act->range_actions[i-j], &act->range_actions[i], sizeof(range_action));
641 		}
642 		act->range_actions_count -= j;
643 		missiles_log_message("%s (%d): removing %d actions from the queue",
644 							 act->actor_name, act->actor_id, j);
645 	}
646 }
647 
missiles_aim_at_b(int actor1_id,int actor2_id)648 void missiles_aim_at_b(int actor1_id, int actor2_id)
649 {
650 	actor *act1, *act2;
651 
652 	LOCK_ACTORS_LISTS();
653 	act1 = get_actor_ptr_from_id(actor1_id);
654 	act2 = get_actor_ptr_from_id(actor2_id);
655 
656 	if (!act1) {
657 		LOG_ERROR("the actor %d does not exists!", actor1_id);
658 		UNLOCK_ACTORS_LISTS();
659 		return;
660 	}
661 	if (!act2) {
662 		LOG_ERROR("the actor %d does not exists!", actor2_id);
663 		UNLOCK_ACTORS_LISTS();
664 		return;
665 	}
666 
667 	missiles_log_message("%s (%d): cleaning the queue from missiles_aim_at_b",
668 						 act1->actor_name, actor1_id);
669 	missiles_clean_range_actions_queue(act1);
670 
671 	if (act1->range_actions_count < MAX_RANGE_ACTION_QUEUE)	{
672 		range_action *action = &act1->range_actions[act1->range_actions_count];
673 
674 		missiles_log_message("%s (%d): will aim at actor %d (time=%d)", act1->actor_name, actor1_id, actor2_id, cur_time);
675 
676 		cal_get_actor_bone_absolute_position(act2, get_actor_bone_id(act2, body_top_bone), NULL, action->aim_position);
677 		missiles_test_target_validity(action->aim_position, "missiles_aim_at_b");
678 
679 		action->aim_actor = actor2_id;
680 		action->fire_actor = -1;
681 		action->shot_type = NORMAL_SHOT;
682 		action->reload = 0;
683 		action->state = 0;
684 		++act1->range_actions_count;
685 		UNLOCK_ACTORS_LISTS();
686 
687 		add_command_to_actor(actor1_id, enter_aim_mode);
688 	}
689 	else	{
690 		LOG_ERROR("%s (%d): unable to add a new aim action, the queue is full!",
691 				  act1->actor_name, actor1_id);
692 		UNLOCK_ACTORS_LISTS();
693 	}
694 }
695 
missiles_aim_at_xyz(int actor_id,float * target)696 void missiles_aim_at_xyz(int actor_id, float *target)
697 {
698 	actor *act;
699 
700 	LOCK_ACTORS_LISTS();
701 	act = get_actor_ptr_from_id(actor_id);
702 
703 	if (!act) {
704 		LOG_ERROR("the actor %d does not exists!", actor_id);
705 		UNLOCK_ACTORS_LISTS();
706 		return;
707 	}
708 
709 	missiles_log_message("%s (%d): cleaning the queue from missiles_aim_at_xyz",
710 						 act->actor_name, actor_id);
711 	missiles_clean_range_actions_queue(act);
712 
713 	if (act->range_actions_count < MAX_RANGE_ACTION_QUEUE) {
714 		range_action *action = &act->range_actions[act->range_actions_count];
715 
716 		missiles_log_message("%s (%d): will aim at target %f,%f,%f (time=%d)", act->actor_name, actor_id, target[0], target[1], target[2], cur_time);
717 
718 		memcpy(action->aim_position, target, sizeof(float) * 3);
719 		missiles_test_target_validity(action->aim_position, "missiles_aim_at_xyz");
720 
721 		action->aim_actor = -1;
722 		action->fire_actor = -1;
723 		action->shot_type = NORMAL_SHOT;
724 		action->reload = 0;
725 		action->state = 0;
726 		++act->range_actions_count;
727 		UNLOCK_ACTORS_LISTS();
728 
729 		add_command_to_actor(actor_id, enter_aim_mode);
730 	}
731 	else {
732 		LOG_ERROR("%s (%d): unable to add a new aim action, the queue is full!",
733 				  act->actor_name, actor_id);
734 		UNLOCK_ACTORS_LISTS();
735 	}
736 }
737 
missiles_fire_a_to_b(int actor1_id,int actor2_id)738 void missiles_fire_a_to_b(int actor1_id, int actor2_id)
739 {
740 	actor *act1, *act2;
741 
742 	LOCK_ACTORS_LISTS();
743 	act1 = get_actor_ptr_from_id(actor1_id);
744 	act2 = get_actor_ptr_from_id(actor2_id);
745 
746 	if (!act1) {
747 		LOG_ERROR("missiles_fire_a_to_b: the actor %d does not exists!", actor1_id);
748 		UNLOCK_ACTORS_LISTS();
749 		return;
750 	}
751 	if (!act2) {
752 		LOG_ERROR("missiles_fire_a_to_b: the actor %d does not exists!", actor2_id);
753 		UNLOCK_ACTORS_LISTS();
754 		return;
755 	}
756 
757 	if (act1->range_actions_count <= MAX_RANGE_ACTION_QUEUE &&
758 		act1->range_actions_count > 0) {
759 		range_action *action = &act1->range_actions[act1->range_actions_count-1];
760 
761 		missiles_log_message("%s (%d): will fire to actor %d", act1->actor_name, actor1_id, actor2_id);
762 
763 		cal_get_actor_bone_absolute_position(act2, get_actor_bone_id(act2, body_top_bone), NULL, action->fire_position);
764 		missiles_test_target_validity(action->fire_position, "missiles_fire_a_to_b");
765 
766 		action->fire_actor = actor2_id;
767 		action->state = 2;
768 
769 		act2->last_range_attacker_id = actor1_id;
770 
771 		UNLOCK_ACTORS_LISTS();
772 
773 		add_command_to_actor(actor1_id, aim_mode_fire);
774 		if (act2->actor_type == 97)
775 		{
776 			add_command_to_actor(actor2_id, pain1);
777 		}
778 	}
779 	else {
780 		LOG_ERROR("%s (%d): unable to add a fire action, the queue is empty!",
781 				  act1->actor_name, actor1_id);
782 		UNLOCK_ACTORS_LISTS();
783 	}
784 }
785 
missiles_fire_a_to_xyz(int actor_id,float * target)786 void missiles_fire_a_to_xyz(int actor_id, float *target)
787 {
788 	actor *act;
789 
790 	LOCK_ACTORS_LISTS();
791 	act = get_actor_ptr_from_id(actor_id);
792 
793 	if (!act) {
794 		LOG_ERROR("missiles_fire_a_to_xyz: the actor %d does not exists!", actor_id);
795 		UNLOCK_ACTORS_LISTS();
796 		return;
797 	}
798 
799 	if (act->range_actions_count <= MAX_RANGE_ACTION_QUEUE &&
800 		act->range_actions_count > 0) {
801 		range_action *action = &act->range_actions[act->range_actions_count-1];
802 
803 		missiles_log_message("%s (%d): will fire to target %f,%f,%f", act->actor_name, actor_id, target[0], target[1], target[2]);
804 
805 		memcpy(action->fire_position, target, sizeof(float) * 3);
806 		missiles_test_target_validity(action->fire_position, "missiles_fire_a_to_xyz");
807 		action->fire_actor = -1;
808 		action->state = 2;
809 
810 		UNLOCK_ACTORS_LISTS();
811 
812 		add_command_to_actor(actor_id, aim_mode_fire);
813 	}
814 	else {
815 		LOG_ERROR("%s (%d): unable to add a fire action, the queue is empty!",
816 				  act->actor_name, actor_id);
817 		UNLOCK_ACTORS_LISTS();
818 	}
819 }
820 
missiles_fire_xyz_to_b(float * origin,int actor_id)821 void missiles_fire_xyz_to_b(float *origin, int actor_id)
822 {
823 	actor * act;
824 	float target[3];
825 
826 	missiles_log_message("missile was fired from %f,%f,%f to actor %d", origin[0], origin[1], origin[2], actor_id);
827 
828 	LOCK_ACTORS_LISTS();
829 	act = get_actor_ptr_from_id(actor_id);
830 
831 	if (!act) {
832 		LOG_ERROR("the actor %d does not exists!", actor_id);
833 		UNLOCK_ACTORS_LISTS();
834 		return;
835 	}
836 
837 	cal_get_actor_bone_absolute_position(act, get_actor_bone_id(act, body_top_bone), NULL, target);
838 	missiles_test_target_validity(target, "missiles_fire_xyz_to_b");
839 	act->last_range_attacker_id = -1;
840 	UNLOCK_ACTORS_LISTS();
841 
842 	// here, there's no way to know if the target is missed or not as we don't know the actor who fired!
843 	missiles_add(0, origin, target, 0.0, 0);
844 }
845 
missiles_parse_nodes(xmlNode * node)846 int missiles_parse_nodes(xmlNode *node)
847 {
848 	int mis_idx;
849 	missile_type *mis;
850 	xmlNode *item;
851 	int     ok = 1;
852 
853 	if(node == NULL || node->children == NULL) return 0;
854 
855 	mis_idx = get_int_property(node, "id");
856 
857 	if (mis_idx < 0 || mis_idx >= MAX_MISSILES_DEFS) {
858 		LOG_ERROR("missiles_parse_node: no ID found for node %s or ID out of range: id=%d\n", get_string_property(node, "type"), mis_idx);
859 		return 0;
860 	}
861 
862 	mis = &missiles_defs[mis_idx];
863 
864 	for(item=node->children; item; item=item->next) {
865 		if(item->type == XML_ELEMENT_NODE) {
866 			if(xmlStrcasecmp(item->name, (xmlChar*)"mesh") == 0) {
867 				get_string_value(mis->lost_mesh, sizeof(mis->lost_mesh), item);
868 			}
869 			else if(xmlStrcasecmp(item->name, (xmlChar*)"mesh_length") == 0) {
870 				mis->length = get_float_value(item);
871 			}
872 			else if(xmlStrcasecmp(item->name, (xmlChar*)"trace_length") == 0) {
873 				mis->trace_length = get_float_value(item);
874 			}
875 			else if(xmlStrcasecmp(item->name, (xmlChar*)"speed") == 0) {
876 				mis->speed = get_float_value(item);
877 			}
878 			else if(xmlStrcasecmp(item->name, (xmlChar*)"effect") == 0) {
879 				char effect_name[64];
880 				get_string_value(effect_name, sizeof(effect_name), item);
881 				if (!strcasecmp(effect_name, "none")) {
882 					mis->effect = REGULAR_MISSILE;
883 				}
884 				else if (!strcasecmp(effect_name, "magic")) {
885 					mis->effect = MAGIC_MISSILE;
886 				}
887 				else if (!strcasecmp(effect_name, "fire")) {
888 					mis->effect = FIRE_MISSILE;
889 				}
890 				else if (!strcasecmp(effect_name, "ice")) {
891 					mis->effect = ICE_MISSILE;
892 				}
893 				else if (!strcasecmp(effect_name, "explosive")) {
894 					mis->effect = EXPLOSIVE_MISSILE;
895 				}
896 				else {
897 					mis->effect = REGULAR_MISSILE;
898 					LOG_ERROR("missiles_parse_node: \"%s\" is an unknown effect", effect_name);
899 				}
900 			}
901 			else {
902 				LOG_ERROR("missiles_parse_node: unknown attribute \"%s\"", item->name);
903 				ok = 0;
904 			}
905 		}
906 		else if (item->type == XML_ENTITY_REF_NODE) {
907 			ok &= missiles_parse_nodes(item->children);
908 		}
909 	}
910 
911 	return ok;
912 }
913 
missiles_parse_defs(xmlNode * node)914 int missiles_parse_defs(xmlNode *node)
915 {
916 	xmlNode *def;
917 	int ok = 1;
918 
919 	for (def = node->children; def; def = def->next) {
920 		if (def->type == XML_ELEMENT_NODE)
921 			if (xmlStrcasecmp(def->name, (xmlChar*)"missile") == 0) {
922 				ok &= missiles_parse_nodes(def);
923 			} else {
924 				LOG_ERROR("parse error: missile or include expected");
925 				ok = 0;
926 			}
927 		else if (def->type == XML_ENTITY_REF_NODE) {
928 			ok &= missiles_parse_defs(def->children);
929 		}
930 	}
931 
932 	return ok;
933 }
934 
missiles_read_defs(const char * file_name)935 int missiles_read_defs(const char *file_name)
936 {
937 	xmlNode *root;
938 	xmlDoc *doc;
939 	int ok = 1;
940 
941 	doc = xmlReadFile(file_name, NULL, 0);
942 	if (doc == NULL) {
943 		LOG_ERROR("Unable to read missiles definition file %s", file_name);
944 		return 0;
945 	}
946 
947 	root = xmlDocGetRootElement(doc);
948 	if (root == NULL) {
949 		LOG_ERROR("Unable to parse missiles definition file %s", file_name);
950 		ok = 0;
951 	} else if (xmlStrcasecmp(root->name, (xmlChar*)"missiles") != 0) {
952 		LOG_ERROR("Unknown key \"%s\" (\"missiles\" expected).", root->name);
953 		ok = 0;
954 	} else {
955 		ok = missiles_parse_defs(root);
956 	}
957 
958 	xmlFreeDoc(doc);
959 	return ok;
960 }
961 
missiles_init_defs()962 void missiles_init_defs()
963 {
964 	// initialize the whole thing to zero
965 	memset(missiles_defs, 0, sizeof(missiles_defs));
966 
967 	missiles_read_defs("actor_defs/missile_defs.xml");
968 }
969 
970 /**********************************************/
971 /*! Ranging win */
972 /**********************************************/
973 
974 static int range_win_x_len = 10;
975 static int range_win_y_len = 20;
976 static int print_to_console = 0;
977 static int result_x = 0;
978 
display_range_handler(window_info * win)979 int display_range_handler(window_info *win)
980 {
981 	char str[50];
982 	const int margin = 5 * win->current_scale;
983 	const int step_y = get_line_height(win->font_category, win->current_scale_small);
984 	const int pos_x = margin;
985 	int pos_y = margin;
986 
987 	if (print_to_console)
988 	{
989 		int len, max_len = strlen(ranging_total_shots_str);
990 		if ((len = strlen(ranging_sucessful_shots_str)) > max_len) max_len = len;
991 		if ((len = strlen(ranging_missed_shots_str)) > max_len) max_len = len;
992 		if ((len = strlen(ranging_success_rate_str)) > max_len) max_len = len;
993 		if ((len = strlen(ranging_critical_rate_str)) > max_len) max_len = len;
994 		if ((len = strlen(ranging_exp_per_arrow_str)) > max_len) max_len = len;
995 
996 		LOG_TO_CONSOLE(c_green2, ranging_win_title_str);
997 		safe_snprintf(str, sizeof(str), "%-*s %d", max_len, ranging_total_shots_str,
998 			range_total_shots);
999 		LOG_TO_CONSOLE(c_green1, str);
1000 		safe_snprintf(str, sizeof(str), "%-*s %d", max_len, ranging_sucessful_shots_str,
1001 			range_success_hits);
1002 		LOG_TO_CONSOLE(c_green1, str);
1003 		safe_snprintf(str, sizeof(str), "%-*s %d", max_len, ranging_missed_shots_str,
1004 			range_total_shots - range_success_hits);
1005 		LOG_TO_CONSOLE(c_green1, str);
1006 		safe_snprintf(str, sizeof(str), "%-*s %.2f %%", max_len, ranging_success_rate_str,
1007 			range_success_hits > 0 ? (float)range_success_hits/range_total_shots*100 : 0.0f);
1008 		LOG_TO_CONSOLE(c_green1, str);
1009 		safe_snprintf(str, sizeof(str), "%-*s %.2f %%", max_len, ranging_critical_rate_str,
1010 			range_critical_hits > 0 ? (float)range_critical_hits/range_success_hits*100 : 0.0f);
1011 		LOG_TO_CONSOLE(c_green1, str);
1012 		safe_snprintf(str, sizeof(str), "%-*s %.2f exp", max_len, ranging_exp_per_arrow_str,
1013 			range_total_shots > 0 ? (float)get_session_exp_ranging()/range_total_shots : 0.0f);
1014 		LOG_TO_CONSOLE(c_green1, str);
1015 
1016 		print_to_console = 0;
1017 	}
1018 
1019 	draw_string_small_zoomed(pos_x, pos_y, (const unsigned char*)ranging_total_shots_str,
1020 		1, win->current_scale);
1021 	safe_snprintf(str, sizeof(str), "%d", range_total_shots);
1022 	draw_string_small_zoomed(result_x, pos_y, (const unsigned char*)str, 1, win->current_scale);
1023 	pos_y += step_y;
1024 
1025 	draw_string_small_zoomed(pos_x, pos_y, (const unsigned char*)ranging_sucessful_shots_str,
1026 		1, win->current_scale);
1027 	safe_snprintf(str, sizeof(str), "%d", range_success_hits);
1028 	draw_string_small_zoomed(result_x, pos_y, (const unsigned char*)str, 1, win->current_scale);
1029 	pos_y += step_y;
1030 
1031 	draw_string_small_zoomed(pos_x, pos_y, (const unsigned char*)ranging_missed_shots_str,
1032 		1, win->current_scale);
1033 	safe_snprintf(str, sizeof(str), "%d", range_total_shots - range_success_hits);
1034 	draw_string_small_zoomed(result_x, pos_y, (const unsigned char*)str, 1, win->current_scale);
1035 	pos_y += 2*step_y;
1036 
1037 	draw_string_small_zoomed(pos_x, pos_y, (const unsigned char*)ranging_success_rate_str,
1038 		1, win->current_scale);
1039 	safe_snprintf(str, sizeof(str), "%.2f %%",
1040 		range_success_hits > 0 ? (float)range_success_hits/range_total_shots*100 : 0.0f);
1041 	draw_string_small_zoomed(result_x, pos_y, (const unsigned char*)str, 1, win->current_scale);
1042 	pos_y += step_y;
1043 
1044 	draw_string_small_zoomed(pos_x, pos_y, (const unsigned char*)ranging_critical_rate_str,
1045 		1, win->current_scale);
1046 	safe_snprintf(str, sizeof(str), "%.2f %%", ranging_critical_rate_str,
1047 		range_critical_hits > 0 ? (float)range_critical_hits/range_success_hits*100 : 0.0f);
1048 	draw_string_small_zoomed(result_x, pos_y, (const unsigned char*)str, 1, win->current_scale);
1049 	pos_y += 2*step_y;
1050 
1051 	// % .2f exp
1052 	draw_string_small_zoomed(pos_x, pos_y, (const unsigned char*)ranging_exp_per_arrow_str,
1053 		2, win->current_scale);
1054 	safe_snprintf(str, sizeof(str), "%.2f exp",
1055 		range_total_shots > 0 ? (float)get_session_exp_ranging()/range_total_shots : 0.0f);
1056 	draw_string_small_zoomed(result_x, pos_y, (const unsigned char*)str, 1, win->current_scale);
1057 
1058 	return 1;
1059 }
1060 
cm_ranging_handler(window_info * win,int widget_id,int mx,int my,int option)1061 static int cm_ranging_handler(window_info *win, int widget_id, int mx, int my, int option)
1062 {
1063 	if (option<ELW_CM_MENU_LEN)
1064 		return cm_title_handler(win, widget_id, mx, my, option);
1065 	if (option == ELW_CM_MENU_LEN+1)
1066 		print_to_console = 1;
1067 	return 1;
1068 }
1069 
set_range_window_size(window_info * win)1070 static int set_range_window_size(window_info *win)
1071 {
1072 	static const char* labels[6] = {
1073 		ranging_total_shots_str,
1074 		ranging_sucessful_shots_str,
1075 		ranging_missed_shots_str,
1076 		ranging_success_rate_str,
1077 		ranging_critical_rate_str,
1078 		ranging_exp_per_arrow_str,
1079 	};
1080 
1081 	float zoom = win->current_scale_small;
1082 	int lbl_max_width = 0;
1083 	int exp_max_width = get_string_width_zoom((const unsigned char*)"100.00 exp",
1084 		win->font_category, zoom);
1085 	int sep_width = 2 * get_char_width_zoom('M', win->font_category, zoom);
1086 	int margin = 5 * win->current_scale;
1087 	int line_height = get_line_height(win->font_category, zoom);
1088 	int len_x, len_y;
1089 	int i;
1090 
1091 	for (i = 0; i < 6; ++i)
1092 	{
1093 		int lbl_width = get_string_width_zoom((const unsigned char*)labels[i],
1094 			win->font_category, zoom);
1095 		if (lbl_width > lbl_max_width)
1096 			lbl_max_width = lbl_width;
1097 	}
1098 
1099 	result_x = margin + lbl_max_width + sep_width;
1100 
1101 	len_x = lbl_max_width + sep_width + exp_max_width + 2 * margin + win->box_size;
1102 	len_y = 8 * line_height + 2 * margin;
1103 	resize_window(win->window_id, len_x, len_y);
1104 
1105 	return 1;
1106 }
1107 
change_range_font_handler(window_info * win,font_cat cat)1108 int change_range_font_handler(window_info* win, font_cat cat)
1109 {
1110 	if (cat != UI_FONT)
1111 		return 0;
1112 	set_range_window_size(win);
1113 	return 1;
1114 }
1115 
display_range_win(void)1116 void display_range_win(void)
1117 {
1118 	int range_win = get_id_MW(MW_RANGING);
1119 	if(range_win < 0){
1120 		range_win = create_window(ranging_win_title_str, (not_on_top_now(MW_RANGING) ?game_root_win : -1), 0, get_pos_x_MW(MW_RANGING), get_pos_y_MW(MW_RANGING),
1121 			range_win_x_len, range_win_y_len, ELW_USE_UISCALE|ELW_WIN_DEFAULT);
1122 		set_id_MW(MW_RANGING, range_win);
1123 		set_window_custom_scale(range_win, MW_RANGING);
1124 		set_window_handler(range_win, ELW_HANDLER_DISPLAY, &display_range_handler);
1125 		set_window_handler(range_win, ELW_HANDLER_UI_SCALE, &set_range_window_size);
1126 		set_window_handler(range_win, ELW_HANDLER_FONT_CHANGE, &change_range_font_handler);
1127 		cm_add(windows_list.window[range_win].cm_id, cm_ranging_menu_str, cm_ranging_handler);
1128 		set_range_window_size(&windows_list.window[range_win]);
1129 		check_proportional_move(MW_RANGING);
1130 	}
1131 	else
1132 	{
1133 		show_window(range_win);
1134 		select_window(range_win);
1135 	}
1136 }
1137