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