1
2 #include <stdlib.h>
3
4 #include "core/vmath.h"
5 #include "core/obb.h"
6 #include "core/system.h"
7 #include "core/console.h"
8 #include "core/polygon.h"
9 #include "core/obb.h"
10 #include "render/render.h"
11 #include "script/script.h"
12 #include "physics/ragdoll.h"
13
14 #include "vt/tr_versions.h"
15 #include "audio/audio.h"
16 #include "room.h"
17 #include "world.h"
18 #include "character_controller.h"
19 #include "engine.h"
20 #include "entity.h"
21 #include "skeletal_model.h"
22 #include "resource.h"
23 #include "engine_string.h"
24 #include "game.h"
25 #include "controls.h"
26 #include "mesh.h"
27
28 void Character_CollisionCallback(struct entity_s *ent, struct collision_node_s *cn);
29 void Character_FixByBox(struct entity_s *ent);
30
Character_Create(struct entity_s * ent)31 void Character_Create(struct entity_s *ent)
32 {
33 if(ent && !ent->character)
34 {
35 character_p ret;
36 const collision_result_t zero_result = {0};
37
38 ret = (character_p)malloc(sizeof(character_t));
39 ret->state_func = NULL;
40 ret->set_key_anim_func = NULL;
41 ret->set_weapon_model_func = NULL;
42 ret->ent = ent;
43 ent->character = ret;
44 ret->height_info.self = ent->self;
45 ent->dir_flag = ENT_STAY;
46 ent->no_anim_pos_autocorrection = 0x00;
47
48 ret->target_id = ENTITY_ID_NONE;
49 ret->hair_count = 0;
50 ret->path_dist = 0;
51 ret->path[0] = (ent->self->sector) ? (ent->self->sector->box) : (NULL);
52 ret->path_target = NULL;
53 ret->hairs = NULL;
54 ret->ragdoll = NULL;
55 ret->ai_zone = 0;
56 ret->ai_zone_type = ZONE_TYPE_ALL;
57
58 ret->bone_head = 0x00;
59 ret->bone_torso = 0x00;
60 ret->bone_l_hand_start = 0x00;
61 ret->bone_l_hand_end = 0x00;
62 ret->bone_r_hand_start = 0x00;
63 ret->bone_r_hand_end = 0x00;
64 ret->weapon_state = 0x00;
65 ret->weapon_id = 0;
66
67 ret->state.floor_collide = 0x00;
68 ret->state.ceiling_collide = 0x00;
69 ret->state.wall_collide = 0x00;
70 ret->state.step_z = 0x00;
71 ret->state.slide = 0x00;
72 ret->state.uw_current = 0x00;
73 ret->state.attack = 0x00;
74 ret->state.dead = 0x00;
75 ret->state.ragdoll = 0x00;
76 ret->state.burn = 0x00;
77 ret->state.crouch = 0x00;
78 ret->state.sprint = 0x00;
79 ret->state.tightrope = 0x00;
80 ret->state.can_attack = 0x00;
81
82 ret->cmd.action = 0x00;
83 ret->cmd.crouch = 0x00;
84 ret->cmd.flags = 0x00;
85 ret->cmd.jump = 0x00;
86 ret->cmd.roll = 0x00;
87 ret->cmd.shift = 0x00;
88 vec3_set_zero(ret->cmd.move);
89 vec3_set_zero(ret->cmd.rot);
90
91 ret->cam_follow_center = 0x00;
92 ret->linear_speed_mult = DEFAULT_CHARACTER_SPEED_MULT;
93 ret->rotate_speed_mult = 1.0f;
94 ret->min_step_up_height = DEFAULT_MIN_STEP_UP_HEIGHT;
95 ret->max_climb_height = DEFAULT_CLIMB_UP_HEIGHT;
96 ret->max_step_up_height = DEFAULT_MAX_STEP_UP_HEIGHT;
97 ret->fall_down_height = DEFAULT_FALL_DOWN_HEIGHT;
98 ret->critical_slant_z_component = DEFAULT_CRITICAL_SLANT_Z_COMPONENT;
99 ret->critical_wall_component = DEFAULT_CRITICAL_WALL_COMPONENT;
100 ret->climb_r = DEFAULT_CHARACTER_CLIMB_R;
101 ret->wade_depth = DEFAULT_CHARACTER_WADE_DEPTH;
102 ret->swim_depth = DEFAULT_CHARACTER_SWIM_DEPTH;
103
104 for(int i = 0; i < PARAM_LASTINDEX; i++)
105 {
106 ret->parameters.param[i] = 0.0;
107 ret->parameters.maximum[i] = 0.0;
108 }
109 ret->parameters.maximum[PARAM_HIT_DAMAGE] = 9999.0;
110
111 ret->sphere = CHARACTER_BASE_RADIUS;
112 ret->climb_sensor = ent->character->climb_r;
113 ret->height_info.ceiling_hit = zero_result;
114 ret->height_info.floor_hit = zero_result;
115 ret->height_info.water = 0x00;
116
117 ret->climb.edge_obj = NULL;
118 ret->climb.edge_z_ang = 0.0f;
119 ret->climb.can_hang = 0x00;
120 ret->climb.next_z_space = 0.0;
121 ret->climb.height_info = 0x00;
122 ret->climb.edge_hit = 0x00;
123 ret->climb.wall_hit = 0x00;
124 ret->forvard_size = 48.0; ///@FIXME: magick number
125 ret->height = CHARACTER_BASE_HEIGHT;
126
127 ret->traversed_object = NULL;
128
129 ent->self->collision_group = COLLISION_GROUP_CHARACTERS;
130 ent->self->collision_mask = COLLISION_GROUP_STATIC_ROOM | COLLISION_GROUP_STATIC_OBLECT | COLLISION_GROUP_KINEMATIC |
131 COLLISION_GROUP_CHARACTERS | COLLISION_GROUP_DYNAMICS | COLLISION_GROUP_DYNAMICS_NI | COLLISION_GROUP_TRIGGERS;
132 Physics_SetCollisionGroupAndMask(ent->physics, ent->self->collision_group, ent->self->collision_mask);
133 Physics_CreateGhosts(ent->physics, ent->bf, NULL);
134 Entity_GhostUpdate(ent);
135 }
136 }
137
Character_Delete(struct entity_s * ent)138 void Character_Delete(struct entity_s *ent)
139 {
140 character_p actor = ent->character;
141
142 if(actor)
143 {
144 actor->path_dist = 0;
145 actor->path[0] = NULL;
146 actor->path_target = NULL;
147 actor->ent = NULL;
148 if(actor->hairs)
149 {
150 for(int i = 0; i < actor->hair_count; i++)
151 {
152 Hair_Delete(actor->hairs[i]);
153 actor->hairs[i] = NULL;
154 }
155 free(actor->hairs);
156 actor->hairs = NULL;
157 actor->hair_count = 0;
158 }
159
160 if(actor->ragdoll)
161 {
162 Ragdoll_DeleteSetup(actor->ragdoll);
163 actor->ragdoll = NULL;
164 }
165
166 actor->height_info.water = 0x00;
167 actor->climb.edge_hit = 0x00;
168
169 free(ent->character);
170 ent->character = NULL;
171 }
172 }
173
174
Character_Update(struct entity_s * ent)175 void Character_Update(struct entity_s *ent)
176 {
177 const uint16_t mask = ENTITY_STATE_ENABLED | ENTITY_STATE_ACTIVE;
178 if(mask == (ent->state_flags & mask))
179 {
180 bool is_player = (World_GetPlayer() == ent);
181 if(ent->character->cmd.action && (ent->type_flags & ENTITY_TYPE_TRIGGER_ACTIVATOR) &&
182 (ent->character->weapon_state == WEAPON_STATE_HIDE))
183 {
184 Entity_CheckActivators(ent);
185 }
186
187 if(ent->character->state.dead)
188 {
189 memset(&ent->character->cmd, 0x00, sizeof(ent->character->cmd));
190 Character_ClearLookAt(ent);
191 if(is_player) // Stop any music, if Lara is dead.
192 {
193 Audio_EndStreams(TR_AUDIO_STREAM_TYPE_ONESHOT);
194 Audio_EndStreams(TR_AUDIO_STREAM_TYPE_CHAT);
195 }
196 }
197 else if(Character_GetParam(ent, PARAM_HEALTH) <= 0.0f)
198 {
199 ent->character->state.dead = 0x01; // Kill, if no HP.
200 }
201
202 if(!is_player && !ent->character->state.dead && (ent->character->ai_zone >= 0))
203 {
204 Character_UpdateAI(ent);
205 }
206 Character_ApplyCommands(ent);
207
208 Entity_ProcessSector(ent);
209 Character_UpdateParams(ent);
210 Entity_CheckCollisionCallbacks(ent);
211
212 if(!is_player && !ent->character->state.dead && (ent->character->ai_zone >= 0))
213 {
214 Character_FixByBox(ent);
215 }
216
217 for(int h = 0; h < ent->character->hair_count; h++)
218 {
219 Hair_Update(ent->character->hairs[h], ent->physics);
220 }
221
222 if(ent->character->state.ragdoll && ent->character->ragdoll &&
223 !(ent->type_flags & ENTITY_TYPE_DYNAMIC) &&
224 Ragdoll_Create(ent->physics, ent->bf, ent->character->ragdoll))
225 {
226 ent->type_flags |= ENTITY_TYPE_DYNAMIC;
227 }
228 }
229 }
230
231
Character_UpdatePath(struct entity_s * ent,struct room_sector_s * target)232 void Character_UpdatePath(struct entity_s *ent, struct room_sector_s *target)
233 {
234 if(ent->character && ent->self->sector && ent->self->sector->box && target && target->box)
235 {
236 const int buf_size = sizeof(room_box_p) * World_GetRoomBoxesCount();
237 room_box_p *path = (room_box_p*)Sys_GetTempMem(buf_size);
238 box_validition_options_t op;
239 op.zone = ent->character->ai_zone;
240 op.zone_type = (ent->move_type == MOVE_FLY) ? (ZONE_TYPE_FLY) : (ent->character->ai_zone_type);
241 op.zone_alt = ent->self->room->is_swapped;
242 op.step_up = (ent->character->max_step_up_height > ent->character->max_climb_height) ? (ent->character->max_step_up_height) : (ent->character->max_climb_height);
243 op.step_down = ent->character->fall_down_height;
244 int dist = Room_FindPath(path, World_GetRoomBoxesCount(), ent->self->sector, target, &op);
245 const int max_dist = sizeof(ent->character->path) / sizeof(ent->character->path[0]);
246 ent->character->path_dist = (dist > max_dist) ? (max_dist) : dist;
247
248 for(int i = 0; i < ent->character->path_dist; ++i)
249 {
250 ent->character->path[i] = path[dist - i - 1];
251 }
252
253 Sys_ReturnTempMem(buf_size);
254 }
255 }
256
257
Character_FixByBox(struct entity_s * ent)258 void Character_FixByBox(struct entity_s *ent)
259 {
260 if(ent->character->path[0])
261 {
262 float r = ent->bf->bone_tags->mesh_base->radius;
263 room_box_p curr_box = ent->character->path[0];
264 room_box_p next_box = (ent->character->path_dist > 1) ? (ent->character->path[1]) : (NULL);
265 int32_t fix_x = 0;
266 int32_t fix_y = 0;
267
268 if(ent->transform.M4x4[12 + 2] < curr_box->bb_min[2])
269 {
270 ent->transform.M4x4[12 + 2] = curr_box->bb_min[2];
271 }
272
273 if(ent->transform.M4x4[12 + 0] + r > curr_box->bb_max[0])
274 {
275 fix_x = curr_box->bb_max[0] - ent->transform.M4x4[12 + 0] - r;
276 }
277 else if(ent->transform.M4x4[12 + 0] - r < curr_box->bb_min[0])
278 {
279 fix_x = curr_box->bb_min[0] - ent->transform.M4x4[12 + 0] + r;
280 }
281
282 if(ent->transform.M4x4[12 + 1] + r > curr_box->bb_max[1])
283 {
284 fix_y = curr_box->bb_max[1] - ent->transform.M4x4[12 + 1] - r;
285 }
286 else if(ent->transform.M4x4[12 + 1] - r < curr_box->bb_min[1])
287 {
288 fix_y = curr_box->bb_min[1] - ent->transform.M4x4[12 + 1] + r;
289 }
290
291 if(fix_x && fix_y || !next_box)
292 {
293 ent->transform.M4x4[12 + 0] += fix_x;
294 ent->transform.M4x4[12 + 1] += fix_y;
295 }
296 else if(next_box && (fix_x || fix_y))
297 {
298 float min = (curr_box->bb_min[0] < next_box->bb_min[0]) ? (curr_box->bb_min[0]) : (next_box->bb_min[0]);
299 float max = (curr_box->bb_max[0] > next_box->bb_max[0]) ? (curr_box->bb_max[0]) : (next_box->bb_max[0]);
300 if(fix_x && ((ent->transform.M4x4[12 + 0] + r > max) || (ent->transform.M4x4[12 + 0] - r < min)))
301 {
302 ent->transform.M4x4[12 + 0] += fix_x;
303 }
304
305 min = (curr_box->bb_min[1] < next_box->bb_min[1]) ? (curr_box->bb_min[1]) : (next_box->bb_min[1]);
306 max = (curr_box->bb_max[1] > next_box->bb_max[1]) ? (curr_box->bb_max[1]) : (next_box->bb_max[1]);
307 if(fix_y && ((ent->transform.M4x4[12 + 1] + r > max) || (ent->transform.M4x4[12 + 1] - r < min)))
308 {
309 ent->transform.M4x4[12 + 1] += fix_y;
310 }
311 }
312 }
313 }
314
315
Character_GoByPathToTarget(struct entity_s * ent,struct entity_s * target)316 void Character_GoByPathToTarget(struct entity_s *ent, struct entity_s *target)
317 {
318 if(ent->self->sector && ent->self->sector->box &&
319 ent->character->path_target && (ent->character->path_dist > 0))
320 {
321 float dir[4];
322 ent->character->rotate_speed_mult = 1.0f;
323 ent->character->cmd.rot[0] = 0;
324 ent->character->cmd.move[0] = 0;
325 ent->character->cmd.move[1] = 0;
326 ent->character->cmd.move[2] = 0;
327 ent->character->cmd.shift = 0;
328
329 if(!target && (ent->character->path_target == ent->self->sector))
330 {
331 ent->character->path_target = NULL;
332 return;
333 }
334
335 if(ent->self->sector->box->id != ent->character->path[0]->id)
336 {
337 Character_UpdatePath(ent, ent->character->path_target);
338 if(ent->character->path_dist == 0)
339 {
340 return;
341 }
342 }
343
344 if((ent->character->path_dist > 1) && Room_IsInBox(ent->character->path[1], ent->transform.M4x4 + 12))
345 {
346 for(int i = 1; i < ent->character->path_dist; ++i)
347 {
348 ent->character->path[i - 1] = ent->character->path[i];
349 }
350 ent->character->path_dist--;
351 }
352
353 if(ent->character->path_dist == 1)
354 {
355 float *v = (target) ? (target->obb->centre) : (ent->character->path_target->pos);
356 vec3_copy(dir, v);
357 }
358 else
359 {
360 Room_GetOverlapCenter(ent->character->path[0], ent->character->path[1], dir);
361 if(vec3_dist_sq(dir, ent->transform.M4x4 + 12) < 0.25f * TR_METERING_SECTORSIZE * TR_METERING_SECTORSIZE)
362 {//FIX to 2d condition
363 if(ent->character->path_dist > 2)
364 {
365 Room_GetOverlapCenter(ent->character->path[1], ent->character->path[2], dir);
366 }
367 else
368 {
369 vec3_copy(dir, ent->character->path_target->pos);
370 }
371 }
372 }
373
374 vec3_sub(dir, dir, ent->transform.M4x4 + 12);
375 vec3_norm(dir, dir[3]);
376
377 float sin_a = dir[0] * ent->transform.M4x4[4 + 1] - dir[1] * ent->transform.M4x4[4 + 0];
378 float cos_a = dir[0] * ent->transform.M4x4[4 + 0] + dir[1] * ent->transform.M4x4[4 + 1];
379 float delta = fabs((180.0f / M_PI) * sin_a / cos_a);
380
381 ent->character->cmd.rot[0] = (sin_a >= 0.0f) ? (-1) : (1);
382 if((cos_a > 0.75) && (delta < 360.0f * engine_frame_time))
383 {
384 ent->character->rotate_speed_mult = delta / (360.0f * engine_frame_time);
385 }
386
387 if(ent->move_type == MOVE_FLY)
388 {
389 float target_z = ent->character->path_target->floor + 600.0f;
390 room_sector_p next_sector = Sector_GetNextSector(ent->self->sector, ent->transform.M4x4 + 4);
391 target_z = (target_z < next_sector->floor + TR_METERING_STEP) ? (next_sector->floor + TR_METERING_STEP) : target_z;
392 target_z = (target_z > next_sector->ceiling - TR_METERING_STEP) ? (next_sector->ceiling - TR_METERING_STEP) : target_z;
393 if(ent->transform.M4x4[12 + 2] < target_z - 64.0f)
394 {
395 ent->character->cmd.move[2] = 0x01;
396 }
397 else if(ent->transform.M4x4[12 + 2] > target_z + 64.0f)
398 {
399 ent->character->cmd.move[2] = -0x01;
400 }
401 }
402 else
403 {
404 //ent->character->cmd.shift = (dir[3] < 4096.0f);
405 }
406 ent->character->cmd.move[0] = (dir[3] > 32.0f) ? (0x01) : (0x00);
407 }
408 }
409
410
Character_UpdateAI(struct entity_s * ent)411 void Character_UpdateAI(struct entity_s *ent)
412 {
413 entity_p target = World_GetEntityByID(ent->character->target_id);
414 if(target)
415 {
416 if(target->character && target->character->state.dead)
417 {
418 ent->character->target_id = ENTITY_ID_NONE;
419 Character_LookAtTarget(ent, NULL);
420 return;
421 }
422 Character_LookAtTarget(ent, target);
423 if(target->self->sector && (ent->character->path_target != target->self->sector))
424 {
425 ent->character->path_target = target->self->sector;
426 Character_UpdatePath(ent, ent->character->path_target);
427 }
428 }
429
430 ent->character->cmd.action = (ent->character->state.can_attack) ? (0x01) : (0x00);
431 Character_GoByPathToTarget(ent, target);
432 }
433
434
Character_CollisionCallback(struct entity_s * ent,struct collision_node_s * cn)435 void Character_CollisionCallback(struct entity_s *ent, struct collision_node_s *cn)
436 {
437 for(; cn && cn->obj; cn = cn->next)
438 {
439 if(cn->obj->object_type == OBJECT_ENTITY)
440 {
441 entity_p trigger = (entity_p)cn->obj->object;
442 if(trigger->callback_flags & ENTITY_CALLBACK_COLLISION)
443 {
444 // Activator and entity IDs are swapped in case of collision callback.
445 Script_ExecEntity(engine_lua, ENTITY_CALLBACK_COLLISION, trigger->id, ent->id);
446 }
447 if(trigger->character && trigger->character->state.attack)
448 {
449 Script_EntityUpdateCollisionInfo(engine_lua, trigger->id, cn);
450 Script_ExecEntity(engine_lua, ENTITY_CALLBACK_ATTACK, trigger->id, ent->id);
451 }
452 }
453 }
454 }
455
456
457 /**
458 * Calculates character speed, based on direction flag and anim linear speed
459 * @param ent
460 */
Character_UpdateCurrentSpeed(struct entity_s * ent,int zeroVz)461 void Character_UpdateCurrentSpeed(struct entity_s *ent, int zeroVz)
462 {
463 float t = ent->anim_linear_speed;
464 float vz = (zeroVz) ? (0.0) : (ent->speed[2]);
465
466 t *= (ent->character) ? (ent->character->linear_speed_mult) : (DEFAULT_CHARACTER_SPEED_MULT);
467 if(ent->dir_flag & ENT_MOVE_FORWARD)
468 {
469 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, t);
470 }
471 else if(ent->dir_flag & ENT_MOVE_BACKWARD)
472 {
473 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4,-t);
474 }
475 else if(ent->dir_flag & ENT_MOVE_LEFT)
476 {
477 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0,-t);
478 }
479 else if(ent->dir_flag & ENT_MOVE_RIGHT)
480 {
481 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, t);
482 }
483 else
484 {
485 vec3_set_zero(ent->speed);
486 }
487
488 ent->speed[2] = vz;
489 }
490
491 /**
492 * Calculates next height info and information about next step
493 * @param ent
494 */
Character_UpdateCurrentHeight(struct entity_s * ent)495 void Character_UpdateCurrentHeight(struct entity_s *ent)
496 {
497 float from[3], *v;
498 height_info_p hi = &ent->character->height_info;
499
500 v = ent->bf->bone_tags[0].transform + 12;
501 Mat4_vec3_mul_macro(from, ent->transform.M4x4, v);
502 from[2] -= ent->speed[2] * engine_frame_time;
503 from[0] = ent->transform.M4x4[12 + 0];
504 from[1] = ent->transform.M4x4[12 + 1];
505 Character_GetHeightInfo(from, hi, ent->character->height);
506 }
507
508 /**
509 * Start position are taken from ent->transform.M4x4
510 */
Character_GetHeightInfo(float pos[3],struct height_info_s * fc,float v_offset)511 void Character_GetHeightInfo(float pos[3], struct height_info_s *fc, float v_offset)
512 {
513 float from[3], to[3];
514 room_p r = (fc->self) ? (fc->self->room) : (NULL);
515 room_sector_p rs;
516
517 fc->floor_hit.hit = 0x00;
518 fc->ceiling_hit.hit = 0x00;
519 fc->water = 0x00;
520 fc->quicksand = 0x00;
521 fc->transition_level = 32512.0;
522
523 r = World_FindRoomByPosCogerrence(pos, r);
524 if(r)
525 {
526 rs = Room_GetSectorXYZ(r, pos); // if r != NULL then rs can not been NULL!!!
527 if(r->content->room_flags & TR_ROOM_FLAG_WATER) // in water - go up
528 {
529 while(rs->room_above)
530 {
531 rs = Room_GetSectorRaw(rs->room_above->real_room, rs->pos);
532 if((rs->owner_room->content->room_flags & TR_ROOM_FLAG_WATER) == 0x00) // find air
533 {
534 fc->transition_level = (float)rs->floor;
535 fc->water = 0x01;
536 break;
537 }
538 }
539 }
540 else if(r->content->room_flags & TR_ROOM_FLAG_QUICKSAND)
541 {
542 while(rs->room_above)
543 {
544 rs = Room_GetSectorRaw(rs->room_above->real_room, rs->pos);
545 if((rs->owner_room->content->room_flags & TR_ROOM_FLAG_QUICKSAND) == 0x00) // find air
546 {
547 fc->transition_level = (float)rs->floor;
548 if(fc->transition_level - fc->floor_hit.point[2] > v_offset)
549 {
550 fc->quicksand = 0x02;
551 }
552 else
553 {
554 fc->quicksand = 0x01;
555 }
556 break;
557 }
558 }
559 }
560 else // in air - go down
561 {
562 while(rs->room_below)
563 {
564 rs = Room_GetSectorRaw(rs->room_below->real_room, rs->pos);
565 if((rs->owner_room->content->room_flags & TR_ROOM_FLAG_WATER) != 0x00) // find water
566 {
567 fc->transition_level = (float)rs->ceiling;
568 fc->water = 0x01;
569 break;
570 }
571 else if((rs->owner_room->content->room_flags & TR_ROOM_FLAG_QUICKSAND) != 0x00)// find water
572 {
573 fc->transition_level = (float)rs->ceiling;
574 if(fc->transition_level - fc->floor_hit.point[2] > v_offset)
575 {
576 fc->quicksand = 0x02;
577 }
578 else
579 {
580 fc->quicksand = 0x01;
581 }
582 break;
583 }
584 }
585 }
586 }
587
588 /*
589 * GET HEIGHTS
590 */
591 vec3_copy(from, pos);
592 to[0] = from[0];
593 to[1] = from[1];
594 to[2] = from[2] - 8192.0f;
595
596 Physics_RayTestFiltered(&fc->floor_hit, from ,to, fc->self, COLLISION_FILTER_HEIGHT_TEST);
597
598 to[2] = from[2] + 4096.0f;
599 Physics_RayTestFiltered(&fc->ceiling_hit, from ,to, fc->self, COLLISION_FILTER_HEIGHT_TEST);
600 }
601
602 /**
603 * @function calculates next floor info + fantom filter + returns step info.
604 * Current height info must be calculated!
605 */
Character_CheckNextStep(struct entity_s * ent,float offset[3],struct height_info_s * nfc)606 int Character_CheckNextStep(struct entity_s *ent, float offset[3], struct height_info_s *nfc)
607 {
608 float pos[3], from[3], to[3], delta;
609 height_info_p fc = &ent->character->height_info;
610 int ret = CHARACTER_STEP_HORIZONTAL;
611 ///penetration test?
612
613 vec3_add(pos, ent->transform.M4x4 + 12, offset);
614 Character_GetHeightInfo(pos, nfc);
615
616 if(fc->floor_hit.hit && nfc->floor_hit.hit)
617 {
618 delta = nfc->floor_hit.point[2] - fc->floor_hit.point[2];
619 if(fabs(delta) < SPLIT_EPSILON)
620 {
621 from[2] = fc->floor_hit.point[2];
622 ret = CHARACTER_STEP_HORIZONTAL; // horizontal
623 }
624 else if(delta < 0.0) // down way
625 {
626 delta = -delta;
627 from[2] = fc->floor_hit.point[2];
628 if(delta <= ent->character->min_step_up_height)
629 {
630 ret = CHARACTER_STEP_DOWN_LITTLE;
631 }
632 else if(delta <= ent->character->max_step_up_height)
633 {
634 ret = CHARACTER_STEP_DOWN_BIG;
635 }
636 else if(delta <= ent->character->height)
637 {
638 ret = CHARACTER_STEP_DOWN_DROP;
639 }
640 else
641 {
642 ret = CHARACTER_STEP_DOWN_CAN_HANG;
643 }
644 }
645 else // up way
646 {
647 from[2] = nfc->floor_hit.point[2];
648 if(delta <= ent->character->min_step_up_height)
649 {
650 ret = CHARACTER_STEP_UP_LITTLE;
651 }
652 else if(delta <= ent->character->max_step_up_height)
653 {
654 ret = CHARACTER_STEP_UP_BIG;
655 }
656 else if(delta <= ent->character->max_climb_height)
657 {
658 ret = CHARACTER_STEP_UP_CLIMB;
659 }
660 else
661 {
662 ret = CHARACTER_STEP_UP_IMPOSSIBLE;
663 }
664 }
665 }
666 else if(!fc->floor_hit.hit && !nfc->floor_hit.hit)
667 {
668 from[2] = pos[2];
669 ret = CHARACTER_STEP_HORIZONTAL; // horizontal? yes no maybe...
670 }
671 else if(!fc->floor_hit.hit && nfc->floor_hit.hit) // strange case
672 {
673 from[2] = nfc->floor_hit.point[2];
674 ret = 0x00;
675 }
676 else //if(fc->floor_hit && !nfc->floor_hit) // bottomless
677 {
678 from[2] = fc->floor_hit.point[2];
679 ret = CHARACTER_STEP_DOWN_CAN_HANG;
680 }
681
682 /*
683 * check walls! If test is positive, than CHARACTER_STEP_UP_IMPOSSIBLE - can not go next!
684 */
685 from[0] = ent->transform.M4x4[12 + 0];
686 from[1] = ent->transform.M4x4[12 + 1];
687 from[2] += ent->character->climb_r;
688
689 to[0] = pos[0];
690 to[1] = pos[1];
691 to[2] = from[2];
692
693 if(Physics_RayTest(NULL, from, to, fc->self, COLLISION_FILTER_HEIGHT_TEST))
694 {
695 ret = CHARACTER_STEP_UP_IMPOSSIBLE;
696 }
697
698 return ret;
699 }
700
701 /**
702 *
703 * @param ent - entity
704 * @param next_fc - next step floor / ceiling information
705 * @return 1 if character can't run / walk next; in other cases returns 0
706 */
Character_HasStopSlant(struct entity_s * ent,height_info_p next_fc)707 int Character_HasStopSlant(struct entity_s *ent, height_info_p next_fc)
708 {
709 float *pos = ent->transform.M4x4 + 12;
710 float *v1 = ent->transform.M4x4 + 4;
711 float *v2 = next_fc->floor_hit.normale;
712
713 return next_fc->floor_hit.hit &&
714 (next_fc->floor_hit.point[2] > pos[2]) &&
715 (next_fc->floor_hit.normale[2] < ent->character->critical_slant_z_component) &&
716 (v1[0] * v2[0] + v1[1] * v2[1] < 0.0f);
717 }
718
719
Character_GetMiddleHandsPos(const struct entity_s * ent,float pos[3])720 void Character_GetMiddleHandsPos(const struct entity_s *ent, float pos[3])
721 {
722 float temp[3];
723 const float *v1 = ent->bf->bone_tags[ent->character->bone_l_hand_end].full_transform + 12;
724 const float *v2 = ent->bf->bone_tags[ent->character->bone_r_hand_end].full_transform + 12;
725
726 temp[0] = 0.0f;
727 temp[1] = 0.5f * (v1[1] + v2[1]);
728 temp[2] = ((v1[2] > v2[2]) ? (v1[2]) : (v2[2]));
729 Mat4_vec3_mul_macro(pos, ent->transform.M4x4, temp);
730 }
731
732
733 /**
734 * @param ent - entity
735 * @param climb - returned climb information
736 * @param test_from - where we start check height (i.e. middle hands position)
737 * @param test_to - test area parameters (next pos xy, z_min)
738 *
739 * OZ ^
740 * *-------------* from
741 * | |
742 * | |
743 * | |
744 * to *-------------*
745 */
Character_CheckClimbability(struct entity_s * ent,struct climb_info_s * climb,float test_from[3],float test_to[3])746 void Character_CheckClimbability(struct entity_s *ent, struct climb_info_s *climb, float test_from[3], float test_to[3])
747 {
748 const float z_step = -0.66f * ent->character->climb_r;
749 float from[3], to[3];
750 float *pos = ent->transform.M4x4 + 12;
751 double n0[4], n1[4]; // planes equations
752 char up_founded = 0;
753 collision_result_t cb;
754 //const float color[3] = {1.0f, 0.0f, 0.0f};
755
756 if(ent->self->sector && ent->self->sector->room_above &&
757 ent->self->sector->room_above->bb_min[2] < test_from[2] + TR_METERING_STEP)
758 {
759 Entity_MoveToRoom(ent, ent->self->sector->room_above->real_room);
760 }
761
762 climb->height_info = CHARACTER_STEP_HORIZONTAL;
763 climb->can_hang = 0x00;
764 climb->edge_hit = 0x00;
765 climb->edge_obj = NULL;
766
767 to[0] = test_from[0];
768 to[1] = test_from[1];
769 to[2] = test_to[2];
770 if(Physics_RayTestFiltered(&cb, test_from, to, ent->self, COLLISION_FILTER_HEIGHT_TEST))
771 {
772 test_to[2] = cb.point[2];
773 }
774
775 from[0] = test_from[0];
776 from[1] = test_from[1];
777 from[2] = test_from[2];
778 to[0] = test_to[0];
779 to[1] = test_to[1];
780 to[2] = test_from[2];
781 //renderer.debugDrawer->DrawLine(from, to, color, color);
782 if(Physics_SphereTest(&cb, from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST))
783 {
784 // NEAR WALL CASE
785 if(cb.fraction > 0.0f)
786 {
787 uint8_t heavy_flag = ent->self->collision_heavy;
788 climb->edge_obj = cb.obj;
789 vec3_copy(n0, cb.normale);
790 n0[3] = -vec3_dot(n0, cb.point);
791 from[0] = to[0] = cb.point[0] - cb.normale[0] * 2.0f;
792 from[1] = to[1] = cb.point[1] - cb.normale[1] * 2.0f;
793 to[2] = cb.point[2];
794 // get stable normale
795 if(Physics_SphereTest(&cb, test_from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST))
796 {
797 vec3_copy(n0, cb.normale);
798 n0[3] = -vec3_dot(n0, cb.point);
799 }
800
801 from[2] = test_from[2] + ent->character->climb_r;
802 to[2] = test_to[2];
803 //renderer.debugDrawer->DrawLine(from, to, color, color);
804 ent->self->collision_heavy = 0x01;
805 if(Physics_RayTestFiltered(&cb, from, to, ent->self, COLLISION_FILTER_HEIGHT_TEST))
806 {
807 vec3_copy(n1, cb.normale);
808 n1[3] = -vec3_dot(n1, cb.point);
809 up_founded = 2;
810 from[0] = test_from[0];
811 from[1] = test_from[1];
812 from[2] = cb.point[2];
813 to[0] = test_to[0];
814 to[1] = test_to[1];
815 to[2] = from[2];
816 while(to[2] > test_to[2])
817 {
818 if(Physics_SphereTest(&cb, from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST) && (vec3_dot(cb.normale, n1) < 0.98f))
819 {
820 vec3_copy(n0, cb.normale);
821 n0[3] = -vec3_dot(n0, cb.point);
822 break;
823 }
824 from[2] += z_step;
825 to[2] += z_step;
826 }
827 }
828 ent->self->collision_heavy = heavy_flag;
829 }
830 }
831 else
832 {
833 // IN FLY CASE
834 from[0] = to[0] = test_to[0];
835 from[1] = to[1] = test_to[1];
836 from[2] = test_from[2];
837 to[2] = test_to[2];
838 //renderer.debugDrawer->DrawLine(from, to, color, color);
839 if(Physics_SphereTest(&cb, from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST) && (cb.fraction > 0.0f))
840 {
841 vec3_copy(n0, cb.normale);
842 n0[3] = -vec3_dot(n0, cb.point);
843 up_founded = 1;
844
845 // mult 0.66 is magick, but it must be less than 1.0 and greater than 0.0;
846 // close to 1.0 - bad precision, good speed;
847 // close to 0.0 - bad speed, bad precision;
848 // close to 0.5 - middle speed, good precision
849 from[0] = test_from[0];
850 from[1] = test_from[1];
851 from[2] = to[2] = cb.point[2];
852 for(; to[2] >= test_to[2]; from[2] += z_step, to[2] += z_step)
853 {
854 //renderer.debugDrawer->DrawLine(from, to, color, color);
855 if(Physics_SphereTest(&cb, from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST))
856 {
857 if(cb.fraction == 0.0f)
858 {
859 return;
860 }
861 if(up_founded && (cb.normale[2] < 0.01f) && (vec3_dist_sq(cb.normale, n0) > 0.05f))
862 {
863 vec3_copy(n1, cb.normale);
864 n1[3] = -vec3_dot(n1, cb.point);
865 climb->edge_obj = cb.obj;
866 up_founded = 2;
867 break;
868 }
869 }
870 }
871 }
872 }
873
874 if(up_founded == 2)
875 {
876 double d, n2[4];
877 // get the character plane equation
878 vec3_copy(n2, ent->transform.M4x4 + 0);
879 n2[3] = -vec3_dot(n2, pos);
880
881 /*
882 * Solve system of the linear equations by Kramer method!
883 * I know - It may be slow, but it has a good precision!
884 * The root is point of 3 planes intersection.
885 */
886 d =-n0[0] * (n1[1] * n2[2] - n1[2] * n2[1]) +
887 n1[0] * (n0[1] * n2[2] - n0[2] * n2[1]) -
888 n2[0] * (n0[1] * n1[2] - n0[2] * n1[1]);
889
890 if(fabs(d) < 0.005f)
891 {
892 return;
893 }
894
895 climb->edge_point[0] = n0[3] * (n1[1] * n2[2] - n1[2] * n2[1]) -
896 n1[3] * (n0[1] * n2[2] - n0[2] * n2[1]) +
897 n2[3] * (n0[1] * n1[2] - n0[2] * n1[1]);
898 climb->edge_point[0] /= d;
899
900 climb->edge_point[1] = n0[0] * (n1[3] * n2[2] - n1[2] * n2[3]) -
901 n1[0] * (n0[3] * n2[2] - n0[2] * n2[3]) +
902 n2[0] * (n0[3] * n1[2] - n0[2] * n1[3]);
903 climb->edge_point[1] /= d;
904
905 climb->edge_point[2] = n0[0] * (n1[1] * n2[3] - n1[3] * n2[1]) -
906 n1[0] * (n0[1] * n2[3] - n0[3] * n2[1]) +
907 n2[0] * (n0[1] * n1[3] - n0[3] * n1[1]);
908 climb->edge_point[2] /= d;
909 vec3_copy(climb->point, climb->edge_point);
910 //renderer.debugDrawer->DrawLine(to, climb->point, color, color);
911 vec3_sub(from, test_to, test_from);
912 vec3_sub(to, climb->edge_point, test_from);
913 if((from[0] * to[0] + from[1] * to[1] < 0.0f) ||
914 (climb->edge_point[2] < test_to[2]) ||
915 (climb->edge_point[2] > test_from[2] + ent->character->climb_r))
916 {
917 return;
918 }
919
920 // unclimbable edge slant test
921 vec3_cross(n2, n0, n1);
922 d = ent->character->critical_slant_z_component;
923 d *= d * (n2[0] * n2[0] + n2[1] * n2[1] + n2[2] * n2[2]);
924 if(n2[2] * n2[2] > d)
925 {
926 return;
927 }
928
929 /*
930 * Now, let us calculate z_angle
931 */
932 climb->edge_hit = 0x01;
933
934 n2[2] = n2[0];
935 n2[0] = n2[1];
936 n2[1] =-n2[2];
937 n2[2] = 0.0f;
938 if(n2[0] * ent->transform.M4x4[4 + 0] + n2[1] * ent->transform.M4x4[4 + 1] > 0) // direction fixing
939 {
940 n2[0] = -n2[0];
941 n2[1] = -n2[1];
942 }
943
944 vec3_copy(climb->n, n2);
945 climb->up[0] = 0.0f;
946 climb->up[1] = 0.0f;
947 climb->up[2] = 1.0f;
948 climb->edge_z_ang = 180.0f * atan2f(n2[0], -n2[1]) / M_PI;
949 climb->edge_tan_xy[0] = -n2[1];
950 climb->edge_tan_xy[1] = n2[0];
951 d = sqrtf(n2[0] * n2[0] + n2[1] * n2[1]);
952 climb->edge_tan_xy[0] /= d;
953 climb->edge_tan_xy[1] /= d;
954 climb->t[0] = climb->edge_tan_xy[0];
955 climb->t[1] = climb->edge_tan_xy[1];
956
957 // Calc hang info
958 from[0] = test_to[0];
959 from[1] = test_to[1];
960 from[2] = climb->edge_point[2];
961 climb->next_z_space = 2.0f * ent->character->height;
962 {
963 room_sector_p next_sector = (ent->self->room) ? (Room_GetSectorXYZ(ent->self->room, from)) : (NULL);
964 next_sector = Sector_GetPortalSectorTargetRaw(next_sector);
965 if(next_sector)
966 {
967 next_sector = Sector_GetHighest(next_sector);
968 climb->next_z_space = next_sector->ceiling - climb->edge_point[2];
969 vec3_copy(to, from);
970 from[2] = next_sector->ceiling;
971 to[2] = next_sector->ceiling + TR_METERING_SECTORSIZE;
972 if(Physics_RayTestFiltered(&cb, from, to, ent->self, COLLISION_FILTER_HEIGHT_TEST))
973 {
974 climb->next_z_space = cb.point[2] - climb->edge_point[2];
975 }
976 }
977 else
978 {
979 climb->next_z_space = 0.0f;
980 }
981 }
982
983 from[0] = to[0] = test_from[0];
984 from[1] = to[1] = test_from[1];
985 from[2] = test_from[2];
986 to[2] = climb->edge_point[2] - ent->character->height;
987 climb->can_hang = (Physics_RayTestFiltered(NULL, from, to, ent->self, COLLISION_FILTER_HEIGHT_TEST) ? (0x00) : (0x01));
988 }
989 }
990
991
Character_CheckWallsClimbability(struct entity_s * ent,struct climb_info_s * climb)992 void Character_CheckWallsClimbability(struct entity_s *ent, struct climb_info_s *climb)
993 {
994 float from[3], to[3], t;
995 collision_result_t cb;
996
997 climb->can_hang = 0x00;
998 climb->wall_hit = 0x00;
999 climb->edge_hit = 0x00;
1000 climb->edge_obj = NULL;
1001
1002 if(ent->character->height_info.walls_climb == 0x00)
1003 {
1004 return;
1005 }
1006
1007 // now we have wall normale in XOY plane. Let us check all flags
1008 if(!((ent->character->height_info.walls_climb_dir & SECTOR_FLAG_CLIMB_NORTH) && (ent->transform.M4x4[4 + 1] > 0.7)) &&
1009 !((ent->character->height_info.walls_climb_dir & SECTOR_FLAG_CLIMB_EAST) && (ent->transform.M4x4[4 + 0] > 0.7)) &&
1010 !((ent->character->height_info.walls_climb_dir & SECTOR_FLAG_CLIMB_SOUTH) && (ent->transform.M4x4[4 + 1] < -0.7)) &&
1011 !((ent->character->height_info.walls_climb_dir & SECTOR_FLAG_CLIMB_WEST) && (ent->transform.M4x4[4 + 0] < -0.7)))
1012 {
1013 return;
1014 }
1015
1016 climb->up[0] = 0.0f;
1017 climb->up[1] = 0.0f;
1018 climb->up[2] = 1.0f;
1019
1020 Character_GetMiddleHandsPos(ent, from);
1021 vec3_copy(to, from);
1022 t = ent->character->climb_r * 2.0f;
1023 from[0] -= ent->transform.M4x4[4 + 0] * t;
1024 from[1] -= ent->transform.M4x4[4 + 1] * t;
1025
1026 t += ent->character->forvard_size + 64.0f; //@WORKAROUND! stupid useless anim move command usages!
1027 to[0] += ent->transform.M4x4[4 + 0] * t;
1028 to[1] += ent->transform.M4x4[4 + 1] * t;
1029
1030 to[2] -= ent->character->min_step_up_height;
1031 Character_CheckClimbability(ent, climb, from, to);
1032 to[2] += ent->character->min_step_up_height;
1033 if(Physics_SphereTest(&cb, from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST))
1034 {
1035 float wn2[2] = {cb.normale[0], cb.normale[1]};
1036
1037 climb->wall_hit = 0x01;
1038 vec3_copy(climb->point, cb.point);
1039 vec3_copy(climb->n, cb.normale);
1040 t = sqrtf(wn2[0] * wn2[0] + wn2[1] * wn2[1]);
1041 wn2[0] /= t;
1042 wn2[1] /= t;
1043
1044 climb->t[0] =-wn2[1];
1045 climb->t[1] = wn2[0];
1046 climb->t[2] = 0.0f;
1047
1048 if(climb->wall_hit)
1049 {
1050 from[2] -= 0.67f * ent->character->height;
1051 to[2] = from[2];
1052
1053 if(Physics_SphereTest(NULL, from, to, ent->character->climb_r, ent->self, COLLISION_FILTER_HEIGHT_TEST))
1054 {
1055 climb->wall_hit = 0x02;
1056 }
1057 }
1058 }
1059 }
1060
1061
Character_SetToJump(struct entity_s * ent,float v_vertical,float v_horizontal)1062 void Character_SetToJump(struct entity_s *ent, float v_vertical, float v_horizontal)
1063 {
1064 // Jump length is a speed value multiplied by global speed coefficient.
1065 float t = v_horizontal * ent->character->linear_speed_mult;
1066
1067 // Calculate the direction of jump by vector multiplication.
1068 if(ent->dir_flag & ENT_MOVE_FORWARD)
1069 {
1070 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, t);
1071 }
1072 else if(ent->dir_flag & ENT_MOVE_BACKWARD)
1073 {
1074 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, -t);
1075 }
1076 else if(ent->dir_flag & ENT_MOVE_LEFT)
1077 {
1078 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, -t);
1079 }
1080 else if(ent->dir_flag & ENT_MOVE_RIGHT)
1081 {
1082 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, t);
1083 }
1084 else
1085 {
1086 // Jump speed should NOT be added to current speed, as native engine
1087 // fully replaces current speed with jump speed by anim command.
1088 //vec3_set_zero(spd);
1089 ent->dir_flag = ENT_MOVE_FORWARD;
1090 }
1091
1092 ent->character->state.floor_collide = 0x00;
1093 ent->character->state.ceiling_collide = 0x00;
1094 //ent->character->state.slide = 0x00;
1095 ent->character->state.step_z = 0x00;
1096
1097 // Apply vertical speed.
1098 ent->speed[2] = v_vertical * ent->character->linear_speed_mult;
1099 ent->move_type = MOVE_FREE_FALLING;
1100 }
1101
1102
Character_Lean(struct entity_s * ent,character_command_p cmd,float max_lean)1103 void Character_Lean(struct entity_s *ent, character_command_p cmd, float max_lean)
1104 {
1105 float neg_lean = 360.0f - max_lean;
1106 float lean_coeff = (max_lean == 0.0f) ? (48.0f) : (max_lean * 3.0f);
1107
1108 // Continously lean character, according to current left/right direction.
1109 if((cmd->move[1] == 0) || (max_lean == 0.0f)) // No direction - restore straight vertical position!
1110 {
1111 if(ent->transform.angles[2] != 0.0f)
1112 {
1113 if(ent->transform.angles[2] < 180.0f)
1114 {
1115 ent->transform.angles[2] -= 0.5f * (fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1116 ent->transform.angles[2] = (ent->transform.angles[2] >= 0.0f) ? (ent->transform.angles[2]) : (0.0f);
1117 }
1118 else
1119 {
1120 ent->transform.angles[2] += 0.5f * (360.0f - fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1121 ent->transform.angles[2] = (ent->transform.angles[2] >= 180.0f) ? (ent->transform.angles[2]) : (0.0f);
1122 }
1123 }
1124 }
1125 else if(cmd->move[1] == 1) // Right direction
1126 {
1127 if(ent->transform.angles[2] != max_lean)
1128 {
1129 if(ent->transform.angles[2] < max_lean) // Approaching from center
1130 {
1131 ent->transform.angles[2] += 0.5f * (fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1132 ent->transform.angles[2] = (ent->transform.angles[2] <= max_lean) ? (ent->transform.angles[2]) : (max_lean);
1133 }
1134 else if(ent->transform.angles[2] > 180.0f) // Approaching from left
1135 {
1136 ent->transform.angles[2] += 0.5f * (360.0f - fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1137 ent->transform.angles[2] = (ent->transform.angles[2] >= 180.0f) ? (ent->transform.angles[2]) : (0.0f);
1138 }
1139 else // Reduce previous lean
1140 {
1141 ent->transform.angles[2] -= 0.5f * (fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1142 ent->transform.angles[2] = (ent->transform.angles[2] >= 0.0f) ? (ent->transform.angles[2]) : (0.0f);
1143 }
1144 }
1145 }
1146 else if(cmd->move[1] == -1) // Left direction
1147 {
1148 if(ent->transform.angles[2] != neg_lean)
1149 {
1150 if(ent->transform.angles[2] > neg_lean) // Reduce previous lean
1151 {
1152 ent->transform.angles[2] -= 0.5f * (360.0f - fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1153 ent->transform.angles[2] = (ent->transform.angles[2] >= neg_lean) ? (ent->transform.angles[2]) : (neg_lean);
1154 }
1155 else if(ent->transform.angles[2] < 180.0f) // Approaching from right
1156 {
1157 ent->transform.angles[2] -= 0.5f * (fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1158 if(ent->transform.angles[2] < 0.0f) ent->transform.angles[2] += 360.0f;
1159 }
1160 else // Approaching from center
1161 {
1162 ent->transform.angles[2] += 0.5f * (360.0f - fabs(ent->transform.angles[2]) + lean_coeff) * engine_frame_time;
1163 if(ent->transform.angles[2] > 360.0f) ent->transform.angles[2] -= 360.0f;
1164 }
1165 }
1166 }
1167 }
1168
1169
Character_LookAt(struct entity_s * ent,float target[3])1170 void Character_LookAt(struct entity_s *ent, float target[3])
1171 {
1172 const float bone_dir[3] = {0.0f, 1.0f, 0.0f};
1173 const float head_target_limit[4] = {0.0f, 1.0f, 0.0f, 0.373f};
1174 ss_bone_tag_p head = ent->bf->bone_tags + ent->character->bone_head;
1175
1176 if(SSBoneFrame_CheckTargetBoneLimit(ent->bf, head, target))
1177 {
1178 SSBoneFrame_SetTarget(head, target, bone_dir);
1179 SSBoneFrame_SetTargetingLimit(head, head_target_limit);
1180 if(((ent->move_type == MOVE_ON_FLOOR) || (ent->move_type == MOVE_FREE_FALLING)) &&
1181 head->parent && head->parent->parent)
1182 {
1183 const float axis_mod[3] = {0.23f, 0.03f, 1.0f};
1184 const float target_limit[4] = {0.0f, 1.0f, 0.0f, 0.883f};
1185
1186 SSBoneFrame_SetTarget(head->parent, target, bone_dir);
1187 SSBoneFrame_SetTargetingLimit(head->parent, target_limit);
1188 SSBoneFrame_SetTargetingAxisMod(head->parent, axis_mod);
1189 }
1190 }
1191 else
1192 {
1193 head->is_targeted = 0x00;
1194 if(head->parent)
1195 {
1196 head->parent->is_targeted = 0x00;
1197 }
1198 }
1199 }
1200
1201
Character_ClearLookAt(struct entity_s * ent)1202 void Character_ClearLookAt(struct entity_s *ent)
1203 {
1204 ss_bone_tag_p head = ent->bf->bone_tags + ent->character->bone_head;
1205 head->is_targeted = 0x00;
1206 if(head->parent)
1207 {
1208 head->parent->is_targeted = 0x00;
1209 }
1210 }
1211
1212
Character_LookAtTarget(struct entity_s * ent,struct entity_s * target)1213 void Character_LookAtTarget(struct entity_s *ent, struct entity_s *target)
1214 {
1215 if(target && (ent->character->bone_head > 0))
1216 {
1217 float pos[3];
1218 if(target->character)
1219 {
1220 float *v = target->bf->bone_tags[target->character->bone_head].full_transform + 12;
1221 Mat4_vec3_mul_macro(pos, target->transform.M4x4, v);
1222 }
1223 else
1224 {
1225 vec3_copy(pos, target->obb->centre);
1226 }
1227 Character_LookAt(ent, pos);
1228 }
1229 else
1230 {
1231 Character_ClearLookAt(ent);
1232 }
1233 }
1234
1235
1236 /*
1237 * MOVE IN DIFFERENCE CONDITIONS
1238 */
Character_MoveOnFloor(struct entity_s * ent)1239 int Character_MoveOnFloor(struct entity_s *ent)
1240 {
1241 float norm_move_xy_len, t, *pos = ent->transform.M4x4 + 12;
1242 float tv[3], move[3], norm_move_xy[2];
1243
1244 t = ent->anim_linear_speed * ent->character->linear_speed_mult;
1245 ent->transform.angles[0] += ROT_SPEED_LAND * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * (float)ent->character->cmd.rot[0];
1246 Entity_UpdateTransform(ent); // apply rotations
1247
1248 if(ent->dir_flag & ENT_MOVE_FORWARD)
1249 {
1250 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, t);
1251 }
1252 else if(ent->dir_flag & ENT_MOVE_BACKWARD)
1253 {
1254 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4,-t);
1255 }
1256 else if(ent->dir_flag & ENT_MOVE_LEFT)
1257 {
1258 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0,-t);
1259 }
1260 else if(ent->dir_flag & ENT_MOVE_RIGHT)
1261 {
1262 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, t);
1263 }
1264 else
1265 {
1266 vec3_set_zero(ent->speed);
1267 //ent->dir_flag = ENT_MOVE_FORWARD;
1268 }
1269
1270 /*
1271 * do on floor move
1272 */
1273 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1274 t = vec3_abs(move);
1275
1276 norm_move_xy[0] = move[0];
1277 norm_move_xy[1] = move[1];
1278 norm_move_xy_len = sqrtf(move[0] * move[0] + move[1] * move[1]);
1279 if(norm_move_xy_len > 0.2f * t)
1280 {
1281 norm_move_xy[0] /= norm_move_xy_len;
1282 norm_move_xy[1] /= norm_move_xy_len;
1283 }
1284 else
1285 {
1286 norm_move_xy_len = 32512.0f;
1287 norm_move_xy[0] = 0.0f;
1288 norm_move_xy[1] = 0.0f;
1289 }
1290
1291 Entity_GhostUpdate(ent);
1292 vec3_add(pos, pos, move);
1293 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER);
1294 Character_UpdateCurrentHeight(ent);
1295
1296 // check micro gaps cases
1297 if(!ent->character->height_info.floor_hit.hit || (pos[2] >= ent->character->height_info.floor_hit.point[2] + ent->character->fall_down_height))
1298 {
1299 tv[0] = pos[0];
1300 tv[1] = pos[1];
1301 tv[2] = pos[2] - ent->character->fall_down_height;
1302 move[0] = pos[0];
1303 move[1] = pos[1];
1304 move[2] = pos[2] + 24.0f;
1305 if(Physics_SphereTest(&ent->character->height_info.floor_hit, move, tv, 16.0f, ent->self, COLLISION_FILTER_HEIGHT_TEST))
1306 {
1307 ent->character->height_info.floor_hit.normale[0] = 0.0f;
1308 ent->character->height_info.floor_hit.normale[1] = 0.0f;
1309 ent->character->height_info.floor_hit.normale[2] = 1.0f;
1310 }
1311 }
1312
1313 // check move type
1314 if(ent->character->height_info.floor_hit.hit &&
1315 (pos[2] < ent->character->height_info.floor_hit.point[2] + ent->character->fall_down_height) &&
1316 (ent->speed[2] <= 0.0f))
1317 {
1318 height_info_p fc = &ent->character->height_info;
1319 if((fc->floor_hit.normale[2] > 0.02) && (fc->floor_hit.normale[2] < ent->character->critical_slant_z_component))
1320 {
1321 float from[3];
1322 collision_result_t cs;
1323 from[0] = tv[0] = ent->transform.M4x4[12 + 0];
1324 from[1] = tv[1] = ent->transform.M4x4[12 + 1];
1325 from[2] = tv[2] = fc->floor_hit.point[2];
1326 from[2] += 2.0f * ent->character->sphere;
1327 if(Physics_SphereTest(&cs, from, tv, ent->character->sphere, fc->self, COLLISION_FILTER_HEIGHT_TEST) &&
1328 (fabs(cs.normale[2] - fc->floor_hit.normale[2]) < 0.01))
1329 {
1330 ent->character->state.slide = (fc->floor_hit.normale[0] * ent->transform.M4x4[4 + 0] + fc->floor_hit.normale[1] * ent->transform.M4x4[4 + 1] >= 0.0f) ? (CHARACTER_SLIDE_FRONT) : (CHARACTER_SLIDE_BACK);
1331 }
1332 }
1333 ent->character->state.floor_collide = 0x01;
1334 vec3_copy(tv, fc->floor_hit.normale);
1335
1336 if(ent->character->state.slide)
1337 {
1338 tv[2] = -tv[2];
1339 t = ent->character->linear_speed_mult * DEFAULT_CHARACTER_SLIDE_SPEED_MULT;
1340 vec3_mul_scalar(ent->speed, tv, t); // slide down direction
1341 t = 180.0f * atan2f(tv[0], -tv[1]) / M_PI; // from -180 deg to +180 deg
1342 if(ent->character->state.slide == CHARACTER_SLIDE_FRONT)
1343 {
1344 ent->transform.angles[0] = t + 180.0f;
1345 }
1346 else //if(fc->slide == CHARACTER_SLIDE_BACK)
1347 {
1348 ent->transform.angles[0] = t;
1349 }
1350 Entity_UpdateTransform(ent);
1351 }
1352
1353 {
1354 t = pos[2] - ent->character->height_info.floor_hit.point[2];
1355 if(t < 0.0f)
1356 {
1357 pos[2] = ent->character->height_info.floor_hit.point[2];
1358 ent->character->state.floor_collide = 0x01;
1359 ent->character->state.step_z = (t < -ent->character->min_step_up_height) ? (0x01) : (0x00);
1360 pos[2] = ent->character->height_info.floor_hit.point[2];
1361 }
1362 else if(t > ent->character->min_step_up_height)
1363 {
1364 ent->character->state.step_z = 0x02;
1365 pos[2] -= engine_frame_time * 2400.0f; ///@FIXME: magick
1366 pos[2] = (pos[2] >= ent->character->height_info.floor_hit.point[2]) ? (pos[2]) : (ent->character->height_info.floor_hit.point[2]);
1367 }
1368 else
1369 {
1370 pos[2] = ent->character->height_info.floor_hit.point[2];
1371 }
1372 }
1373
1374 if(ent->character->height_info.floor_hit.hit && (ent->character->height_info.floor_hit.point[2] + 1.0f >= pos[2] + ent->bf->bb_min[2]))
1375 {
1376 engine_container_p cont = ent->character->height_info.floor_hit.obj;
1377 if(cont && (cont->object_type == OBJECT_ENTITY))
1378 {
1379 entity_p e = (entity_p)cont->object;
1380 if(e->callback_flags & ENTITY_CALLBACK_STAND)
1381 {
1382 Script_ExecEntity(engine_lua, ENTITY_CALLBACK_STAND, e->id, ent->id);
1383 }
1384 }
1385 }
1386 }
1387 else // no hit to the floor
1388 {
1389 ent->character->state.floor_collide = 0x00;
1390 ent->move_type = MOVE_FREE_FALLING;
1391 ent->speed[2] = (ent->speed[2] < 0.0f) ? (0.0) : (ent->speed[2]);
1392 return 2;
1393 }
1394
1395 Entity_UpdateRoomPos(ent);
1396
1397 return 1;
1398 }
1399
1400
Character_MoveFly(struct entity_s * ent)1401 int Character_MoveFly(struct entity_s *ent)
1402 {
1403 float move[3], *pos = ent->transform.M4x4 + 12;
1404 character_command_p cmd = &ent->character->cmd;
1405 float dir[3] = {0.0f, 0.0f, 0.0f};
1406
1407 // Calculate current speed.
1408 if(cmd->move[0] || cmd->move[1] || cmd->move[2])
1409 {
1410 ent->linear_speed += MAX_SPEED_UNDERWATER * INERTIA_SPEED_UNDERWATER * engine_frame_time;
1411 if(ent->linear_speed > MAX_SPEED_UNDERWATER)
1412 {
1413 ent->linear_speed = MAX_SPEED_UNDERWATER;
1414 }
1415 }
1416 else if(ent->linear_speed > 0.0f)
1417 {
1418 ent->linear_speed -= MAX_SPEED_UNDERWATER * INERTIA_SPEED_UNDERWATER * engine_frame_time;
1419 if(ent->linear_speed < 0.0f)
1420 {
1421 ent->linear_speed = 0.0f;
1422 }
1423 }
1424
1425 if(!cmd->shift)
1426 {
1427 ent->transform.angles[0] += ROT_SPEED_UNDERWATER * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * cmd->rot[0];
1428 ent->transform.angles[1] = 0.0f;
1429 ent->transform.angles[2] = 0.0f;
1430 Entity_UpdateTransform(ent);
1431 }
1432 dir[0] = (cmd->shift) ? (cmd->move[1]) : (0.0f);
1433 dir[1] = cmd->move[0];
1434 dir[2] = cmd->move[2];
1435 Mat4_vec3_rot_macro(ent->speed, ent->transform.M4x4, dir);
1436 vec3_mul_scalar(ent->speed, ent->speed, ent->linear_speed * ent->character->linear_speed_mult); // OY move only!
1437
1438 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1439 vec3_add(pos, pos, move);
1440 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1441 Entity_UpdateRoomPos(ent);
1442 Character_UpdateCurrentHeight(ent);
1443
1444 return 1;
1445 }
1446
1447
Character_FreeFalling(struct entity_s * ent)1448 int Character_FreeFalling(struct entity_s *ent)
1449 {
1450 float move[3], g[3], *pos = ent->transform.M4x4 + 12;
1451 float rot = ROT_SPEED_FREEFALL * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * ent->character->cmd.rot[0];
1452
1453 ent->transform.angles[0] += rot;
1454 ent->transform.angles[1] = 0.0f;
1455
1456 Entity_UpdateTransform(ent); // apply rotations
1457
1458 Physics_GetGravity(g);
1459 vec3_add_mul(move, ent->speed, g, engine_frame_time * 0.5f);
1460 move[0] *= engine_frame_time;
1461 move[1] *= engine_frame_time;
1462 move[2] *= engine_frame_time;
1463 ent->speed[0] += g[0] * engine_frame_time;
1464 ent->speed[1] += g[1] * engine_frame_time;
1465 ent->speed[2] += g[2] * engine_frame_time;
1466 ent->speed[2] = (ent->speed[2] >= -FREE_FALL_SPEED_MAXIMUM) ? (ent->speed[2]) : (-FREE_FALL_SPEED_MAXIMUM);
1467 vec3_RotateZ(ent->speed, ent->speed, rot);
1468
1469 vec3_add(pos, pos, move);
1470 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1471 Entity_GhostUpdate(ent);
1472 Entity_UpdateRoomPos(ent);
1473 Character_UpdateCurrentHeight(ent);
1474
1475 if(ent->self->room && (ent->self->room->content->room_flags & TR_ROOM_FLAG_WATER))
1476 {
1477 if(ent->speed[2] < 0.0f)
1478 {
1479 ent->anim_linear_speed = 0.0f;
1480 ent->speed[0] = 0.0f;
1481 ent->speed[1] = 0.0f;
1482 }
1483
1484 float transition_level = ent->character->height_info.transition_level;
1485 transition_level = (World_GetVersion() < TR_II) ? (transition_level) : (transition_level - ent->character->height);
1486 if(!ent->character->height_info.water || (ent->self->sector->floor <= transition_level))
1487 {
1488 ent->move_type = MOVE_UNDERWATER;
1489 return 2;
1490 }
1491 }
1492
1493 if(ent->character->height_info.ceiling_hit.hit && (ent->speed[2] > 0.0f))
1494 {
1495 if(ent->character->height_info.ceiling_hit.point[2] < ent->bf->bb_max[2] + pos[2])
1496 {
1497 pos[2] = ent->character->height_info.ceiling_hit.point[2] - ent->bf->bb_max[2];
1498 ent->speed[2] = (ent->speed[2] > 0.0f) ? (0.0f) : (ent->speed[2]);
1499 ent->character->state.ceiling_collide = 0x01;
1500 }
1501 }
1502 if(ent->character->height_info.floor_hit.hit && (ent->speed[2] < 0.0f)) // move down
1503 {
1504 if(ent->character->height_info.floor_hit.point[2] >= pos[2] + ent->bf->bb_min[2] + move[2])
1505 {
1506 pos[2] = ent->character->height_info.floor_hit.point[2];
1507 ent->move_type = MOVE_ON_FLOOR;
1508 ent->character->state.floor_collide = 0x01;
1509 return 2;
1510 }
1511 }
1512
1513 return 1;
1514 }
1515
1516 /*
1517 * Monkey CLIMBING - MOVE NO Z LANDING
1518 */
Character_MonkeyClimbing(struct entity_s * ent)1519 int Character_MonkeyClimbing(struct entity_s *ent)
1520 {
1521 float move[3];
1522 float *pos = ent->transform.M4x4 + 12;
1523 float t = ent->anim_linear_speed * ent->character->linear_speed_mult;
1524 int ret = 1;
1525
1526 ent->transform.angles[0] += ROT_SPEED_MONKEYSWING * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * ent->character->cmd.rot[0];
1527 ent->transform.angles[1] = 0.0f;
1528 ent->transform.angles[2] = 0.0f;
1529 Entity_UpdateTransform(ent); // apply rotations
1530
1531 if(ent->dir_flag & ENT_MOVE_FORWARD)
1532 {
1533 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, t);
1534 }
1535 else if(ent->dir_flag & ENT_MOVE_BACKWARD)
1536 {
1537 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4,-t);
1538 }
1539 else if(ent->dir_flag & ENT_MOVE_LEFT)
1540 {
1541 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0,-t);
1542 }
1543 else if(ent->dir_flag & ENT_MOVE_RIGHT)
1544 {
1545 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, t);
1546 }
1547 else
1548 {
1549 vec3_set_zero(ent->speed);
1550 }
1551 ent->speed[2] = 0.0f;
1552 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1553
1554 vec3_add(pos, pos, move);
1555 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1556 if(ent->character->height_info.ceiling_hit.hit && (pos[2] + ent->bf->bb_max[2] - ent->character->height_info.ceiling_hit.point[2] > - 0.33 * ent->character->min_step_up_height))
1557 {
1558 pos[2] = ent->character->height_info.ceiling_hit.point[2] - ent->bf->bb_max[2];
1559 }
1560 else
1561 {
1562 ent->move_type = MOVE_FREE_FALLING;
1563 ret = 2;
1564 }
1565
1566 Entity_UpdateRoomPos(ent);
1567
1568 return ret;
1569 }
1570
1571 /*
1572 * WALLS CLIMBING - MOVE IN ZT plane
1573 */
Character_WallsClimbing(struct entity_s * ent)1574 int Character_WallsClimbing(struct entity_s *ent)
1575 {
1576 climb_info_t *climb = &ent->character->climb;
1577 float move[3], t, *pos = ent->transform.M4x4 + 12;
1578
1579 Character_CheckWallsClimbability(ent, climb);
1580 Character_GetMiddleHandsPos(ent, move);
1581 if(!climb->wall_hit || (climb->edge_hit && (climb->edge_point[2] < move[2])))
1582 {
1583 if(climb->edge_hit && (ent->dir_flag != ENT_MOVE_BACKWARD))
1584 {
1585 pos[2] += climb->edge_point[2] - move[2];
1586 }
1587 return 2;
1588 }
1589
1590 ent->transform.angles[0] = atan2f(climb->n[0], -climb->n[1]) * 180.0f / M_PI;
1591 Entity_UpdateTransform(ent);
1592 pos[0] = climb->point[0] + climb->n[0] * ent->bf->bb_max[1];
1593 pos[1] = climb->point[1] + climb->n[1] * ent->bf->bb_max[1];
1594
1595 if(ent->dir_flag == ENT_MOVE_RIGHT)
1596 {
1597 vec3_copy(move, climb->t);
1598 t = ent->anim_linear_speed * ent->character->linear_speed_mult;
1599 }
1600 else if(ent->dir_flag == ENT_MOVE_LEFT)
1601 {
1602 vec3_copy_inv(move, climb->t);
1603 t = ent->anim_linear_speed * ent->character->linear_speed_mult;
1604 }
1605 else
1606 {
1607 vec3_set_zero(move);
1608 t = 0.0f;
1609 }
1610
1611 if(t != 0.0f)
1612 {
1613 vec3_mul_scalar(ent->speed, move, t);
1614 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1615 vec3_add(pos, pos, move);
1616 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1617 }
1618
1619 Entity_UpdateRoomPos(ent);
1620
1621 return 1;
1622 }
1623
1624 /*
1625 * CLIMBING - MOVE NO Z LANDING
1626 */
Character_Climbing(struct entity_s * ent)1627 int Character_Climbing(struct entity_s *ent)
1628 {
1629 float move[3];
1630 float t, *pos = ent->transform.M4x4 + 12;
1631
1632 t = ent->anim_linear_speed * ent->character->linear_speed_mult;
1633 ent->transform.angles[0] += ROT_SPEED_MONKEYSWING * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * ent->character->cmd.rot[0];
1634 ent->transform.angles[1] = 0.0f;
1635 ent->transform.angles[2] = 0.0f;
1636 Entity_UpdateTransform(ent); // apply rotations
1637
1638 if(ent->dir_flag == ENT_MOVE_FORWARD)
1639 {
1640 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, t);
1641 }
1642 else if(ent->dir_flag == ENT_MOVE_BACKWARD)
1643 {
1644 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4,-t);
1645 }
1646 else if(ent->dir_flag == ENT_MOVE_LEFT)
1647 {
1648 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0,-t);
1649 }
1650 else if(ent->dir_flag == ENT_MOVE_RIGHT)
1651 {
1652 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, t);
1653 }
1654 else
1655 {
1656 vec3_set_zero(ent->speed);
1657 Entity_GhostUpdate(ent);
1658 Entity_FixPenetrations(ent, Character_CollisionCallback, NULL, COLLISION_FILTER_CHARACTER);
1659 return 1;
1660 }
1661
1662 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1663 vec3_add(pos, pos, move);
1664 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1665 Entity_UpdateRoomPos(ent);
1666
1667 return 1;
1668 }
1669
1670 /*
1671 * underwater and onwater swimming has a big trouble:
1672 * the speed and acceleration information is absent...
1673 * I add some sticks to make it work for testing.
1674 * I thought to make export anim information to LUA script...
1675 */
Character_MoveUnderWater(struct entity_s * ent)1676 int Character_MoveUnderWater(struct entity_s *ent)
1677 {
1678 float move[3], *pos = ent->transform.M4x4 + 12;
1679
1680 // Check current place.
1681
1682 if(ent->self->room && !(ent->self->room->content->room_flags & TR_ROOM_FLAG_WATER))
1683 {
1684 ent->move_type = MOVE_FREE_FALLING;
1685 return 2;
1686 }
1687
1688 // Calculate current speed.
1689 if(ent->character->cmd.jump)
1690 {
1691 ent->linear_speed += MAX_SPEED_UNDERWATER * INERTIA_SPEED_UNDERWATER * engine_frame_time;
1692 if(ent->linear_speed > MAX_SPEED_UNDERWATER)
1693 {
1694 ent->linear_speed = MAX_SPEED_UNDERWATER;
1695 }
1696 }
1697 else if(ent->linear_speed > 0.0f)
1698 {
1699 ent->linear_speed -= MAX_SPEED_UNDERWATER * INERTIA_SPEED_UNDERWATER * engine_frame_time;
1700 if(ent->linear_speed < 0.0f)
1701 {
1702 ent->linear_speed = 0.0f;
1703 }
1704 }
1705
1706 ent->transform.angles[0] += ROT_SPEED_UNDERWATER * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * ent->character->cmd.rot[0];
1707 ent->transform.angles[1] -= ROT_SPEED_UNDERWATER * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * ent->character->cmd.rot[1];
1708 ent->transform.angles[2] = 0.0f;
1709
1710 if((ent->transform.angles[1] > 70.0f) && (ent->transform.angles[1] < 180.0f)) // Underwater angle limiter.
1711 {
1712 ent->transform.angles[1] = 70.0f;
1713 }
1714 else if((ent->transform.angles[1] > 180.0f) && (ent->transform.angles[1] < 270.0f))
1715 {
1716 ent->transform.angles[1] = 270.0f;
1717 }
1718
1719 Entity_UpdateTransform(ent); // apply rotations
1720 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, ent->linear_speed * ent->character->linear_speed_mult); // OY move only!
1721
1722 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1723 vec3_add(pos, pos, move);
1724 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1725 Entity_UpdateRoomPos(ent);
1726 Character_UpdateCurrentHeight(ent);
1727 if(ent->character->height_info.water && (pos[2] + ent->bf->bb_max[2] >= ent->character->height_info.transition_level))
1728 {
1729 if(ent->transform.M4x4[4 + 2] > 0.67f) ///@FIXME: magick!
1730 {
1731 ent->move_type = MOVE_ON_WATER;
1732 //pos[2] = fc.transition_level;
1733 return 2;
1734 }
1735 if(!ent->character->height_info.floor_hit.hit || (ent->character->height_info.transition_level - ent->character->height_info.floor_hit.point[2] >= ent->character->height))
1736 {
1737 pos[2] = ent->character->height_info.transition_level - ent->bf->bb_max[2];
1738 }
1739 }
1740
1741 return 1;
1742 }
1743
1744
Character_MoveOnWater(struct entity_s * ent)1745 int Character_MoveOnWater(struct entity_s *ent)
1746 {
1747 float move[3];
1748 float *pos = ent->transform.M4x4 + 12;
1749
1750 ent->transform.angles[0] += ROT_SPEED_ONWATER * 60.0f * ent->character->rotate_speed_mult * engine_frame_time * ent->character->cmd.rot[0];
1751 ent->transform.angles[1] = 0.0f;
1752 ent->transform.angles[2] = 0.0f;
1753 Entity_UpdateTransform(ent); // apply rotations
1754
1755 // Calculate current speed.
1756 if(ent->character->cmd.move[0] || ent->character->cmd.move[1])
1757 {
1758 ent->linear_speed += MAX_SPEED_ONWATER * INERTIA_SPEED_ONWATER * engine_frame_time;
1759 if(ent->linear_speed > MAX_SPEED_ONWATER)
1760 {
1761 ent->linear_speed = MAX_SPEED_ONWATER;
1762 }
1763 }
1764 else if(ent->linear_speed > 0.0f)
1765 {
1766 ent->linear_speed -= MAX_SPEED_ONWATER * INERTIA_SPEED_ONWATER * engine_frame_time;
1767 if(ent->linear_speed < 0.0f)
1768 {
1769 ent->linear_speed = 0.0f;
1770 }
1771 }
1772
1773 float t = ent->linear_speed * ent->linear_speed;
1774 if((ent->dir_flag & ENT_MOVE_FORWARD) && (ent->character->cmd.move[0] == 1))
1775 {
1776 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4, t);
1777 }
1778 else if((ent->dir_flag & ENT_MOVE_BACKWARD) && (ent->character->cmd.move[0] == -1))
1779 {
1780 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 4,-t);
1781 }
1782 else if((ent->dir_flag & ENT_MOVE_LEFT) && (ent->character->cmd.move[1] == -1))
1783 {
1784 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0,-t);
1785 }
1786 else if((ent->dir_flag & ENT_MOVE_RIGHT) && (ent->character->cmd.move[1] == 1))
1787 {
1788 vec3_mul_scalar(ent->speed, ent->transform.M4x4 + 0, t);
1789 }
1790 else
1791 {
1792 vec3_set_zero(ent->speed);
1793 Entity_GhostUpdate(ent);
1794 Entity_FixPenetrations(ent, Character_CollisionCallback, NULL, COLLISION_FILTER_CHARACTER);
1795 Entity_UpdateRoomPos(ent);
1796 if(ent->character->height_info.water)
1797 {
1798 pos[2] = ent->character->height_info.transition_level;
1799 }
1800 else
1801 {
1802 ent->move_type = MOVE_ON_FLOOR;
1803 return 2;
1804 }
1805 return 1;
1806 }
1807
1808 /*
1809 * Prepare to moving
1810 */
1811 vec3_mul_scalar(move, ent->speed, engine_frame_time);
1812 vec3_add(pos, pos, move);
1813 Entity_FixPenetrations(ent, Character_CollisionCallback, move, COLLISION_FILTER_CHARACTER); // get horizontal collide
1814 Entity_UpdateRoomPos(ent);
1815 Character_UpdateCurrentHeight(ent);
1816 if(ent->character->height_info.water)
1817 {
1818 pos[2] = ent->character->height_info.transition_level;
1819 }
1820 else
1821 {
1822 ent->move_type = MOVE_ON_FLOOR;
1823 return 2;
1824 }
1825
1826 return 1;
1827 }
1828
Character_FindTraverse(struct entity_s * ch)1829 int Character_FindTraverse(struct entity_s *ch)
1830 {
1831 room_sector_p ch_s, obj_s = NULL;
1832 ch_s = ch->self->sector;
1833
1834 if(ch_s)
1835 {
1836 ch->character->traversed_object = NULL;
1837
1838 // OX move case
1839 if(ch->transform.M4x4[4 + 0] > 0.9f)
1840 {
1841 float pos[] = {(float)(ch_s->pos[0] + TR_METERING_SECTORSIZE), (float)(ch_s->pos[1]), 0.0f};
1842 obj_s = Room_GetSectorRaw(ch->self->room->real_room, pos);
1843 }
1844 else if(ch->transform.M4x4[4 + 0] < -0.9f)
1845 {
1846 float pos[] = {(float)(ch_s->pos[0] - TR_METERING_SECTORSIZE), (float)(ch_s->pos[1]), 0.0f};
1847 obj_s = Room_GetSectorRaw(ch->self->room->real_room, pos);
1848 }
1849 // OY move case
1850 else if(ch->transform.M4x4[4 + 1] > 0.9f)
1851 {
1852 float pos[] = {(float)(ch_s->pos[0]), (float)(ch_s->pos[1] + TR_METERING_SECTORSIZE), 0.0f};
1853 obj_s = Room_GetSectorRaw(ch->self->room->real_room, pos);
1854 }
1855 else if(ch->transform.M4x4[4 + 1] < -0.9f)
1856 {
1857 float pos[] = {(float)(ch_s->pos[0]), (float)(ch_s->pos[1] - TR_METERING_SECTORSIZE), 0.0f};
1858 obj_s = Room_GetSectorRaw(ch->self->room->real_room, pos);
1859 }
1860
1861 if(obj_s != NULL)
1862 {
1863 obj_s = Sector_GetPortalSectorTargetRaw(obj_s);
1864 for(engine_container_p cont = obj_s->owner_room->containers; cont; cont = cont->next)
1865 {
1866 if(cont->object_type == OBJECT_ENTITY)
1867 {
1868 entity_p e = (entity_p)cont->object;
1869 if((e->type_flags & ENTITY_TYPE_TRAVERSE) && OBB_OBB_Test(e->obb, ch->obb, 32.0f) && (fabs(e->transform.M4x4[12 + 2] - ch->transform.M4x4[12 + 2]) < 1.1f))
1870 {
1871 int oz = (ch->transform.angles[0] + 45.0f) / 90.0f;
1872 ch->transform.angles[0] = oz * 90.0f;
1873 ch->character->traversed_object = e;
1874 Entity_UpdateTransform(ch);
1875 return 1;
1876 }
1877 }
1878 }
1879 }
1880
1881 for(engine_container_p cont = ch_s->owner_room->containers; cont; cont = cont->next)
1882 {
1883 if(cont->object_type == OBJECT_ENTITY)
1884 {
1885 entity_p e = (entity_p)cont->object;
1886 if((e->type_flags & ENTITY_TYPE_TRAVERSE) && OBB_OBB_Test(e->obb, ch->obb, 32.0f) && (fabs(e->transform.M4x4[12 + 2] - ch->transform.M4x4[12 + 2]) < 1.1f))
1887 {
1888 int oz = (ch->transform.angles[0] + 45.0f) / 90.0f;
1889 ch->transform.angles[0] = oz * 90.0f;
1890 ch->character->traversed_object = e;
1891 Entity_UpdateTransform(ch);
1892 return 1;
1893 }
1894 }
1895 }
1896 }
1897
1898 return 0;
1899 }
1900
1901 /**
1902 *
1903 * @param rs: room sector pointer
1904 * @param floor: floor height
1905 * @return 0x01: can traverse, 0x00 can not;
1906 */
Sector_AllowTraverse(struct room_sector_s * rs,float floor)1907 int Sector_AllowTraverse(struct room_sector_s *rs, float floor)
1908 {
1909 float f0 = rs->floor_corners[0][2];
1910 if((rs->floor_corners[0][2] != f0) || (rs->floor_corners[1][2] != f0) ||
1911 (rs->floor_corners[2][2] != f0) || (rs->floor_corners[3][2] != f0) ||
1912 (rs->floor_penetration_config != TR_PENETRATION_CONFIG_SOLID) ||
1913 (rs->ceiling - floor < TR_METERING_SECTORSIZE - 1.1f))
1914 {
1915 return 0x00;
1916 }
1917
1918 if(fabs(f0 - floor) < 1.1f)
1919 {
1920 return 0x01;
1921 }
1922
1923 for(engine_container_p cont = rs->owner_room->real_room->containers; cont; cont = cont->next)
1924 {
1925 if(cont->object_type == OBJECT_ENTITY)
1926 {
1927 entity_p ent = (entity_p)cont->object;
1928 if((ent->type_flags & ENTITY_TYPE_TRAVERSE_FLOOR) &&
1929 (fabs(ent->transform.M4x4[12 + 2] + TR_METERING_SECTORSIZE - floor) < 1.1f) &&
1930 (fabs(ent->transform.M4x4[12 + 0] - rs->pos[0]) < 1.1f) &&
1931 (fabs(ent->transform.M4x4[12 + 1] - rs->pos[1]) < 1.1f))
1932 {
1933 return 0x01;
1934 }
1935 }
1936 }
1937
1938 return 0x00;
1939 }
1940
1941 /**
1942 *
1943 * @param ch: character pointer
1944 * @param obj: traversed object pointer
1945 * @return: 0x01 if can traverse forvard; 0x02 if can traverse backvard; 0x03 can traverse in both directions; 0x00 - can't traverse
1946 */
Character_CheckTraverse(struct entity_s * ch,struct entity_s * obj)1947 int Character_CheckTraverse(struct entity_s *ch, struct entity_s *obj)
1948 {
1949 room_sector_p ch_s = ch->self->sector;
1950 room_sector_p obj_s = obj->self->sector;
1951
1952 if((ch_s == NULL) || (obj_s == NULL))
1953 {
1954 return 0x00;
1955 }
1956
1957 float floor = ch->transform.M4x4[12 + 2];
1958 if((Sector_AllowTraverse(ch_s, floor) == 0x00) || (Sector_AllowTraverse(obj_s, floor) == 0x00))
1959 {
1960 return 0x00;
1961 }
1962
1963 for(engine_container_p cont = obj_s->owner_room->real_room->containers; cont; cont = cont->next)
1964 {
1965 if(cont->object_type == OBJECT_ENTITY)
1966 {
1967 entity_p ent = (entity_p)cont->object;
1968 if((ent->type_flags & (ENTITY_TYPE_TRAVERSE | ENTITY_TYPE_TRAVERSE_FLOOR)) &&
1969 (fabs(ent->transform.M4x4[12 + 2] - TR_METERING_SECTORSIZE - floor) < 1.1f) &&
1970 (fabs(ent->transform.M4x4[12 + 0] - obj_s->pos[0]) < 1.1f) &&
1971 (fabs(ent->transform.M4x4[12 + 1] - obj_s->pos[1]) < 1.1f))
1972 {
1973 return 0x00;
1974 }
1975 }
1976 }
1977
1978 int ret = 0x00;
1979 room_sector_p next_s = NULL;
1980 const float r_test = 0.44f * TR_METERING_SECTORSIZE;
1981 /*
1982 * PUSH MOVE CHECK
1983 */
1984 // OX move case
1985 if(ch->transform.M4x4[4 + 0] > 0.8f)
1986 {
1987 float pos[] = {(float)(obj_s->pos[0] + TR_METERING_SECTORSIZE), (float)(obj_s->pos[1]), 0.0f};
1988 next_s = Room_GetSectorRaw(obj_s->owner_room->real_room, pos);
1989 }
1990 else if(ch->transform.M4x4[4 + 0] < -0.8f)
1991 {
1992 float pos[] = {(float)(obj_s->pos[0] - TR_METERING_SECTORSIZE), (float)(obj_s->pos[1]), 0.0f};
1993 next_s = Room_GetSectorRaw(obj_s->owner_room->real_room, pos);
1994 }
1995 // OY move case
1996 else if(ch->transform.M4x4[4 + 1] > 0.8f)
1997 {
1998 float pos[] = {(float)(obj_s->pos[0]), (float)(obj_s->pos[1] + TR_METERING_SECTORSIZE), 0.0f};
1999 next_s = Room_GetSectorRaw(obj_s->owner_room->real_room, pos);
2000 }
2001 else if(ch->transform.M4x4[4 + 1] < -0.8f)
2002 {
2003 float pos[] = {(float)(obj_s->pos[0]), (float)(obj_s->pos[1] - TR_METERING_SECTORSIZE), 0.0f};
2004 next_s = Room_GetSectorRaw(obj_s->owner_room->real_room, pos);
2005 }
2006
2007 next_s = Sector_GetPortalSectorTargetRaw(next_s);
2008 if((next_s != NULL) && (Sector_AllowTraverse(next_s, floor) == 0x01))
2009 {
2010 float from[3], to[3];
2011 from[0] = obj_s->pos[0];
2012 from[1] = obj_s->pos[1];
2013 from[2] = floor + 0.5f * TR_METERING_SECTORSIZE;
2014
2015 to[0] = next_s->pos[0];
2016 to[1] = next_s->pos[1];
2017 to[2] = from[2];
2018 if(!Physics_SphereTest(NULL, from, to, r_test, obj->self, COLLISION_FILTER_HEIGHT_TEST))
2019 {
2020 ret |= 0x01; // can traverse forvard
2021 }
2022 }
2023
2024 /*
2025 * PULL MOVE CHECK
2026 */
2027 next_s = NULL;
2028 // OX move case
2029 if(ch->transform.M4x4[4 + 0] > 0.8f)
2030 {
2031 float pos[] = {(float)(ch_s->pos[0] - TR_METERING_SECTORSIZE), (float)(ch_s->pos[1]), 0.0f};
2032 next_s = Room_GetSectorRaw(ch_s->owner_room->real_room, pos);
2033 }
2034 else if(ch->transform.M4x4[4 + 0] < -0.8f)
2035 {
2036 float pos[] = {(float)(ch_s->pos[0] + TR_METERING_SECTORSIZE), (float)(ch_s->pos[1]), 0.0f};
2037 next_s = Room_GetSectorRaw(ch_s->owner_room->real_room, pos);
2038 }
2039 // OY move case
2040 else if(ch->transform.M4x4[4 + 1] > 0.8f)
2041 {
2042 float pos[] = {(float)(ch_s->pos[0]), (float)(ch_s->pos[1] - TR_METERING_SECTORSIZE), 0.0f};
2043 next_s = Room_GetSectorRaw(ch_s->owner_room->real_room, pos);
2044 }
2045 else if(ch->transform.M4x4[4 + 1] < -0.8f)
2046 {
2047 float pos[] = {(float)(ch_s->pos[0]), (float)(ch_s->pos[1] + TR_METERING_SECTORSIZE), 0.0f};
2048 next_s = Room_GetSectorRaw(ch_s->owner_room->real_room, pos);
2049 }
2050
2051 next_s = Sector_GetPortalSectorTargetRaw(next_s);
2052 if((next_s != NULL) && (Sector_AllowTraverse(next_s, floor) == 0x01))
2053 {
2054 float from[3], to[3];
2055 from[0] = ch_s->pos[0];
2056 from[1] = ch_s->pos[1];
2057 from[2] = floor + 0.5f * TR_METERING_SECTORSIZE;
2058
2059 to[0] = next_s->pos[0];
2060 to[1] = next_s->pos[1];
2061 to[2] = from[2];
2062 if(!Physics_SphereTest(NULL, from ,to, r_test, ch->self, COLLISION_FILTER_HEIGHT_TEST))
2063 {
2064 ret |= 0x02; // can traverse backvard
2065 }
2066 }
2067
2068 return ret;
2069 }
2070
2071 /**
2072 * Main character frame function
2073 */
Character_ApplyCommands(struct entity_s * ent)2074 void Character_ApplyCommands(struct entity_s *ent)
2075 {
2076 if(ent->type_flags & ENTITY_TYPE_DYNAMIC)
2077 {
2078 return;
2079 }
2080
2081 Character_UpdateCurrentHeight(ent);
2082
2083 if(ent->character->set_weapon_model_func)
2084 {
2085 if((ent->character->weapon_id < 0) && (ent->character->weapon_state == WEAPON_STATE_HIDE))
2086 {
2087 ent->character->set_weapon_model_func(ent, -ent->character->weapon_id, WEAPON_STATE_HIDE);
2088 }
2089 else if((ent->character->cmd.ready_weapon != 0x00) && (ent->character->weapon_id > 0) && (ent->character->weapon_state == WEAPON_STATE_HIDE))
2090 {
2091 ent->character->set_weapon_model_func(ent, ent->character->weapon_id, WEAPON_STATE_HIDE_TO_READY);
2092 }
2093 }
2094
2095 if(ent->character->state_func)
2096 {
2097 ent->character->state_func(ent, &ent->bf->animations);
2098 }
2099
2100 ent->character->state.slide = 0x00;
2101 ent->character->state.floor_collide = 0x00;
2102 ent->character->state.ceiling_collide = 0x00;
2103 ent->character->state.wall_collide = 0x00;
2104 ent->character->state.step_z = 0x00;
2105
2106 if(!ent->no_move)
2107 {
2108 switch(ent->move_type)
2109 {
2110 case MOVE_KINEMATIC:
2111 case MOVE_STATIC_POS:
2112 //Entity_FixPenetrations(ent, NULL, COLLISION_FILTER_CHARACTER);
2113 break;
2114
2115 case MOVE_ON_FLOOR:
2116 Character_MoveOnFloor(ent);
2117 break;
2118
2119 case MOVE_FREE_FALLING:
2120 Character_FreeFalling(ent);
2121 break;
2122
2123 case MOVE_CLIMBING:
2124 Character_Climbing(ent);
2125 break;
2126
2127 case MOVE_MONKEYSWING:
2128 Character_MonkeyClimbing(ent);
2129 break;
2130
2131 case MOVE_WALLS_CLIMB:
2132 Character_WallsClimbing(ent);
2133 break;
2134
2135 case MOVE_UNDERWATER:
2136 Character_MoveUnderWater(ent);
2137 break;
2138
2139 case MOVE_ON_WATER:
2140 Character_MoveOnWater(ent);
2141 break;
2142
2143 case MOVE_FLY:
2144 Character_MoveFly(ent);
2145 break;
2146
2147 default:
2148 ent->move_type = MOVE_ON_FLOOR;
2149 break;
2150 };
2151 }
2152 }
2153
Character_UpdateParams(struct entity_s * ent)2154 void Character_UpdateParams(struct entity_s *ent)
2155 {
2156 float speed = engine_frame_time / GAME_LOGIC_REFRESH_INTERVAL;
2157
2158 if(ent->character->weapon_state != WEAPON_STATE_HIDE)
2159 {
2160 entity_p target = World_GetEntityByID(ent->character->target_id);
2161 Character_LookAtTarget(ent, target);
2162 }
2163
2164 switch(ent->move_type)
2165 {
2166 case MOVE_ON_FLOOR:
2167 case MOVE_FREE_FALLING:
2168 case MOVE_CLIMBING:
2169 case MOVE_MONKEYSWING:
2170 case MOVE_WALLS_CLIMB:
2171
2172 if((ent->character->height_info.quicksand == 0x02) &&
2173 (ent->move_type == MOVE_ON_FLOOR))
2174 {
2175 if(!Character_ChangeParam(ent, PARAM_AIR, -3.0f * speed))
2176 Character_ChangeParam(ent, PARAM_HEALTH, -3.0f * speed);
2177 }
2178 else if(ent->character->height_info.quicksand == 0x01)
2179 {
2180 Character_ChangeParam(ent, PARAM_AIR, 3.0f * speed);
2181 }
2182 else
2183 {
2184 Character_SetParam(ent, PARAM_AIR, PARAM_ABSOLUTE_MAX);
2185 }
2186
2187 if(ent->character->state.sprint)
2188 {
2189 Character_ChangeParam(ent, PARAM_STAMINA, -0.5f * speed);
2190 }
2191 else
2192 {
2193 Character_ChangeParam(ent, PARAM_STAMINA, 0.5f * speed);
2194 }
2195 break;
2196
2197 case MOVE_ON_WATER:
2198 Character_ChangeParam(ent, PARAM_AIR, 3.0f * speed);
2199 break;
2200
2201 case MOVE_UNDERWATER:
2202 if(!ent->character->state.dead && !Character_ChangeParam(ent, PARAM_AIR, -speed))
2203 {
2204 if(!Character_ChangeParam(ent, PARAM_HEALTH, -3.0f * speed))
2205 {
2206 ent->character->state.dead = 0x01;
2207 }
2208 }
2209 break;
2210
2211 default:
2212 break; // Add quicksand later...
2213 }
2214 }
2215
2216
Character_SetParamMaximum(struct entity_s * ent,int parameter,float max_value)2217 int Character_SetParamMaximum(struct entity_s *ent, int parameter, float max_value)
2218 {
2219 if(ent && ent->character && (parameter < PARAM_LASTINDEX))
2220 {
2221 max_value = (max_value < 0) ? (0) : (max_value); // Clamp max. to at least zero
2222 ent->character->parameters.maximum[parameter] = max_value;
2223 return 1;
2224 }
2225
2226 return 0;
2227 }
2228
Character_SetParam(struct entity_s * ent,int parameter,float value)2229 int Character_SetParam(struct entity_s *ent, int parameter, float value)
2230 {
2231 if(ent && ent->character && (parameter < PARAM_LASTINDEX))
2232 {
2233 float maximum = ent->character->parameters.maximum[parameter];
2234 value = (value >= 0.0f) ? (value) : (0.0f);
2235 value = (value <= maximum) ? (value) : (maximum);
2236 ent->character->parameters.param[parameter] = value;
2237 return 1;
2238 }
2239
2240 return 0;
2241 }
2242
Character_GetParam(struct entity_s * ent,int parameter)2243 float Character_GetParam(struct entity_s *ent, int parameter)
2244 {
2245 if(ent && ent->character && (parameter < PARAM_LASTINDEX))
2246 {
2247 return ent->character->parameters.param[parameter];
2248 }
2249
2250 return 0;
2251 }
2252
Character_ChangeParam(struct entity_s * ent,int parameter,float value)2253 int Character_ChangeParam(struct entity_s *ent, int parameter, float value)
2254 {
2255 if(ent && ent->character && (parameter < PARAM_LASTINDEX))
2256 {
2257 float maximum = ent->character->parameters.maximum[parameter];
2258 float current = ent->character->parameters.param[parameter] + value;
2259
2260 current = (current >= 0.0f) ? (current) : (0.0f);
2261 current = (current <= maximum) ? (current) : (maximum);
2262 ent->character->parameters.param[parameter] = current;
2263 return (current != 0.0f) && (current != maximum);
2264 }
2265
2266 return 0;
2267 }
2268
2269
Character_IsTargetAccessible(struct entity_s * character,struct entity_s * target)2270 int Character_IsTargetAccessible(struct entity_s *character, struct entity_s *target)
2271 {
2272 int ret = 0;
2273 if(target && (target->state_flags & ENTITY_STATE_ACTIVE))
2274 {
2275 collision_result_t cs;
2276 float dir[3], t;
2277 vec3_sub(dir, target->transform.M4x4 + 12, character->transform.M4x4 + 12);
2278 vec3_norm(dir, t);
2279 t = vec3_dot(character->transform.M4x4 + 4, dir);
2280 ret = (t > 0.0f) && (!Physics_RayTest(&cs, character->obb->centre, target->obb->centre, character->self, COLLISION_FILTER_CHARACTER) || (cs.obj == target->self));
2281 }
2282
2283 return ret;
2284 }
2285
2286
Character_FindTarget(struct entity_s * ent)2287 struct entity_s *Character_FindTarget(struct entity_s *ent)
2288 {
2289 entity_p ret = NULL;
2290 float max_dot = 0.0f;
2291 collision_result_t cs;
2292
2293 for(int ri = -1; ri < ent->self->room->content->near_room_list_size; ++ri)
2294 {
2295 room_p r = (ri >= 0) ? (ent->self->room->content->near_room_list[ri]) : (ent->self->room);
2296 for(engine_container_p cont = r->containers; cont; cont = cont->next)
2297 {
2298 if(cont->object_type == OBJECT_ENTITY)
2299 {
2300 entity_p target = (entity_p)cont->object;
2301 if((target->type_flags & ENTITY_TYPE_ACTOR) && (target->state_flags & ENTITY_STATE_ACTIVE) &&
2302 (!target->character || (target->character->parameters.param[PARAM_HEALTH] > 0.0f)))
2303 {
2304 float dir[3], t;
2305 vec3_sub(dir, target->transform.M4x4 + 12, ent->transform.M4x4 + 12);
2306 vec3_norm(dir, t);
2307 t = vec3_dot(ent->transform.M4x4 + 4, dir);
2308 if((t > max_dot) && (!Physics_RayTest(&cs, ent->obb->centre, target->obb->centre, ent->self, COLLISION_FILTER_CHARACTER) || (cs.obj == target->self)))
2309 {
2310 max_dot = t;
2311 ret = target;
2312 }
2313 }
2314 }
2315 }
2316 }
2317
2318 return ret;
2319 }
2320
2321
Character_SetTarget(struct entity_s * ent,uint32_t target_id)2322 void Character_SetTarget(struct entity_s *ent, uint32_t target_id)
2323 {
2324 if(ent && ent->character)
2325 {
2326 ent->character->target_id = target_id;
2327 }
2328 }
2329
2330
Character_ChangeWeapon(struct entity_s * ent,int weapon_model)2331 void Character_ChangeWeapon(struct entity_s *ent, int weapon_model)
2332 {
2333 if((ent->character->weapon_id != weapon_model) && (ent->character->weapon_id != -weapon_model))
2334 {
2335 ent->character->weapon_id = (weapon_model > 0) ? (-weapon_model) : (weapon_model);
2336 }
2337 }
2338