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