Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
#include "opponent.h"
20 pmbaty 2
#include "brender/brender.h"
1 pmbaty 3
#include "car.h"
4
#include "controls.h"
5
#include "crush.h"
6
#include "displays.h"
7
#include "errors.h"
8
#include "finteray.h"
9
#include "globvars.h"
10
#include "globvrkm.h"
11
#include "globvrme.h"
12
#include "globvrpb.h"
13
#include "harness/trace.h"
14
#include "loading.h"
15
#include "oppoproc.h"
16
#include "pd/sys.h"
17
#include "skidmark.h"
18
#include "trig.h"
19
#include "utility.h"
20
 
21
#include <float.h>
22
#include <stdlib.h>
23
 
24
br_actor* gOppo_path_actor;
25
br_model* gOppo_path_model;
26
br_material* gMat_dk_yel;
27
br_material* gMat_md_yel;
28
br_material* gMat_lt_yel;
29
br_material* gMat_dk_red;
30
br_material* gMat_lt_red;
31
br_material* gMat_dk_grn;
32
br_material* gMat_lt_grn;
33
br_material* gMat_dk_blu;
34
br_material* gMat_lt_blu;
35
br_material* gMat_dk_turq;
36
br_material* gMat_lt_turq;
37
br_material* gMat_dk_gry;
38
br_material* gMat_md_gry;
39
br_material* gMat_lt_gry;
40
int gMellow_opponents;
41
int gTest_toggle;
42
int gAlready_elasticating;
43
int gVertices_used_in_non_edit_paths;
44
int gFaces_used_in_non_edit_paths;
45
int gMats_allocated;
46
int gOppo_paths_shown;
47
int gMade_path_filename;
48
int gBIG_APC_index = -1;
49
char* gPath_section_type_names[3];
50
int gMin_bangness = 100;
51
int gMax_bangness;
52
tU32 gNext_elastication;
53
tU32 gNext_write_during_elastication;
54
char* gCop_name = "Faceless Cop";
55
char* gDrone_name = "Innocent Civilian";
56
int gChallenger_index__opponent = -1; // suffix added to avoid duplicate symbol
57
int gSFS_count;
58
int gSFS_total_cycles;
59
int gSFS_max_cycles;
60
float gOpponent_nastyness_frigger = 1.f;
61
char gOppo_path_filename[256];
62
br_scalar gIn_view_distance;
63
tU8* gBit_per_node;
64
int gGrudge_reduction_per_period;
65
int gSFS_cycles_this_time;
66
br_scalar gMinimum_yness_before_knackerisation;
67
int gWanky_arse_tit_fuck;
68
br_scalar gHead_on_cos_value;
69
tU32 gNext_grudge_reduction;
70
br_scalar gCop_pursuit_speed_percentage_multiplier;
71
br_scalar gDefinite_cop_pursuit_speed;
72
int gAcknowledged_start;
73
int gStart_jumped;
74
int gNum_of_opponents_getting_near;
75
int gNumber_of_cops_before_faffage;
76
int gFirst_frame;
77
tU32 gAcme_frame_count;
78
br_scalar gDefinite_no_cop_pursuit_speed;
79
int gNum_of_opponents_completing_race;
80
int gNum_of_opponents_pursuing;
81
int gActive_car_list_rebuild_required;
82
br_scalar gFrame_period_for_this_munging_in_secs;
83
int gBig_bang;
84
int gProcessing_opponents;
85
tU32 gFrame_period_for_this_munging;
86
tU32 gTime_stamp_for_this_munging;
87
tS16 gMobile_section;
88
 
89
// IDA: void __usercall PointActorAlongThisBloodyVector(br_actor *pThe_actor@<EAX>, br_vector3 *pThe_vector@<EDX>)
90
void PointActorAlongThisBloodyVector(br_actor* pThe_actor, br_vector3* pThe_vector) {
91
    br_transform trans;
92
    LOG_TRACE("(%p, %p)", pThe_actor, pThe_vector);
93
 
94
    trans.type = BR_TRANSFORM_LOOK_UP;
95
    BrVector3Copy(&trans.t.look_up.look, pThe_vector);
96
    BrVector3Set(&trans.t.look_up.up, 0.f, 1.f, 0.f);
97
    BrVector3Copy(&trans.t.look_up.t, &pThe_actor->t.t.translate.t);
98
    BrTransformToTransform(&pThe_actor->t, &trans);
99
}
100
 
101
// IDA: void __usercall ProcessCurrentObjective(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
102
void ProcessCurrentObjective(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
103
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
104
 
105
    switch (pOpponent_spec->current_objective) {
106
    case eOOT_complete_race:
107
        ProcessCompleteRace(pOpponent_spec, pCommand);
108
        break;
109
    case eOOT_pursue_and_twat:
110
        ProcessPursueAndTwat(pOpponent_spec, pCommand);
111
        break;
112
    case eOOT_run_away:
113
        ProcessRunAway(pOpponent_spec, pCommand);
114
        break;
115
    case eOOT_get_near_player:
116
        ProcessGetNearPlayer(pOpponent_spec, pCommand);
117
        break;
118
    case eOOT_levitate:
119
        ProcessLevitate(pOpponent_spec, pCommand);
120
        break;
121
    case eOOT_knackered_and_freewheeling:
122
        // FIXME: is keys correct?
123
        memset(&pOpponent_spec->car_spec->keys, 0, sizeof(pOpponent_spec->car_spec->keys));
124
        pOpponent_spec->car_spec->acc_force = 0.f;
125
        pOpponent_spec->car_spec->brake_force = 0.f;
126
        pOpponent_spec->car_spec->curvature = 0.f;
127
        break;
128
    case eOOT_frozen:
129
        ProcessFrozen(pOpponent_spec, pCommand);
130
        break;
131
    case eOOT_wait_for_some_hapless_sod:
132
        ProcessWaitForSomeHaplessSod(pOpponent_spec, pCommand);
133
        break;
134
    case eOOT_rematerialise:
135
        break;
136
    case eOOT_return_to_start:
137
        ProcessReturnToStart(pOpponent_spec, pCommand);
138
        break;
139
    default:
140
        break;
141
    }
142
}
143
 
144
// IDA: tS16 __usercall ReallocExtraPathNodes@<AX>(int pHow_many_then@<EAX>)
145
tS16 ReallocExtraPathNodes(int pHow_many_then) {
146
    tPath_node* new_nodes;
147
    tS16 first_new_node;
148
    LOG_TRACE("(%d)", pHow_many_then);
149
 
150
    first_new_node = -1;
151
    if (pHow_many_then != 0) {
152
        first_new_node = gProgram_state.AI_vehicles.number_of_path_nodes;
153
        new_nodes = BrMemAllocate(sizeof(tPath_node) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_nodes), kMem_oppo_new_nodes);
154
        memcpy(new_nodes, gProgram_state.AI_vehicles.path_nodes, sizeof(tPath_node) * gProgram_state.AI_vehicles.number_of_path_nodes);
155
        if (gProgram_state.AI_vehicles.path_nodes != NULL) {
156
            BrMemFree(gProgram_state.AI_vehicles.path_nodes);
157
        }
158
        gProgram_state.AI_vehicles.number_of_path_nodes += pHow_many_then;
159
        gProgram_state.AI_vehicles.path_nodes = new_nodes;
160
    }
161
    dr_dprintf(
162
        "ReallocExtraPathNodes(): Allocated %d bytes for %d path nodes",
163
        sizeof(tPath_node) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_nodes),
164
        pHow_many_then);
165
    return first_new_node;
166
}
167
 
168
// IDA: tS16 __usercall ReallocExtraPathSections@<AX>(int pHow_many_then@<EAX>)
169
tS16 ReallocExtraPathSections(int pHow_many_then) {
170
    tPath_section* new_sections;
171
    tS16 first_new_section;
172
    LOG_TRACE("(%d)", pHow_many_then);
173
 
174
    first_new_section = -1;
175
    if (pHow_many_then != 0) {
176
        first_new_section = gProgram_state.AI_vehicles.number_of_path_sections;
177
        new_sections = BrMemAllocate(sizeof(tPath_section) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_sections), kMem_oppo_new_sections);
178
        memcpy(new_sections, gProgram_state.AI_vehicles.path_sections, sizeof(tPath_section) * gProgram_state.AI_vehicles.number_of_path_sections);
179
        if (gProgram_state.AI_vehicles.path_sections != NULL) {
180
            BrMemFree(gProgram_state.AI_vehicles.path_sections);
181
        }
182
        gProgram_state.AI_vehicles.number_of_path_sections += pHow_many_then;
183
        gProgram_state.AI_vehicles.path_sections = new_sections;
184
    }
185
    dr_dprintf(
186
        "ReallocExtraPathSections(): Allocated %d bytes for %d path sections",
187
        sizeof(tPath_section) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_sections),
188
        pHow_many_then);
189
    return first_new_section;
190
}
191
 
192
// IDA: int __usercall PointVisibleFromHere@<EAX>(br_vector3 *pFrom@<EAX>, br_vector3 *pTo@<EDX>)
193
int PointVisibleFromHere(br_vector3* pFrom, br_vector3* pTo) {
194
    br_vector3 from;
195
    br_vector3 dir;
196
    br_vector3 norm;
197
    br_scalar t;
198
    br_material* material;
199
    LOG_TRACE("(%p, %p)", pFrom, pTo);
200
 
201
    BrVector3Sub(&dir, pTo, pFrom);
202
    BrVector3Copy(&from, pFrom);
203
    from.v[1] += 0.15f;
204
    dir.v[1] += 0.15f;
205
    FindFace(&from, &dir, &norm, &t, &material);
206
    return t > 1.0;
207
}
208
 
209
// IDA: tS16 __usercall FindNearestPathNode@<AX>(br_vector3 *pActor_coords@<EAX>, br_scalar *pDistance@<EDX>)
210
tS16 FindNearestPathNode(br_vector3* pActor_coords, br_scalar* pDistance) {
211
    int i;
212
    tS16 nearest_node;
213
    br_scalar distance;
214
    br_vector3 actor_to_node;
215
    LOG_TRACE("(%p, %p)", pActor_coords, pDistance);
216
 
217
    nearest_node = -1;
218
    *pDistance = FLT_MAX;
219
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
220
        BrVector3Sub(&actor_to_node, &gProgram_state.AI_vehicles.path_nodes[i].p, pActor_coords);
221
        distance = BrVector3Length(&actor_to_node);
222
        if (distance < *pDistance && (!gAlready_elasticating || gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] != i)) {
223
            *pDistance = distance;
224
            nearest_node = i;
225
        }
226
    }
227
    return nearest_node;
228
}
229
 
230
// IDA: tS16 __usercall FindNearestPathSection@<AX>(br_vector3 *pActor_coords@<EAX>, br_vector3 *pPath_direction@<EDX>, br_vector3 *pIntersect@<EBX>, br_scalar *pDistance@<ECX>)
231
tS16 FindNearestPathSection(br_vector3* pActor_coords, br_vector3* pPath_direction, br_vector3* pIntersect, br_scalar* pDistance) {
232
    LOG_TRACE("(%p, %p, %p, %p)", pActor_coords, pPath_direction, pIntersect, pDistance);
233
 
234
    return FindNearestGeneralSection(NULL, pActor_coords, pPath_direction, pIntersect, pDistance);
235
}
236
 
237
// IDA: tS16 __usercall FindNearestGeneralSection@<AX>(tCar_spec *pPursuee@<EAX>, br_vector3 *pActor_coords@<EDX>, br_vector3 *pPath_direction@<EBX>, br_vector3 *pIntersect@<ECX>, br_scalar *pDistance)
238
tS16 FindNearestGeneralSection(tCar_spec* pPursuee, br_vector3* pActor_coords, br_vector3* pPath_direction, br_vector3* pIntersect, br_scalar* pDistance) {
239
    int section_no;
240
    int no_sections;
241
    tS16 nearest_node_section_no;
242
    tS16 nearest_section;
243
    br_scalar nearest_node_distance_squared;
244
    br_scalar closest_distance_squared;
245
    br_scalar the_distance_squared;
246
    br_scalar t;
247
    br_scalar length_squared_a;
248
    br_vector3 a;
249
    br_vector3 p;
250
    //br_vector3 wank; // Pierre-Marie Baty -- unused variable
251
    br_vector3 intersect;
252
    br_vector3* start;
253
    br_vector3* finish;
254
    br_vector3* nearest_node_v;
255
#if defined(DETHRACE_FIX_BUGS)
256
    br_vector3 zero_vector;
257
#endif
258
    LOG_TRACE("(%p, %p, %p, %p, %p)", pPursuee, pActor_coords, pPath_direction, pIntersect, pDistance);
259
 
260
    nearest_section = -1;
261
    nearest_node_section_no = -1;
262
    closest_distance_squared = BR_SCALAR_MAX;
263
    nearest_node_distance_squared = BR_SCALAR_MAX;
264
#if defined(DETHRACE_FIX_BUGS)
265
    BrVector3Set(&zero_vector, 0.f, 0.f, 0.f);
266
    nearest_node_v = &zero_vector;
267
#endif
268
 
269
    if (pPursuee != NULL) {
270
        no_sections = pPursuee->my_trail.number_of_nodes - 1;
271
    } else {
272
        no_sections = gProgram_state.AI_vehicles.number_of_path_sections;
273
    }
274
 
275
    for (section_no = 0; section_no < no_sections; section_no++) {
276
        if (pPursuee != NULL) {
277
            start = &pPursuee->my_trail.trail_nodes[section_no];
278
            finish = &pPursuee->my_trail.trail_nodes[section_no + 1];
279
        } else {
280
            start = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p;
281
            finish = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p;
282
        }
283
        if (!gAlready_elasticating || gMobile_section != section_no) {
284
            BrVector3Sub(&a, finish, start);
285
            BrVector3Sub(&p, pActor_coords, start);
286
            the_distance_squared = Vector3DistanceSquared(&p, &a);
287
            if (the_distance_squared < closest_distance_squared) {
288
                closest_distance_squared = the_distance_squared;
289
                nearest_section = section_no;
290
                nearest_node_v = finish;
291
            }
292
            the_distance_squared = BrVector3LengthSquared(&p);
293
            if (the_distance_squared < closest_distance_squared) {
294
                closest_distance_squared = the_distance_squared;
295
                nearest_section = section_no;
296
                nearest_node_v = start;
297
            }
298
            length_squared_a = BrVector3LengthSquared(&a);
299
            if (length_squared_a >= 0.0001f) {
300
                t = BrVector3Dot(&p, &a) / length_squared_a;
301
                if (t >= 0 && t <= 1.f) {
302
                    p.v[0] -= t * a.v[0];
303
                    p.v[1] -= t * a.v[1];
304
                    p.v[2] -= t * a.v[2];
305
                    the_distance_squared = BrVector3LengthSquared(&p);
306
                    if (the_distance_squared < nearest_node_distance_squared) {
307
                        BrVector3Scale(&intersect, &a, t);
308
                        BrVector3Add(pIntersect, start, &intersect);
309
                        BrVector3NormaliseQuick(pPath_direction, &a);
310
                        nearest_node_distance_squared = the_distance_squared;
311
                        nearest_node_section_no = section_no;
312
                    }
313
                }
314
            }
315
        }
316
    }
317
    if (nearest_node_distance_squared > closest_distance_squared) {
318
        nearest_node_section_no = nearest_section;
319
        if (pPursuee != NULL) {
320
            start = &pPursuee->my_trail.trail_nodes[nearest_section];
321
            finish = &pPursuee->my_trail.trail_nodes[nearest_section + 1];
322
        } else {
323
            start = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[nearest_section].node_indices[0]].p;
324
            finish = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[nearest_section].node_indices[1]].p;
325
        }
326
        BrVector3Sub(&p, finish, start);
327
        BrVector3NormaliseQuick(pPath_direction, &p);
328
        BrVector3Copy(pIntersect, nearest_node_v);
329
        *pDistance = sqrtf(closest_distance_squared);
330
    } else {
331
        *pDistance = sqrtf(nearest_node_distance_squared);
332
    }
333
    if (pPursuee != NULL) {
334
        nearest_node_section_no += 15000;
335
    }
336
    return nearest_node_section_no;
337
}
338
 
339
// IDA: void __usercall DeadStopCar(tCar_spec *pCar_spec@<EAX>)
340
void DeadStopCar(tCar_spec* pCar_spec) {
341
    LOG_TRACE("(%p)", pCar_spec);
342
 
343
    pCar_spec->acc_force = 0.f;
344
    pCar_spec->brake_force = 0.f;
345
    pCar_spec->curvature = 0.f;
346
    pCar_spec->gear = 0;
347
    pCar_spec->revs = 0.f;
348
    BrVector3Set(&pCar_spec->omega, 0.f, 0.f, 0.f);
349
    BrVector3Set(&pCar_spec->v, 0.f, 0.f, 0.f);
350
}
351
 
352
// IDA: void __usercall TurnOpponentPhysicsOn(tOpponent_spec *pOpponent_spec@<EAX>)
353
void TurnOpponentPhysicsOn(tOpponent_spec* pOpponent_spec) {
354
    LOG_TRACE("(%p)", pOpponent_spec);
355
 
356
    if (pOpponent_spec->physics_me == 0) {
357
        pOpponent_spec->physics_me = 1;
358
        gActive_car_list_rebuild_required = 1;
359
    }
360
}
361
 
362
// IDA: void __usercall TurnOpponentPhysicsOff(tOpponent_spec *pOpponent_spec@<EAX>)
363
void TurnOpponentPhysicsOff(tOpponent_spec* pOpponent_spec) {
364
    LOG_TRACE("(%p)", pOpponent_spec);
365
 
366
    DeadStopCar(pOpponent_spec->car_spec);
367
    if (pOpponent_spec->physics_me) {
368
        pOpponent_spec->physics_me = 0;
369
        gActive_car_list_rebuild_required = 1;
370
    }
371
}
372
 
373
// IDA: void __cdecl NewObjective(tOpponent_spec *pOpponent_spec, tOpponent_objective_type pObjective_type, ...)
374
void NewObjective(tOpponent_spec* pOpponent_spec, tOpponent_objective_type pObjective_type, ...) {
375
    va_list marker;
376
    LOG_TRACE("(%p, %d)", pOpponent_spec, pObjective_type);
377
 
378
    if (pOpponent_spec->current_objective != eOOT_none) {
379
        ProcessCurrentObjective(pOpponent_spec, ePOC_die);
380
    }
381
    pOpponent_spec->current_objective = pObjective_type;
382
    pOpponent_spec->time_this_objective_started = gTime_stamp_for_this_munging;
383
    pOpponent_spec->time_for_this_objective_to_finish = gTime_stamp_for_this_munging + IRandomBetween(30, 180) * 1000;
384
    if (pObjective_type == eOOT_pursue_and_twat) {
385
        pOpponent_spec->time_for_this_objective_to_finish += 90000;
386
    }
387
    switch (pObjective_type) {
388
    case eOOT_complete_race:
389
        gNum_of_opponents_completing_race++;
390
        break;
391
    case eOOT_pursue_and_twat:
392
        va_start(marker, pObjective_type);
393
        pOpponent_spec->pursue_car_data.pursuee = va_arg(marker, tCar_spec*);
394
        va_end(marker);
395
        break;
396
    case eOOT_get_near_player:
397
        gNum_of_opponents_getting_near++;
398
        break;
399
    default:
400
        break;
401
    }
402
    dr_dprintf("%s: NewObjective() - type %d", pOpponent_spec->car_spec->driver_name, pObjective_type);
403
    ProcessCurrentObjective(pOpponent_spec, ePOC_start);
404
}
405
 
406
// IDA: void __usercall CalcRaceRoute(tOpponent_spec *pOpponent_spec@<EAX>)
407
void CalcRaceRoute(tOpponent_spec* pOpponent_spec) {
408
    tS16 section_no;
409
    //tS16 section_no_index; // Pierre-Marie Baty -- unused variable
410
    tS16 node_no;
411
    tS16 race_section_count;
412
    tS16 normal_section_ok_direction_count;
413
    tS16 normal_section_wrong_direction_count;
414
    tS16 temp_section_array[8];
415
    br_scalar distance;
416
    br_vector3 direction_v;
417
    br_vector3 intersect;
418
    //char str[256]; // Pierre-Marie Baty -- unused variable
419
    //char work_str[32]; // Pierre-Marie Baty -- unused variable
420
    int i;
421
    LOG_TRACE("(%p)", pOpponent_spec);
422
 
423
    if (pOpponent_spec->nnext_sections >= COUNT_OF(pOpponent_spec->next_sections)) {
424
        dr_dprintf("%s: CalcRaceRoute() - Pissing off 'cos projected route full up", pOpponent_spec->car_spec->driver_name);
425
        return;
426
    }
427
    if (pOpponent_spec->nnext_sections == 0) {
428
        dr_dprintf("%s: CalcRaceRoute() - Projected route empty; starting from nearest section", pOpponent_spec->car_spec->driver_name);
429
        pOpponent_spec->complete_race_data.finished_calcing_race_route = 0;
430
        pOpponent_spec->complete_race_data.found_race_section = 0;
431
        section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
432
        if (section_no < 0) {
433
            return;
434
        }
435
        AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
436
        if (gProgram_state.AI_vehicles.path_sections[section_no].type == ePST_race_path) {
437
            pOpponent_spec->complete_race_data.found_race_section = 1;
438
        }
439
    }
440
    while (pOpponent_spec->nnext_sections < COUNT_OF(pOpponent_spec->next_sections) && !pOpponent_spec->complete_race_data.finished_calcing_race_route) {
441
        node_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no].node_indices[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction];
442
        race_section_count = 0;
443
        normal_section_ok_direction_count = 0;
444
        normal_section_wrong_direction_count = 0;
445
        for (i = 0; i < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; i++) {
446
            section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[i];
447
            if (pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no != section_no) {
448
                if (gProgram_state.AI_vehicles.path_sections[section_no].type == 1 && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] == node_no) {
449
                    pOpponent_spec->complete_race_data.found_race_section = 1;
450
                    temp_section_array[race_section_count] = section_no;
451
                    race_section_count++;
452
                } else if (race_section_count == 0 && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] == node_no) {
453
                    temp_section_array[normal_section_ok_direction_count] = section_no;
454
                    normal_section_ok_direction_count++;
455
                } else if (race_section_count == 0 && normal_section_ok_direction_count == 0 && (!gProgram_state.AI_vehicles.path_sections[section_no].one_way || gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no)) {
456
                    temp_section_array[normal_section_wrong_direction_count] = section_no;
457
                    normal_section_wrong_direction_count++;
458
                }
459
            }
460
        }
461
        if (race_section_count != 0) {
462
            AddToOpponentsProjectedRoute(pOpponent_spec, temp_section_array[IRandomBetween(0, race_section_count - 1)], 1);
463
        } else if (normal_section_ok_direction_count != 0) {
464
            AddToOpponentsProjectedRoute(pOpponent_spec, temp_section_array[IRandomBetween(0, normal_section_ok_direction_count - 1)], 1);
465
        } else if (normal_section_wrong_direction_count != 0) {
466
            AddToOpponentsProjectedRoute(pOpponent_spec, temp_section_array[IRandomBetween(0, normal_section_wrong_direction_count - 1)], 1);
467
        } else if (pOpponent_spec->complete_race_data.found_race_section) {
468
            pOpponent_spec->complete_race_data.finished_calcing_race_route = 1;
469
        } else {
470
            AddToOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no, pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction == 0);
471
        }
472
    }
473
}
474
 
475
// IDA: void __usercall TopUpRandomRoute(tOpponent_spec *pOpponent_spec@<EAX>, int pSections_to_add@<EDX>)
476
void TopUpRandomRoute(tOpponent_spec* pOpponent_spec, int pSections_to_add) {
477
    tS16 section_no;
478
    tS16 node_no;
479
    tS16 temp_section_array[8];
480
    int i;
481
    int target;
482
    int num_of_temp_sections;
483
    int direction;
484
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSections_to_add);
485
 
486
    if (!pSections_to_add) {
487
        PDEnterDebugger("TopUpRandomRoute() called with no seed (woof, bark, etc.)");
488
    }
489
    if (pSections_to_add >= 0) {
490
        target = MIN(pSections_to_add + pOpponent_spec->nnext_sections, 10);
491
    } else {
492
        target = 10;
493
    }
494
    while (pOpponent_spec->nnext_sections < target) {
495
        node_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no].node_indices[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction];
496
        if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections <= 1) {
497
            section_no = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no;
498
            direction = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction == 0;
499
        } else {
500
            num_of_temp_sections = 0;
501
            for (i = 0; i < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; i++) {
502
                section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[i];
503
                if (pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no != section_no
504
                    && (!gProgram_state.AI_vehicles.path_sections[section_no].one_way || gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no)
505
                    && (pOpponent_spec->cheating || gProgram_state.AI_vehicles.path_sections[section_no].type != ePST_cheat_only)) {
506
                    temp_section_array[num_of_temp_sections] = section_no;
507
                    num_of_temp_sections++;
508
                }
509
            }
510
 
511
            if (num_of_temp_sections == 0) {
512
                section_no = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no;
513
                direction = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction == 0;
514
            } else if (num_of_temp_sections == 1) {
515
                section_no = temp_section_array[0];
516
                direction = gProgram_state.AI_vehicles.path_sections[temp_section_array[0]].node_indices[1] != node_no;
517
            } else {
518
                section_no = temp_section_array[IRandomBetween(0, num_of_temp_sections - 1)];
519
                direction = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no;
520
            }
521
        }
522
        AddToOpponentsProjectedRoute(pOpponent_spec, section_no, direction);
523
    }
524
}
525
 
526
// IDA: int __usercall SearchForSection@<EAX>(tRoute_section *pTemp_store@<EAX>, tRoute_section *pPerm_store@<EDX>, int *pNum_of_perm_store_sections@<EBX>, tS16 pTarget_section@<ECX>, int pDepth, br_scalar pDistance_so_far, tOpponent_spec *pOpponent_spec)
527
int SearchForSection(tRoute_section* pTemp_store, tRoute_section* pPerm_store, int* pNum_of_perm_store_sections, tS16 pTarget_section, int pDepth, br_scalar pDistance_so_far, tOpponent_spec* pOpponent_spec) {
528
    static br_scalar shortest_dist;
529
    static int routes_found;
530
    //char depth_indent[32]; // Pierre-Marie Baty -- unused variable
531
    int direction;
532
    tPath_node* node_ptr;
533
    tS16 node_no;
534
    tS16 section_no;
535
    tS16 section_no_index;
536
    br_scalar distance_so_far;
537
    LOG_TRACE("(%p, %p, %p, %d, %d, %f, %p)", pTemp_store, pPerm_store, pNum_of_perm_store_sections, pTarget_section, pDepth, pDistance_so_far, pOpponent_spec);
538
 
539
    // added by dethrace for readability (?)
540
    tS16 section_no_dir_index;
541
 
542
    gSFS_cycles_this_time++;
543
    if (pDepth == 1) {
544
        memset(gBit_per_node, 0, (gProgram_state.AI_vehicles.number_of_path_nodes + 7) / 8);
545
        shortest_dist = BR_SCALAR_MAX;
546
        routes_found = 0;
547
        *pNum_of_perm_store_sections = 0;
548
    }
549
    if (pDepth >= 10) {
550
        return 0;
551
    }
552
    node_no = gProgram_state.AI_vehicles.path_sections[pTemp_store[pDepth - 1].section_no].node_indices[pTemp_store[pDepth - 1].direction];
553
    node_ptr = &gProgram_state.AI_vehicles.path_nodes[node_no];
554
    gBit_per_node[node_no / 8] |= 1 << (node_no % 8);
555
    for (section_no_index = 0; section_no_index < node_ptr->number_of_sections; section_no_index++) {
556
 
557
        section_no = node_ptr->sections[section_no_index];
558
        direction = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no;
559
        section_no_dir_index = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[direction];
560
 
561
        // int b = BYTE4(v8);
562
        // int y = (int)(((BYTE4(v8) ^ (((BYTE4(v8) ^ v8) - BYTE4(v8)) & 7)) - BYTE4(v8)));
563
        // int val = valx(v8);
564
        // LOG_DEBUG("val %d, b %d, y %d", val, b, y);
565
        // int x = ((BYTE4(v8) ^ (((BYTE4(v8) ^ v8) - BYTE4(v8)) & 7)) - BYTE4(v8));
566
        // int x2 = v8 & 7;
567
        // if (x != x2 || val != x) {
568
        //     TELL_ME_IF_WE_PASS_THIS_WAY();
569
        // }
570
        if ((gBit_per_node[section_no_dir_index / 8] & (1 << (section_no_dir_index & 7))) == 0
571
            && (!gProgram_state.AI_vehicles.path_sections[section_no].one_way || direction)
572
            && (pOpponent_spec->cheating || gProgram_state.AI_vehicles.path_sections[section_no].type != ePST_cheat_only)) {
573
 
574
            pTemp_store[pDepth].section_no = section_no;
575
            pTemp_store[pDepth].direction = direction;
576
            distance_so_far = gProgram_state.AI_vehicles.path_sections[section_no].length + pDistance_so_far;
577
 
578
            if (pTarget_section == section_no && distance_so_far < shortest_dist) {
579
                shortest_dist = distance_so_far;
580
                *pNum_of_perm_store_sections = pDepth + 1;
581
                memcpy(pPerm_store, pTemp_store, sizeof(tRoute_section) * *pNum_of_perm_store_sections);
582
                // dword_530DD4 = ++routes_found
583
                routes_found++;
584
                if (routes_found >= 2) {
585
                    return 1;
586
                }
587
                break;
588
            }
589
 
590
            if (pDepth < 9
591
                && SearchForSection(pTemp_store, pPerm_store, pNum_of_perm_store_sections, pTarget_section, pDepth + 1, distance_so_far, pOpponent_spec)) {
592
                return 1;
593
            }
594
        }
595
    }
596
    gBit_per_node[node_no / 8] &= ~(1 << (node_no % 8));
597
    return 0;
598
}
599
 
600
// IDA: void __usercall CalcGetNearPlayerRoute(tOpponent_spec *pOpponent_spec@<EAX>, tCar_spec *pPlayer@<EDX>)
601
void CalcGetNearPlayerRoute(tOpponent_spec* pOpponent_spec, tCar_spec* pPlayer) {
602
    //int i; // Pierre-Marie Baty -- unused variable
603
    //int pass_2_depth; // Pierre-Marie Baty -- unused variable
604
    //int sections_away; // Pierre-Marie Baty -- unused variable
605
    int num_of_perm_store_sections;
606
    int sections_to_copy;
607
    int fuck_it;
608
    tS16 section_no;
609
    tS16 players_section;
610
    br_vector3 section_v;
611
    br_vector3 intersect;
612
    br_scalar distance;
613
    tRoute_section temp_store[10];
614
    tRoute_section perm_store[10];
615
    //char work_str[32]; // Pierre-Marie Baty -- unused variable
616
    //char str[256]; // Pierre-Marie Baty -- unused variable
617
    LOG_TRACE("(%p, %p)", pOpponent_spec, pPlayer);
618
 
619
    fuck_it = 0;
620
    if (pOpponent_spec->nnext_sections >= COUNT_OF(pOpponent_spec->next_sections)) {
621
        dr_dprintf("%s: CalcGetNearPlayerRoute() - Quitting because route full up", pOpponent_spec->car_spec->driver_name);
622
        return;
623
    }
624
    players_section = FindNearestPathSection(&pPlayer->car_master_actor->t.t.translate.t, &section_v, &intersect, &distance);
625
    if (players_section < 0) {
626
        PDEnterDebugger("No path section near player. THIS CAN'T HAPPEN!");
627
        return;
628
    }
629
    if (pOpponent_spec->players_section_when_last_calced_full_path != players_section) {
630
        dr_dprintf("%s: CalcGetNearPlayerRoute() - Player has moved since last time (section #%d; was #%d)", pOpponent_spec->car_spec->driver_name, players_section, pOpponent_spec->players_section_when_last_calced_full_path);
631
        ClearOpponentsProjectedRoute(pOpponent_spec);
632
    }
633
    if (pOpponent_spec->nnext_sections == 0) {
634
        pOpponent_spec->players_section_when_last_calced_full_path = players_section;
635
        dr_dprintf("%s: CalcGetNearPlayerRoute() - Empty route; setting players_section_when_last_calced_full_path to #%d", pOpponent_spec->car_spec->driver_name, pOpponent_spec->players_section_when_last_calced_full_path);
636
        section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &section_v, &intersect, &distance);
637
        if (section_no < 0) {
638
            return;
639
        }
640
        AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
641
    }
642
    if (pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no == players_section) {
643
        dr_dprintf("%s: CalcGetNearPlayerRoute() - Last section is player's section (%d), so tack a random one on t'end", pOpponent_spec->car_spec->driver_name, players_section);
644
        TopUpRandomRoute(pOpponent_spec, 1);
645
    }
646
    while (pOpponent_spec->nnext_sections < 6 && !fuck_it) {
647
        temp_store[0] = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1];
648
        dr_dprintf("%s: CalcGetNearPlayerRoute() - In loop; our section #%d, player's section #%d", pOpponent_spec->car_spec->driver_name, temp_store[0].section_no, players_section);
649
        gSFS_count++;
650
        gSFS_cycles_this_time = 0;
651
        SearchForSection(temp_store, perm_store, &num_of_perm_store_sections, players_section, 1, 0.f, pOpponent_spec);
652
        gSFS_total_cycles += gSFS_cycles_this_time;
653
        if (gSFS_max_cycles < gSFS_cycles_this_time) {
654
            gSFS_max_cycles = gSFS_cycles_this_time;
655
        }
656
        dr_dprintf(">>>SearchForSection() - max %d, avg %.1f", gSFS_max_cycles, gSFS_total_cycles / (float)gSFS_count);
657
        if (num_of_perm_store_sections <= 1) {
658
            dr_dprintf("%s: CalcGetNearPlayerRoute() - SearchForSection() produced bugger all", pOpponent_spec->car_spec->driver_name);
659
            fuck_it = 1;
660
            if (pOpponent_spec->nnext_sections <= 4) {
661
                TopUpRandomRoute(pOpponent_spec, 4 - pOpponent_spec->nnext_sections + 4);
662
            }
663
        } else {
664
            sections_to_copy = MIN((int) COUNT_OF(pOpponent_spec->next_sections) - pOpponent_spec->nnext_sections, num_of_perm_store_sections - 1); // Pierre-Marie Baty -- added type cast
665
            memcpy(&pOpponent_spec->next_sections[pOpponent_spec->nnext_sections], &perm_store[1], sizeof(tRoute_section) * sections_to_copy);
666
            pOpponent_spec->nnext_sections += sections_to_copy;
667
            TopUpRandomRoute(pOpponent_spec, 1);
668
        }
669
    }
670
}
671
 
672
// IDA: void __usercall CalcReturnToStartPointRoute(tOpponent_spec *pOpponent_spec@<EAX>)
673
void CalcReturnToStartPointRoute(tOpponent_spec* pOpponent_spec) {
674
    //int i; // Pierre-Marie Baty -- unused variable
675
    //int pass_2_depth; // Pierre-Marie Baty -- unused variable
676
    //int sections_away; // Pierre-Marie Baty -- unused variable
677
    int num_of_perm_store_sections;
678
    int sections_to_copy;
679
    tS16 section_no;
680
    br_vector3 intersect;
681
    br_vector3 section_v;
682
    br_scalar distance;
683
    tRoute_section temp_store[10];
684
    tRoute_section perm_store[10];
685
    LOG_TRACE("(%p)", pOpponent_spec);
686
 
687
    ClearOpponentsProjectedRoute(pOpponent_spec);
688
    section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &section_v, &intersect, &distance);
689
    distance = BrVector3Length(&section_v);
690
    BrVector3Normalise(&section_v, &section_v);
691
 
692
    if (BrVector3Dot(&pOpponent_spec->car_spec->direction, &section_v) <= 0.0f) {
693
        AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 0);
694
    } else {
695
        AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
696
    }
697
    temp_store[0] = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1];
698
    gSFS_count++;
699
    gSFS_cycles_this_time = 0;
700
    SearchForSection(temp_store, perm_store, &num_of_perm_store_sections, pOpponent_spec->return_to_start_data.section_no, 1, 0.0f, pOpponent_spec);
701
    gSFS_total_cycles += gSFS_cycles_this_time;
702
    if (gSFS_max_cycles < gSFS_cycles_this_time) {
703
        gSFS_max_cycles = gSFS_cycles_this_time;
704
    }
705
    if (num_of_perm_store_sections <= 1) {
706
        if (pOpponent_spec->nnext_sections <= 6) {
707
            TopUpRandomRoute(pOpponent_spec, 4 - pOpponent_spec->nnext_sections + 4);
708
        }
709
    } else {
710
        sections_to_copy = 10 - pOpponent_spec->nnext_sections;
711
        if (sections_to_copy >= num_of_perm_store_sections - 1) {
712
            sections_to_copy = num_of_perm_store_sections - 1;
713
        }
714
        memcpy(&pOpponent_spec->next_sections[pOpponent_spec->nnext_sections], &perm_store[1], sizeof(tRoute_section) * sections_to_copy);
715
        pOpponent_spec->nnext_sections += sections_to_copy;
716
        TopUpRandomRoute(pOpponent_spec, 1);
717
    }
718
}
719
 
720
// IDA: void __usercall ClearOpponentsProjectedRoute(tOpponent_spec *pOpponent_spec@<EAX>)
721
void ClearOpponentsProjectedRoute(tOpponent_spec* pOpponent_spec) {
722
    LOG_TRACE("(%p)", pOpponent_spec);
723
 
724
    pOpponent_spec->nnext_sections = 0;
725
}
726
 
727
// IDA: int __usercall AddToOpponentsProjectedRoute@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection_no@<EDX>, int pDirection@<EBX>)
728
int AddToOpponentsProjectedRoute(tOpponent_spec* pOpponent_spec, tS16 pSection_no, int pDirection) {
729
    LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection_no, pDirection);
730
 
731
    if (pOpponent_spec->nnext_sections >= COUNT_OF(pOpponent_spec->next_sections)) {
732
        return 0;
733
    }
734
    pOpponent_spec->next_sections[pOpponent_spec->nnext_sections].section_no = pSection_no;
735
    pOpponent_spec->next_sections[pOpponent_spec->nnext_sections].direction = pDirection;
736
    pOpponent_spec->nnext_sections++;
737
    return 1;
738
}
739
 
740
// IDA: int __usercall ShiftOpponentsProjectedRoute@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pPlaces@<EDX>)
741
int ShiftOpponentsProjectedRoute(tOpponent_spec* pOpponent_spec, int pPlaces) {
742
    int i;
743
    LOG_TRACE("(%p, %d)", pOpponent_spec, pPlaces);
744
 
745
    if (pOpponent_spec->nnext_sections <= pPlaces) {
746
        return 0;
747
    }
748
    for (i = 0; i < (int) COUNT_OF(pOpponent_spec->next_sections) - pPlaces; i++) { // Pierre-Marie Baty -- added type cast
749
        pOpponent_spec->next_sections[i].section_no = pOpponent_spec->next_sections[pPlaces + i].section_no;
750
        pOpponent_spec->next_sections[i].direction = pOpponent_spec->next_sections[pPlaces + i].direction;
751
    }
752
    pOpponent_spec->nnext_sections -= pPlaces;
753
    return 1;
754
}
755
 
756
// IDA: void __usercall StunTheBugger(tOpponent_spec *pOpponent_spec@<EAX>, int pMilliseconds@<EDX>)
757
void StunTheBugger(tOpponent_spec* pOpponent_spec, int pMilliseconds) {
758
    LOG_TRACE("(%p, %d)", pOpponent_spec, pMilliseconds);
759
 
760
    pOpponent_spec->car_spec->acc_force = 0.f;
761
    pOpponent_spec->car_spec->brake_force = 0.f;
762
    pOpponent_spec->car_spec->curvature = 0.f;
763
    pOpponent_spec->stun_time_ends = MAX(gTime_stamp_for_this_munging + pMilliseconds, pOpponent_spec->stun_time_ends);
764
}
765
 
766
// IDA: void __usercall UnStunTheBugger(tOpponent_spec *pOpponent_spec@<EAX>)
767
void UnStunTheBugger(tOpponent_spec* pOpponent_spec) {
768
    LOG_TRACE("(%p)", pOpponent_spec);
769
 
770
    pOpponent_spec->stun_time_ends = 0;
771
}
772
 
773
// IDA: void __usercall ProcessCompleteRace(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
774
void ProcessCompleteRace(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
775
    //br_vector3* initial_pos; // Pierre-Marie Baty -- unused variable
776
    //br_actor* car_actor; // Pierre-Marie Baty -- unused variable
777
    //tComplete_race_data* data; // Pierre-Marie Baty -- unused variable
778
    int res;
779
    //char str[256]; // Pierre-Marie Baty -- unused variable
780
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
781
 
782
    switch (pCommand) {
783
    case ePOC_start:
784
        dr_dprintf("%s: ProcessCompleteRace() - new objective started", pOpponent_spec->car_spec->driver_name);
785
        ClearOpponentsProjectedRoute(pOpponent_spec);
786
        CalcRaceRoute(pOpponent_spec);
787
        ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
788
        break;
789
    case ePOC_run:
790
        if (pOpponent_spec->follow_path_data.section_no > 20000) {
791
            ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
792
            pOpponent_spec->follow_path_data.section_no = 20000;
793
        }
794
        res = ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0);
795
        if (pOpponent_spec->nnext_sections == 0 || res == eFPR_end_of_path) {
796
            dr_dprintf("%s: Giving up following race path because ran out of race path", pOpponent_spec->car_spec->driver_name);
797
            NewObjective(pOpponent_spec, eOOT_get_near_player);
798
        }
799
        if (res != eFPR_OK) {
800
            if (res == eFPR_given_up) {
801
                dr_dprintf("%s: Giving up complete_race because ProcessFollowPath() gave up", pOpponent_spec->car_spec->driver_name);
802
            } else {
803
                dr_dprintf("%s: Giving up complete_race because reached end", pOpponent_spec->car_spec->driver_name);
804
            }
805
            ObjectiveComplete(pOpponent_spec);
806
        }
807
        if (gTime_stamp_for_this_munging > pOpponent_spec->time_this_objective_started + 20000) {
808
            dr_dprintf("%s: Time to give up complete_race. Might be back in a sec, though!", pOpponent_spec->car_spec->driver_name);
809
            ObjectiveComplete(pOpponent_spec);
810
        }
811
        if (pOpponent_spec->nnext_sections < 5 && !pOpponent_spec->complete_race_data.finished_calcing_race_route) {
812
            CalcRaceRoute(pOpponent_spec);
813
        }
814
        break;
815
    default:
816
        break;
817
    }
818
}
819
 
820
// IDA: void __usercall StartRecordingTrail(tCar_spec *pPursuee@<EAX>)
821
void StartRecordingTrail(tCar_spec* pPursuee) {
822
    //int i; // Pierre-Marie Baty -- unused variable
823
    LOG_TRACE("(%p)", pPursuee);
824
 
825
    if (pPursuee->no_of_processes_recording_my_trail == 0) {
826
        dr_dprintf("StartRecordingTrail - starting from scratch");
827
        pPursuee->no_of_processes_recording_my_trail = 1;
828
        pPursuee->my_trail.nodes_shifted_this_frame = 0;
829
        pPursuee->my_trail.has_deviated_recently = 0;
830
        pPursuee->my_trail.number_of_nodes = 2;
831
        pPursuee->my_trail.time_of_next_recording = gTime_stamp_for_this_munging + 500;
832
        BrVector3Copy(&pPursuee->my_trail.base_heading, &pPursuee->direction);
833
        BrVector3Copy(&pPursuee->my_trail.trail_nodes[0], &pPursuee->car_master_actor->t.t.translate.t);
834
        BrVector3Copy(&pPursuee->my_trail.trail_nodes[1], &pPursuee->car_master_actor->t.t.translate.t);
835
        pPursuee->my_trail.trail_nodes[0].v[2] += 0.2f;
836
    } else {
837
        dr_dprintf("StartRecordingTrail - another pursuer attaching");
838
        pPursuee->no_of_processes_recording_my_trail++;
839
    }
840
}
841
 
842
// IDA: void __usercall RecordNextTrailNode(tCar_spec *pPursuee@<EAX>)
843
void RecordNextTrailNode(tCar_spec* pPursuee) {
844
    tPursuee_trail* trail;
845
    br_vector3 start1;
846
    br_vector3 finish1;
847
    br_vector3 start2;
848
    br_vector3 finish2;
849
    br_vector3 offset_v;
850
    br_vector3 car_to_last_point_v;
851
    br_scalar length;
852
    int visible;
853
    LOG_TRACE("(%p)", pPursuee);
854
 
855
    trail = &pPursuee->my_trail;
856
    if (trail->time_of_next_recording >= gTime_stamp_for_this_munging) {
857
        return;
858
    }
859
    trail->time_of_next_recording = gTime_stamp_for_this_munging + 500;
860
    trail->nodes_shifted_this_frame = 0;
861
    if (BrVector3Dot(&trail->base_heading, &pPursuee->direction) < FastScalarCos(30)) {
862
        trail->has_deviated_recently = 1;
863
    }
864
    BrVector3Sub(&car_to_last_point_v, &trail->trail_nodes[trail->number_of_nodes - 2], &pPursuee->car_master_actor->t.t.translate.t);
865
    length = BrVector3Length(&car_to_last_point_v);
866
    if (length < 0.3f) {
867
        return;
868
    }
869
    CalcNegativeXVector(&offset_v, &trail->trail_nodes[trail->number_of_nodes - 2], &pPursuee->car_master_actor->t.t.translate.t, 0.5f);
870
 
871
    BrVector3Add(&start1, &trail->trail_nodes[trail->number_of_nodes - 2], &offset_v);
872
    BrVector3Add(&finish1, &pPursuee->car_master_actor->t.t.translate.t, &offset_v);
873
    BrVector3Sub(&start2, &trail->trail_nodes[trail->number_of_nodes - 2], &offset_v);
874
    BrVector3Sub(&finish2, &pPursuee->car_master_actor->t.t.translate.t, &offset_v);
875
    visible = 1;
876
    if ((trail->has_deviated_recently
877
            || !(visible = PointVisibleFromHere(&start1, &finish1))
878
            || !(visible = PointVisibleFromHere(&start2, &finish2))
879
            || !(visible = PointVisibleFromHere(&trail->trail_nodes[trail->number_of_nodes - 2], &pPursuee->car_master_actor->t.t.translate.t)))
880
        && ((visible && length > 2.0f) || (!visible && length > 1.5f))) {
881
        if (trail->number_of_nodes >= COUNT_OF(trail->trail_nodes)) {
882
            memmove(trail->trail_nodes, &trail->trail_nodes[1], (COUNT_OF(trail->trail_nodes) - 1) * sizeof(trail->trail_nodes[0]));
883
            trail->nodes_shifted_this_frame = 1;
884
        } else {
885
            trail->number_of_nodes++;
886
        }
887
        trail->has_deviated_recently = 0;
888
        BrVector3Copy(&trail->base_heading, &pPursuee->direction);
889
    }
890
    BrVector3Copy(&trail->trail_nodes[trail->number_of_nodes - 1], &pPursuee->car_master_actor->t.t.translate.t);
891
}
892
 
893
// IDA: tS16 __usercall FindNearestTrailSection@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tCar_spec *pPursuee@<EDX>, br_vector3 *pSection_v@<EBX>, br_vector3 *pIntersect@<ECX>, br_scalar *pDistance)
894
tS16 FindNearestTrailSection(tOpponent_spec* pOpponent_spec, tCar_spec* pPursuee, br_vector3* pSection_v, br_vector3* pIntersect, br_scalar* pDistance) {
895
    LOG_TRACE("(%p, %p, %p, %p, %p)", pOpponent_spec, pPursuee, pSection_v, pIntersect, pDistance);
896
 
897
    return FindNearestGeneralSection(pPursuee, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, pSection_v, pIntersect, pDistance);
898
}
899
 
900
// IDA: tS16 __usercall CalcNextTrailSection@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, int pSection@<EDX>)
901
tS16 CalcNextTrailSection(tOpponent_spec* pOpponent_spec, int pSection) {
902
    int section_no;
903
    tPursuee_trail* trail;
904
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
905
 
906
    trail = &pOpponent_spec->pursue_car_data.pursuee->my_trail;
907
    section_no = pSection - 15000;
908
 
909
    if (trail->number_of_nodes - 2 > section_no) {
910
        return pSection + 1;
911
    }
912
    return -1;
913
}
914
 
915
// IDA: void __usercall ProcessPursueAndTwat(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
916
void ProcessPursueAndTwat(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
917
    tPursue_car_data* data;
918
    br_vector3 wank;
919
    br_vector3 section_v;
920
    br_vector3 intersect;
921
    br_scalar d;
922
    br_scalar s;
923
    br_scalar t;
924
    br_scalar distance;
925
    tFollow_path_result res;
926
    char str[256];
927
    tS16 section_no;
928
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
929
 
930
    data = &pOpponent_spec->pursue_car_data;
931
    if (pCommand == ePOC_start) {
932
        dr_dprintf("%s: ProcessPursueAndTwat() - new objective started", pOpponent_spec->car_spec->driver_name);
933
        data->direct_line_nodes[0].number_of_sections = 1;
934
        data->direct_line_nodes[0].sections[0] = 10000;
935
        data->direct_line_nodes[1].number_of_sections = 1;
936
        data->direct_line_nodes[1].sections[0] = 10000;
937
        data->direct_line_section.node_indices[0] = 10000;
938
        data->direct_line_section.node_indices[1] = 10001;
939
        data->direct_line_section.min_speed[0] = 0;
940
        data->direct_line_section.min_speed[1] = 0;
941
        data->direct_line_section.max_speed[0] = -1;
942
        data->direct_line_section.max_speed[1] = -1;
943
        data->direct_line_section.width = 0.5f;
944
        data->direct_line_section.type = ePST_normal;
945
        data->start_backup_time = 0;
946
        data->time_of_next_visibility_check = 0;
947
        data->time_pursuee_last_visible = 0;
948
        data->time_last_twatted_em = 0;
949
        data->time_last_away_from_pursuee = gTime_stamp_for_this_munging;
950
        data->state = ePCS_what_now;
951
        return;
952
    }
953
 
954
    if (pCommand != ePOC_run) {
955
        return;
956
    }
957
 
958
    if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec) && pOpponent_spec->distance_from_home > 75.0f) {
959
        dr_dprintf("%s: Completing pursuit objective because I'm out of my precinct", pOpponent_spec->car_spec->driver_name);
960
        NewObjective(pOpponent_spec, eOOT_return_to_start);
961
        return;
962
    }
963
 
964
    data->direct_line_section.length = MAX(pOpponent_spec->player_to_oppo_d, 3.0f);
965
    if (pOpponent_spec->player_to_oppo_d > 3.0f) {
966
        data->time_last_away_from_pursuee = gTime_stamp_for_this_munging;
967
    }
968
    if (gOpponents[pOpponent_spec->index].psyche.grudge_against_player < 15u) {
969
        dr_dprintf("%s: Completing pursuit objective because I'm happy now", pOpponent_spec->car_spec->driver_name);
970
        ObjectiveComplete(pOpponent_spec);
971
        return;
972
    }
973
    if (data->state != ePCS_backing_up) {
974
        if (data->time_last_twatted_em + 1000 >= gTime_stamp_for_this_munging || data->time_last_twatted_em + 3000 <= gTime_stamp_for_this_munging || BrVector3Length(&data->pursuee->v) >= 0.3f) {
975
            if (data->time_last_away_from_pursuee + 7000 >= gTime_stamp_for_this_munging || data->time_last_twatted_em + 7000 >= gTime_stamp_for_this_munging || data->start_backup_time + 10000 >= gTime_stamp_for_this_munging) {
976
                if (pOpponent_spec->cheating) {
977
                    if (pOpponent_spec->player_to_oppo_d < 50.0f
978
                        && PointVisibleFromHere(&data->pursuee->car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
979
                        data->time_pursuee_last_visible = gTime_stamp_for_this_munging;
980
                    } else {
981
                        data->time_pursuee_last_visible = 0;
982
                    }
983
                } else if (pOpponent_spec->player_in_view_now || (data->time_of_next_visibility_check < gTime_stamp_for_this_munging && pOpponent_spec->player_to_oppo_d < 35.0f && PointVisibleFromHere(&data->pursuee->car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t))) {
984
                    data->time_pursuee_last_visible = gTime_stamp_for_this_munging;
985
                    data->time_of_next_visibility_check = gTime_stamp_for_this_munging + 600;
986
                }
987
                if (data->time_pursuee_last_visible + 3000 <= gTime_stamp_for_this_munging) {
988
                    if (data->pursuee->my_trail.number_of_nodes < 2) {
989
                        dr_dprintf("%s: Giving up pursuit - not visible & no trail yet", pOpponent_spec->car_spec->driver_name);
990
                        NewObjective(pOpponent_spec, eOOT_get_near_player);
991
                        return;
992
                    }
993
                    if (data->state != ePCS_following_trail) {
994
                        section_no = FindNearestTrailSection(pOpponent_spec, data->pursuee, &section_v, &intersect, &distance);
995
                        data->state = ePCS_following_trail;
996
                        if (distance > 20.0f || section_no == -1) {
997
                            dr_dprintf("%s: Giving up pursuit - not visible & trail ain't close enough (%f)", pOpponent_spec->car_spec->driver_name, distance);
998
                            NewObjective(pOpponent_spec, eOOT_get_near_player);
999
                            return;
1000
                        }
1001
                        dr_dprintf("%s: Commencing ePCS_following_trail state", pOpponent_spec->car_spec->driver_name);
1002
                        pOpponent_spec->follow_path_data.section_no = section_no;
1003
                        ProcessFollowPath(pOpponent_spec, ePOC_start, 1, 0, 0);
1004
                    }
1005
                } else if (data->state != ePCS_following_line_of_sight) {
1006
                    dr_dprintf("%s: Commencing ePCS_following_line_of_sight state", pOpponent_spec->car_spec->driver_name);
1007
                    data->state = ePCS_following_line_of_sight;
1008
                    sprintf(str, "%s: I've spotted you!", pOpponent_spec->car_spec->driver_name);
1009
                    ProcessFollowPath(pOpponent_spec, ePOC_start, 1, 1, 0);
1010
                }
1011
            } else {
1012
                dr_dprintf("%s: Backing up because we're too close to pursuee without having twatted him", pOpponent_spec->car_spec->driver_name);
1013
                data->start_backup_time = gTime_stamp_for_this_munging;
1014
                data->state = ePCS_backing_up;
1015
            }
1016
        } else {
1017
            dr_dprintf("%s: Backing up because we're 'stationary' after colliding with pursuee", pOpponent_spec->car_spec->driver_name);
1018
            data->start_backup_time = gTime_stamp_for_this_munging;
1019
            data->state = ePCS_backing_up;
1020
        }
1021
    }
1022
    switch (data->state) {
1023
    case ePCS_what_now:
1024
        PDEnterDebugger("ERROR: what_now state called in ProcessPursueAndTwat()");
1025
        break;
1026
    case ePCS_following_trail:
1027
        if (data->pursuee->my_trail.nodes_shifted_this_frame) {
1028
            if (pOpponent_spec->follow_path_data.section_no <= 15000) {
1029
                data->state = ePCS_following_trail;
1030
                section_no = FindNearestTrailSection(pOpponent_spec, data->pursuee, &section_v, &intersect, &distance);
1031
                dr_dprintf("%s: Trail got away; found new trail section %d", pOpponent_spec->car_spec->driver_name, section_no);
1032
                if (section_no == -1 || distance > 20.0f || !PointVisibleFromHere(&intersect, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
1033
                    dr_dprintf("%s: ...which unfortunately is too far away (%fBRU) or not visible - end of pursuit", pOpponent_spec->car_spec->driver_name, distance);
1034
                    NewObjective(pOpponent_spec, eOOT_get_near_player);
1035
                    return;
1036
                }
1037
                pOpponent_spec->follow_path_data.section_no = section_no;
1038
                ProcessFollowPath(pOpponent_spec, ePOC_start, 1, 0, 0);
1039
            } else {
1040
                pOpponent_spec->follow_path_data.section_no--;
1041
            }
1042
            dr_dprintf("%s: Following re-jobbied section %d/%d", pOpponent_spec->car_spec->driver_name, pOpponent_spec->follow_path_data.section_no, data->pursuee->my_trail.number_of_nodes - 1);
1043
        }
1044
        sprintf(str, "%s: Trail section %d/%d", pOpponent_spec->car_spec->driver_name, pOpponent_spec->follow_path_data.section_no, data->pursuee->my_trail.number_of_nodes - 1);
1045
        res = ProcessFollowPath(pOpponent_spec, ePOC_run, 1, 0, 0);
1046
        if (res == eFPR_given_up || res == eFPR_end_of_path) {
1047
            NewObjective(pOpponent_spec, eOOT_get_near_player);
1048
        }
1049
        break;
1050
    case ePCS_following_line_of_sight:
1051
        BrVector3Copy(&data->direct_line_nodes[0].p, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
1052
        BrVector3Sub(&wank, &data->pursuee->car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
1053
        s = BrVector3Length(&wank);
1054
        BrVector3Sub(&wank, &data->pursuee->v, &pOpponent_spec->car_spec->v);
1055
        t = BrVector3Length(&wank);
1056
        if (t >= 1.0f) {
1057
            d = s / t / 2.0;
1058
 
1059
        } else {
1060
            d = 0.0;
1061
        }
1062
        BrVector3Scale(&data->direct_line_nodes[1].p, &data->pursuee->v, d);
1063
        BrVector3Accumulate(&data->direct_line_nodes[1].p, &data->pursuee->car_master_actor->t.t.translate.t);
1064
        if (s >= 2.0f) {
1065
            ProcessFollowPath(pOpponent_spec, ePOC_run, 1, 1, 0);
1066
        } else {
1067
            ProcessFollowPath(pOpponent_spec, ePOC_run, 1, 1, 1);
1068
        }
1069
        break;
1070
    case ePCS_backing_up:
1071
        if (data->start_backup_time + 2200 >= gTime_stamp_for_this_munging) {
1072
            pOpponent_spec->car_spec->curvature = 0.0f;
1073
            pOpponent_spec->car_spec->brake_force = 0.0f;
1074
            pOpponent_spec->car_spec->acc_force = pOpponent_spec->car_spec->M * -8.0f;
1075
        } else {
1076
            pOpponent_spec->car_spec->acc_force = 0.0;
1077
            pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
1078
            if (data->start_backup_time + 3000 < gTime_stamp_for_this_munging) {
1079
                pOpponent_spec->car_spec->brake_force = 0.0f;
1080
                data->state = ePCS_what_now;
1081
                dr_dprintf("%s: Finished backing up.", pOpponent_spec->car_spec->driver_name);
1082
            }
1083
        }
1084
        break;
1085
    default:
1086
        return;
1087
    }
1088
}
1089
 
1090
// IDA: void __usercall ProcessRunAway(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
1091
void ProcessRunAway(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
1092
    //int res; // Pierre-Marie Baty -- unused variable
1093
    tS16 section_no;
1094
    br_scalar distance;
1095
    br_vector3 intersect;
1096
    br_vector3 direction_v;
1097
    char str[256];
1098
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
1099
 
1100
    switch (pCommand) {
1101
 
1102
    case ePOC_run:
1103
        if (pOpponent_spec->run_away_data.time_to_stop >= gTime_stamp_for_this_munging) {
1104
            if (pOpponent_spec->follow_path_data.section_no > 20000) {
1105
                ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
1106
                pOpponent_spec->follow_path_data.section_no = 20000;
1107
            }
1108
            if (pOpponent_spec->nnext_sections < 10) {
1109
                TopUpRandomRoute(pOpponent_spec, 10 - pOpponent_spec->nnext_sections);
1110
            }
1111
            if (ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0) == eFPR_given_up) {
1112
                ClearOpponentsProjectedRoute(pOpponent_spec);
1113
                section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
1114
                if (BrVector3Dot(&pOpponent_spec->car_spec->direction, &direction_v) < 0.0f) {
1115
                    AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 0);
1116
                } else {
1117
                    AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
1118
                }
1119
                TopUpRandomRoute(pOpponent_spec, -1);
1120
                ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
1121
            }
1122
        } else {
1123
            ObjectiveComplete(pOpponent_spec);
1124
        }
1125
        break;
1126
 
1127
    case ePOC_start:
1128
        dr_dprintf("%s: ProcessRunAway() - new objective started", pOpponent_spec->car_spec->driver_name);
1129
        pOpponent_spec->run_away_data.time_to_stop = gTime_stamp_for_this_munging + 1000 * IRandomBetween(30, 90);
1130
        ClearOpponentsProjectedRoute(pOpponent_spec);
1131
        section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
1132
        if (BrVector3Dot(&pOpponent_spec->car_spec->direction, &direction_v) < 0.0f) {
1133
            AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 0);
1134
        } else {
1135
            AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
1136
        }
1137
        TopUpRandomRoute(pOpponent_spec, -1);
1138
        ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
1139
        sprintf(str, "%s: Shit! I'm out of here...", pOpponent_spec->car_spec->driver_name);
1140
        break;
1141
 
1142
    case ePOC_die:
1143
        break;
1144
    }
1145
}
1146
 
1147
// IDA: void __usercall ProcessWaitForSomeHaplessSod(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
1148
void ProcessWaitForSomeHaplessSod(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
1149
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
1150
 
1151
    switch (pCommand) {
1152
    case ePOC_start:
1153
    case ePOC_run:
1154
        pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
1155
        break;
1156
    default:
1157
        break;
1158
    }
1159
}
1160
 
1161
// IDA: void __usercall ProcessReturnToStart(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
1162
void ProcessReturnToStart(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
1163
    br_vector3 section_v;
1164
    br_vector3 our_pos_xz;
1165
    br_vector3 cop_to_start;
1166
    br_scalar distance;
1167
    int res;
1168
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
1169
 
1170
    switch (pCommand) {
1171
    case ePOC_run:
1172
        if (TeleportCopToStart(pOpponent_spec)) {
1173
            break;
1174
        }
1175
        if (pOpponent_spec->return_to_start_data.waiting_near_start) {
1176
            pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
1177
        } else {
1178
            our_pos_xz = pOpponent_spec->car_spec->car_master_actor->t.t.translate.t;
1179
            our_pos_xz.v[1] = 0.0f;
1180
            BrVector3Sub(&cop_to_start, &pOpponent_spec->start_pos, &our_pos_xz);
1181
            if (BrVector3Length(&cop_to_start) >= 10.0) {
1182
                if (pOpponent_spec->follow_path_data.section_no > 20000) {
1183
                    ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
1184
                    pOpponent_spec->follow_path_data.section_no = 20000;
1185
                }
1186
                if (pOpponent_spec->nnext_sections <= 4) {
1187
                    CalcReturnToStartPointRoute(pOpponent_spec);
1188
                }
1189
                res = ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0);
1190
                if (res == eFPR_given_up || res == eFPR_end_of_path) {
1191
                    if (res == eFPR_given_up) {
1192
                        dr_dprintf("%s: Restarting return_to_start route because ProcessFollowPath() gave up.", pOpponent_spec->car_spec->driver_name);
1193
                    } else {
1194
                        dr_dprintf("%s: Restarting return_to_start route because ran out of path!", pOpponent_spec->car_spec->driver_name);
1195
                    }
1196
                    ClearOpponentsProjectedRoute(pOpponent_spec);
1197
                    CalcReturnToStartPointRoute(pOpponent_spec);
1198
                    ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
1199
                }
1200
            } else {
1201
                pOpponent_spec->return_to_start_data.waiting_near_start = 1;
1202
                pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
1203
            }
1204
        }
1205
        break;
1206
    case ePOC_start:
1207
        dr_dprintf("%s: ProcessReturnToStart() - new objective started", pOpponent_spec->car_spec->driver_name);
1208
        pOpponent_spec->return_to_start_data.waiting_near_start = 0;
1209
        pOpponent_spec->return_to_start_data.section_no = FindNearestPathSection(&pOpponent_spec->start_pos, &section_v, &pOpponent_spec->return_to_start_data.nearest_path_point, &distance);
1210
        pOpponent_spec->return_to_start_data.nearest_path_point.v[1] = 0.0;
1211
        CalcReturnToStartPointRoute(pOpponent_spec);
1212
        ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
1213
        break;
1214
    default:
1215
        break;
1216
    }
1217
}
1218
 
1219
// IDA: void __usercall ProcessLevitate(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
1220
void ProcessLevitate(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
1221
    float t;
1222
    //float terminal_time; // Pierre-Marie Baty -- unused variable
1223
    float y;
1224
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
1225
 
1226
    if (pCommand == ePOC_start) {
1227
        dr_dprintf("%s: ProcessLevitate() - new objective started", pOpponent_spec->car_spec->driver_name);
1228
        pOpponent_spec->levitate_data.waiting_to_levitate = 1;
1229
        pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
1230
        pOpponent_spec->car_spec->acc_force = 0.f;
1231
        pOpponent_spec->levitate_data.time_started = gTime_stamp_for_this_munging;
1232
    } else if (pCommand == ePOC_run) {
1233
        if (pOpponent_spec->levitate_data.waiting_to_levitate) {
1234
            if ((BrVector3Length(&pOpponent_spec->car_spec->v) < .01f && BrVector3Length(&pOpponent_spec->car_spec->omega) < 1.f) || gTime_stamp_for_this_munging - pOpponent_spec->levitate_data.time_started > 4000) {
1235
                pOpponent_spec->levitate_data.waiting_to_levitate = 0;
1236
                pOpponent_spec->levitate_data.time_started = gTime_stamp_for_this_munging;
1237
                pOpponent_spec->levitate_data.initial_y = pOpponent_spec->car_spec->car_master_actor->t.t.translate.t.v[1];
1238
                if (pOpponent_spec->car_spec->has_been_stolen) {
1239
                    NewTextHeadupSlot(4, 250, 2500, -4, GetMiscString(kMiscString_CarAddedToChangeCarList));
1240
                }
1241
            } else {
1242
                pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
1243
                pOpponent_spec->car_spec->acc_force = 0.f;
1244
                BrVector3InvScale(&pOpponent_spec->car_spec->omega, &pOpponent_spec->car_spec->omega,
1245
                    powf(gFrame_period_for_this_munging / 1000.f, 2.f));
1246
            }
1247
        }
1248
        if (!pOpponent_spec->levitate_data.waiting_to_levitate) {
1249
            TurnOpponentPhysicsOff(pOpponent_spec);
1250
            t = (gTime_stamp_for_this_munging - pOpponent_spec->levitate_data.time_started) / 1000.f;
1251
            if (t < 20.f) {
1252
                y = .5f * t * t / 2.f;
1253
            } else {
1254
                y = 10.f * (t - 20.f) + 100.f;
1255
            }
1256
            pOpponent_spec->car_spec->car_master_actor->t.t.translate.t.v[1] = pOpponent_spec->levitate_data.initial_y + y;
1257
            if (y > 200.f) {
1258
                pOpponent_spec->finished_for_this_race = 1;
1259
            }
1260
        }
1261
    }
1262
}
1263
 
1264
// IDA: void __usercall ProcessGetNearPlayer(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
1265
void ProcessGetNearPlayer(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
1266
    //br_vector3* initial_pos; // Pierre-Marie Baty -- unused variable
1267
    //br_actor* car_actor; // Pierre-Marie Baty -- unused variable
1268
    int res;
1269
    char str[256];
1270
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
1271
 
1272
    if (pCommand == ePOC_start) {
1273
        dr_dprintf("%s: ProcessGetNearPlayer() - new objective started", pOpponent_spec->car_spec->driver_name);
1274
        ClearOpponentsProjectedRoute(pOpponent_spec);
1275
        CalcGetNearPlayerRoute(pOpponent_spec, &gProgram_state.current_car);
1276
        ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
1277
        return;
1278
    }
1279
    if (pCommand == ePOC_run) {
1280
        if ((pOpponent_spec->car_spec->car_ID & 0xff00) == 768 && pOpponent_spec->distance_from_home > 75.0) {
1281
            dr_dprintf("%s: Completing get_near objective because I'm out of my precinct", pOpponent_spec->car_spec->driver_name);
1282
            NewObjective(pOpponent_spec, eOOT_return_to_start);
1283
            return;
1284
        }
1285
        if (pOpponent_spec->follow_path_data.section_no > 20000) {
1286
            if (pOpponent_spec->player_to_oppo_d < 10.0 || pOpponent_spec->follow_path_data.section_no == pOpponent_spec->players_section_when_last_calced_full_path) {
1287
                dr_dprintf("%s: ProcessGetNearPlayer() - giving up 'cos got to player's section", pOpponent_spec->car_spec->driver_name);
1288
                ObjectiveComplete(pOpponent_spec);
1289
                return;
1290
            }
1291
            ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
1292
            pOpponent_spec->follow_path_data.section_no = 20000;
1293
        }
1294
        if (pOpponent_spec->nnext_sections <= 4) {
1295
            CalcGetNearPlayerRoute(pOpponent_spec, &gProgram_state.current_car);
1296
        }
1297
        res = ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0);
1298
        sprintf(str, "Get near: %d", GetOpponentsRealSection(pOpponent_spec, pOpponent_spec->follow_path_data.section_no));
1299
 
1300
        if (res == eFPR_given_up) {
1301
            NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1302
        } else if (res == eFPR_end_of_path) {
1303
            dr_dprintf("%s: Restarting get_near_player route because ran out of path!", pOpponent_spec->car_spec->driver_name);
1304
            ClearOpponentsProjectedRoute(pOpponent_spec);
1305
            CalcGetNearPlayerRoute(pOpponent_spec, &gProgram_state.current_car);
1306
            ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
1307
        }
1308
    }
1309
}
1310
 
1311
// IDA: void __usercall ProcessFrozen(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
1312
void ProcessFrozen(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
1313
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
1314
 
1315
    switch (pCommand) {
1316
    case ePOC_start:
1317
        dr_dprintf("%d ProcessFrozen() - new task started", pOpponent_spec->index);
1318
        dr_dprintf("%s: Rematerialising from ePOC_start in ProcessFrozen()...", pOpponent_spec->car_spec->driver_name);
1319
        RematerialiseOpponentOnNearestSection(pOpponent_spec, 0.f);
1320
        pOpponent_spec->car_spec->acc_force = 0.f;
1321
        pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
1322
        break;
1323
    case ePOC_run:
1324
        pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
1325
        break;
1326
    case ePOC_die:
1327
        pOpponent_spec->car_spec->brake_force = 0.f;
1328
        break;
1329
    }
1330
}
1331
 
1332
// IDA: int __usercall HeadOnWithPlayerPossible@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
1333
int HeadOnWithPlayerPossible(tOpponent_spec* pOpponent_spec) {
1334
    br_vector3 oppo_to_player_norm;
1335
    LOG_TRACE("(%p)", pOpponent_spec);
1336
 
1337
    oppo_to_player_norm.v[0] = gProgram_state.current_car.car_master_actor->t.t.mat.m[3][0]
1338
        - pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][0];
1339
    oppo_to_player_norm.v[1] = gProgram_state.current_car.car_master_actor->t.t.mat.m[3][1]
1340
        - pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][1];
1341
    oppo_to_player_norm.v[2] = gProgram_state.current_car.car_master_actor->t.t.mat.m[3][2]
1342
        - pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][2];
1343
 
1344
    BrVector3Normalise(&oppo_to_player_norm, &oppo_to_player_norm);
1345
    if (gHead_on_cos_value >= BrVector3Dot(&pOpponent_spec->car_spec->direction, &pOpponent_spec->car_spec->direction)
1346
        || -gHead_on_cos_value <= BrVector3Dot(&pOpponent_spec->car_spec->direction, &pOpponent_spec->car_spec->direction)) {
1347
        return 0;
1348
    }
1349
    dr_dprintf("HOORAY! Head-on imminent");
1350
    return 1;
1351
}
1352
 
1353
// IDA: int __usercall AlreadyPursuingCar@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tCar_spec *pPursuee@<EDX>)
1354
int AlreadyPursuingCar(tOpponent_spec* pOpponent_spec, tCar_spec* pPursuee) {
1355
    LOG_TRACE("(%p, %p)", pOpponent_spec, pPursuee);
1356
 
1357
    return pOpponent_spec->current_objective == eOOT_pursue_and_twat && pOpponent_spec->pursue_car_data.pursuee == pPursuee;
1358
}
1359
 
1360
// IDA: int __usercall LastTwatteeAPlayer@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
1361
int LastTwatteeAPlayer(tOpponent_spec* pOpponent_spec) {
1362
    LOG_TRACE("(%p)", pOpponent_spec);
1363
 
1364
    return pOpponent_spec->car_spec->last_person_we_hit && pOpponent_spec->car_spec->last_person_we_hit->driver == eDriver_local_human;
1365
}
1366
 
1367
// IDA: int __usercall LastTwatterAPlayer@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
1368
int LastTwatterAPlayer(tOpponent_spec* pOpponent_spec) {
1369
    LOG_TRACE("(%p)", pOpponent_spec);
1370
 
1371
    return pOpponent_spec->car_spec->last_person_to_hit_us && pOpponent_spec->car_spec->last_person_to_hit_us->driver == eDriver_local_human;
1372
}
1373
 
1374
// IDA: void __usercall ObjectiveComplete(tOpponent_spec *pOpponent_spec@<EAX>)
1375
void ObjectiveComplete(tOpponent_spec* pOpponent_spec) {
1376
    LOG_TRACE("(%p)", pOpponent_spec);
1377
 
1378
    dr_dprintf("%s: Objective Completed", pOpponent_spec->car_spec->driver_name);
1379
    pOpponent_spec->new_objective_required = 1;
1380
    switch (pOpponent_spec->current_objective) {
1381
    case eOOT_complete_race:
1382
        gNum_of_opponents_completing_race--;
1383
        break;
1384
    case eOOT_pursue_and_twat:
1385
        gNum_of_opponents_pursuing--;
1386
        break;
1387
    case eOOT_get_near_player:
1388
        gNum_of_opponents_getting_near--;
1389
        break;
1390
    default:
1391
        break;
1392
    }
1393
}
1394
 
1395
// IDA: void __usercall TeleportOpponentToNearestSafeLocation(tOpponent_spec *pOpponent_spec@<EAX>)
1396
void TeleportOpponentToNearestSafeLocation(tOpponent_spec* pOpponent_spec) {
1397
    tS16 section_no;
1398
    tU8 section_direction;
1399
    br_scalar distance;
1400
    br_vector3 direction_v;
1401
    br_vector3 intersect;
1402
    int section_counter;
1403
    int found_safe_place;
1404
    LOG_TRACE("(%p)", pOpponent_spec);
1405
 
1406
    found_safe_place = 0;
1407
    section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
1408
    if (section_no < 0) {
1409
        return;
1410
    }
1411
    pOpponent_spec->last_in_view = 0;
1412
    TurnOpponentPhysicsOff(pOpponent_spec);
1413
    section_direction = BrVector3Dot(&pOpponent_spec->car_spec->direction, &direction_v) < 0.0f;
1414
    ClearOpponentsProjectedRoute(pOpponent_spec);
1415
    AddToOpponentsProjectedRoute(pOpponent_spec, section_no, section_direction);
1416
    TopUpRandomRoute(pOpponent_spec, -1);
1417
    section_counter = 1;
1418
    while (!found_safe_place) {
1419
        BrVector3Copy(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[section_counter].section_no].node_indices[pOpponent_spec->next_sections[section_counter].direction]].p);
1420
        CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(pOpponent_spec);
1421
        if (pOpponent_spec->player_to_oppo_d > gIn_view_distance) {
1422
            found_safe_place = 1;
1423
        }
1424
        section_counter++;
1425
        if (pOpponent_spec->nnext_sections <= section_counter) {
1426
            ShiftOpponentsProjectedRoute(pOpponent_spec, section_counter - 1);
1427
            section_counter = 0;
1428
            TopUpRandomRoute(pOpponent_spec, -1);
1429
        }
1430
    }
1431
}
1432
 
1433
// IDA: void __usercall ChooseNewObjective(tOpponent_spec *pOpponent_spec@<EAX>, int pMust_choose_one@<EDX>)
1434
void ChooseNewObjective(tOpponent_spec* pOpponent_spec, int pMust_choose_one) {
1435
    char str[255];
1436
    //tS16 players_section; // Pierre-Marie Baty -- unused variable
1437
    //br_vector3 wank; // Pierre-Marie Baty -- unused variable
1438
    //br_vector3 player_to_oppo_v; // Pierre-Marie Baty -- unused variable
1439
    //br_vector3 section_v; // Pierre-Marie Baty -- unused variable
1440
    //br_vector3 intersect; // Pierre-Marie Baty -- unused variable
1441
    //br_scalar dot; // Pierre-Marie Baty -- unused variable
1442
    //br_scalar distance; // Pierre-Marie Baty -- unused variable
1443
    int do_it;
1444
    //int i; // Pierre-Marie Baty -- unused variable
1445
    //int j; // Pierre-Marie Baty -- unused variable
1446
    int pursuit_percentage;
1447
    int percentage;
1448
    int general_grudge_increase;
1449
    LOG_TRACE("(%p, %d)", pOpponent_spec, pMust_choose_one);
1450
 
1451
    // v3 = pMust_choose_one;
1452
    if (pOpponent_spec->current_objective == eOOT_knackered_and_freewheeling || pOpponent_spec->knackeredness_detected) {
1453
        return;
1454
    }
1455
    if (gTime_stamp_for_this_munging > pOpponent_spec->next_out_of_world_check) {
1456
        pOpponent_spec->next_out_of_world_check = gTime_stamp_for_this_munging + 500;
1457
        if (HasCarFallenOffWorld(pOpponent_spec->car_spec)) {
1458
            if (pOpponent_spec->car_spec->last_time_we_touched_a_player <= gTime_stamp_for_this_munging - 7000) {
1459
                TeleportOpponentToNearestSafeLocation(pOpponent_spec);
1460
                NewObjective(pOpponent_spec, eOOT_complete_race);
1461
            } else {
1462
                TurnOpponentPhysicsOff(pOpponent_spec);
1463
                pOpponent_spec->finished_for_this_race = 1;
1464
                KnackerThisCar(pOpponent_spec->car_spec);
1465
                pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][1] -= 1000.0f;
1466
            }
1467
            return;
1468
        }
1469
    }
1470
    if (pOpponent_spec->car_spec->knackered && !pOpponent_spec->knackeredness_detected) {
1471
        pOpponent_spec->knackeredness_detected = 1;
1472
        dr_dprintf("%s: Knackered - dealing with appropriately", pOpponent_spec->car_spec->driver_name);
1473
        if (pOpponent_spec->car_spec->has_been_stolen) {
1474
            NewObjective(pOpponent_spec, eOOT_levitate);
1475
        } else {
1476
            NewObjective(pOpponent_spec, eOOT_knackered_and_freewheeling);
1477
        }
1478
        return;
1479
    }
1480
    if (pOpponent_spec->current_objective == eOOT_frozen) {
1481
        if (CAR_SPEC_GET_SPEED_FACTOR(pOpponent_spec->car_spec) != 0.0f) {
1482
            dr_dprintf("%s: Time to unfreeze", pOpponent_spec->car_spec->driver_name);
1483
            if (pOpponent_spec->pursuing_player_before_freeze == 1) {
1484
                NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1485
            } else {
1486
                NewObjective(pOpponent_spec, eOOT_get_near_player);
1487
            }
1488
        }
1489
        return;
1490
    } else {
1491
        if (CAR_SPEC_GET_SPEED_FACTOR(pOpponent_spec->car_spec) == 0.0f) {
1492
            dr_dprintf("%s: Decided to freeze", pOpponent_spec->car_spec->driver_name);
1493
            if (pOpponent_spec->current_objective == eOOT_pursue_and_twat && pOpponent_spec->pursue_car_data.pursuee == &gProgram_state.current_car) {
1494
                pOpponent_spec->pursuing_player_before_freeze = 1;
1495
            } else {
1496
                pOpponent_spec->pursuing_player_before_freeze = 0;
1497
            }
1498
            NewObjective(pOpponent_spec, eOOT_frozen);
1499
            return;
1500
        }
1501
        if (!gFirst_frame) {
1502
            general_grudge_increase = (pOpponent_spec->nastiness * 40.0f + 10.0f);
1503
            if (pOpponent_spec->car_spec->scary_bang && pOpponent_spec->player_to_oppo_d < 10.0f) {
1504
                if (pOpponent_spec->current_objective == eOOT_pursue_and_twat) {
1505
                    percentage = 40;
1506
                } else {
1507
                    percentage = 0;
1508
                }
1509
                if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
1510
                    if (PercentageChance(20)) {
1511
                        dr_dprintf("%s: Decided to run away", pOpponent_spec->car_spec->driver_name);
1512
                        NewObjective(pOpponent_spec, eOOT_run_away);
1513
                        return;
1514
                    }
1515
                } else if (PercentageChance((percentage + 60) - pOpponent_spec->nastiness * 50.0)) {
1516
                    dr_dprintf("%s: Decided to run away", pOpponent_spec->car_spec->driver_name);
1517
                    NewObjective(pOpponent_spec, eOOT_run_away);
1518
                    return;
1519
                }
1520
            }
1521
            if (!gMellow_opponents && (pOpponent_spec->current_objective != eOOT_run_away || pOpponent_spec->time_this_objective_started + 15000 <= gTime_stamp_for_this_munging)) {
1522
                if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec) && pOpponent_spec->murder_reported && pOpponent_spec->player_to_oppo_d < 20.0f && !AlreadyPursuingCar(pOpponent_spec, &gProgram_state.current_car)) {
1523
                    gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
1524
                    sprintf(str, "%s: Furderous melon!", pOpponent_spec->car_spec->driver_name);
1525
                    dr_dprintf("%s: Decided to pursue after MURDER", pOpponent_spec->car_spec->driver_name);
1526
                    NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1527
                    return;
1528
                }
1529
                if (pOpponent_spec->car_spec->big_bang && LastTwatterAPlayer(pOpponent_spec) && !AlreadyPursuingCar(pOpponent_spec, pOpponent_spec->car_spec->last_person_to_hit_us)) {
1530
                    // v4 = gOpponents[pOpponent_spec->index].psyche.grudge_against_player;
1531
                    // if (v4 <= 20) {
1532
                    //     v4 = 20;
1533
                    // }
1534
                    // v5 = general_grudge_increase + v4;
1535
                    // if (v5 >= 100) {
1536
                    //     LOBYTE(v5) = 100;
1537
                    // }
1538
                    gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
1539
                    sprintf(str, "%s: Christ! What was that?", pOpponent_spec->car_spec->driver_name);
1540
                    dr_dprintf("%s: Decided to pursue after big bang; last person to twat us was %s", pOpponent_spec->car_spec->driver_name, pOpponent_spec->car_spec->last_person_to_hit_us->driver_name);
1541
                    NewObjective(pOpponent_spec, eOOT_pursue_and_twat, pOpponent_spec->car_spec->last_person_to_hit_us);
1542
                    return;
1543
                }
1544
                if (LastTwatteeAPlayer(pOpponent_spec) && !AlreadyPursuingCar(pOpponent_spec, pOpponent_spec->car_spec->last_person_we_hit)) {
1545
                    // v6 = gOpponents[pOpponent_spec->index].psyche.grudge_against_player;
1546
                    // if (v6 <= 20) {
1547
                    //     v6 = 20;
1548
                    // }
1549
                    // v7 = general_grudge_increase + v6;
1550
                    // if (v7 >= 100) {
1551
                    //     LOBYTE(v7) = 100;
1552
                    // }
1553
                    gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
1554
                    sprintf(str, "%s: Ha! Bet you weren't expecting that!", pOpponent_spec->car_spec->driver_name);
1555
                    dr_dprintf("%s: Decided to pursue %s after accidentally hitting them", pOpponent_spec->car_spec->driver_name, pOpponent_spec->car_spec->last_person_we_hit->driver_name);
1556
                    NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1557
                    return;
1558
                }
1559
                if (!AlreadyPursuingCar(pOpponent_spec, &gProgram_state.current_car)) {
1560
                    if (pOpponent_spec->car_spec->grudge_raised_recently && (!CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec) || pOpponent_spec->player_to_oppo_d <= 20.0) && LastTwatterAPlayer(pOpponent_spec) && gOpponents[pOpponent_spec->index].psyche.grudge_against_player > 20) {
1561
                        // v8 = gOpponents[pOpponent_spec->index].psyche.grudge_against_player;
1562
                        // if (v8 <= 20) {
1563
                        //     v8 = 20;
1564
                        // }
1565
                        // v9 = general_grudge_increase + v8;
1566
                        // if (v9 >= 100) {
1567
                        //     LOBYTE(v9) = 100;
1568
                        // }
1569
                        gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
1570
                        sprintf(str, "%s: Right! That's enough, %s!", pOpponent_spec->car_spec->driver_name, gProgram_state.current_car.driver_name);
1571
                        dr_dprintf("%s: Decided to pursue after grudginess raised; last person to twat us was %s", pOpponent_spec->car_spec->driver_name, pOpponent_spec->car_spec->last_person_to_hit_us->driver_name);
1572
                        NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1573
                        return;
1574
                    }
1575
 
1576
                    if ((pOpponent_spec->player_in_view_now) != 0 && (pOpponent_spec->acknowledged_piv) == 0) {
1577
                        pOpponent_spec->acknowledged_piv = 1;
1578
                        if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
1579
                            pursuit_percentage = (BrVector3Length(&gProgram_state.current_car.v) - gDefinite_no_cop_pursuit_speed) * gCop_pursuit_speed_percentage_multiplier;
1580
                        } else if (gProgram_state.skill_level + 3 > gNum_of_opponents_pursuing) {
1581
                            pursuit_percentage = gOpponents[pOpponent_spec->index].psyche.grudge_against_player - 20 + pOpponent_spec->nastiness * 30.f;
1582
                        } else {
1583
                            pursuit_percentage = 0;
1584
                        }
1585
 
1586
                        pursuit_percentage += 50 * HeadOnWithPlayerPossible(pOpponent_spec);
1587
                        do_it = PercentageChance(pursuit_percentage);
1588
                        dr_dprintf("%s: Spotted player; chance of pursuing %d%%: %s", pOpponent_spec->car_spec->driver_name, pursuit_percentage, do_it ? "YES, Decided to pursue" : "NO, Decided NOT to pursue");
1589
                        if (do_it) {
1590
                            gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
1591
                            sprintf(str, "%s: I've decided to kill you for the fun of it", pOpponent_spec->car_spec->driver_name);
1592
                            NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1593
                            return;
1594
                        }
1595
                    }
1596
                }
1597
            }
1598
        }
1599
        if (!pMust_choose_one) {
1600
            return;
1601
        }
1602
        dr_dprintf("%s: Choosing new objective because we have to...", pOpponent_spec->car_spec->driver_name);
1603
        if (pOpponent_spec->has_moved_at_some_point) {
1604
            if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
1605
                NewObjective(pOpponent_spec, eOOT_return_to_start);
1606
                return;
1607
            }
1608
            if (gNum_of_opponents_pursuing + gNum_of_opponents_getting_near >= 3 || pOpponent_spec->player_to_oppo_d <= 10.0) {
1609
                if (gNum_of_opponents_completing_race >= 2) {
1610
                    pursuit_percentage = pOpponent_spec->player_to_oppo_d - 15.0f;
1611
                    if (PercentageChance(pursuit_percentage)) {
1612
                        dr_dprintf("%s: Choosing to get_near because chance dictated it (%d%%)", pOpponent_spec->car_spec->driver_name, pursuit_percentage);
1613
                        NewObjective(pOpponent_spec, eOOT_get_near_player);
1614
                        return;
1615
                    } else {
1616
                        dr_dprintf("%s: Choosing to complete_race because chance dictated it (%d%%)", pOpponent_spec->car_spec->driver_name, pursuit_percentage);
1617
                    }
1618
                } else {
1619
                    dr_dprintf("%s: Choosing to complete_race because not enough oppos are yet (%d/%d)", pOpponent_spec->car_spec->driver_name, gNum_of_opponents_completing_race, 2);
1620
                }
1621
                NewObjective(pOpponent_spec, eOOT_complete_race);
1622
                return;
1623
            }
1624
            dr_dprintf("%s: Choosing to get_near because not enough oppos are yet (%d/%d)", pOpponent_spec->car_spec->driver_name, gNum_of_opponents_pursuing + gNum_of_opponents_getting_near, 3);
1625
            NewObjective(pOpponent_spec, eOOT_get_near_player);
1626
            return;
1627
        }
1628
        if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
1629
            NewObjective(pOpponent_spec, eOOT_wait_for_some_hapless_sod);
1630
            return;
1631
        }
1632
        if (!pOpponent_spec->pursue_from_start || gMellow_opponents) {
1633
            NewObjective(pOpponent_spec, eOOT_complete_race);
1634
            return;
1635
        }
1636
 
1637
        gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, pOpponent_spec->nastiness * 40.0 + (MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + 20));
1638
        NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
1639
    }
1640
}
1641
 
1642
// IDA: void __usercall ProcessThisOpponent(tOpponent_spec *pOpponent_spec@<EAX>)
1643
void ProcessThisOpponent(tOpponent_spec* pOpponent_spec) {
1644
    //int i; // Pierre-Marie Baty -- unused variable
1645
    LOG_TRACE("(%p)", pOpponent_spec);
1646
 
1647
    if ((gMap_mode && gShow_opponents) || pOpponent_spec->last_in_view + 3000 >= gTime_stamp_for_this_munging) {
1648
        if (pOpponent_spec->cheating) {
1649
            OiStopCheating(pOpponent_spec);
1650
        }
1651
    } else if (pOpponent_spec->cheating == 0) {
1652
        StartToCheat(pOpponent_spec);
1653
    }
1654
    ChooseNewObjective(pOpponent_spec, pOpponent_spec->new_objective_required);
1655
    pOpponent_spec->new_objective_required = 0;
1656
    if (gCountdown || gRace_finished) {
1657
        pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 10.f;
1658
    }
1659
    if (!pOpponent_spec->finished_for_this_race && !gStop_opponents_moving && !gRace_finished && pOpponent_spec->stun_time_ends < gTime_stamp_for_this_munging) {
1660
        ProcessCurrentObjective(pOpponent_spec, ePOC_run);
1661
    }
1662
    if (pOpponent_spec->cheating) {
1663
        BrVector3Copy(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
1664
    }
1665
}
1666
 
1667
// IDA: int __usercall IsNetCarActive@<EAX>(br_vector3 *pPoint@<EAX>)
1668
int IsNetCarActive(br_vector3* pPoint) {
1669
    br_vector3 tv;
1670
    LOG_TRACE("(%p)", pPoint);
1671
 
1672
    BrVector3Sub(&tv, &gProgram_state.current_car.car_master_actor->t.t.translate.t, pPoint);
1673
    if (BrVector3LengthSquared(&tv) < 100.f) {
1674
        return 1;
1675
    }
1676
    if (gCar_to_view != &gProgram_state.current_car) {
1677
        BrVector3Sub(&tv, &gCar_to_view->car_master_actor->t.t.translate.t, pPoint);
1678
        return BrVector3LengthSquared(&tv) < 100.f;
1679
    }
1680
    return 0;
1681
}
1682
 
1683
// IDA: void __cdecl RebuildActiveCarList()
1684
void RebuildActiveCarList(void) {
1685
    int i;
1686
    tCar_spec* car_spec;
1687
    LOG_TRACE("()");
1688
 
1689
    if (gActive_car_list_rebuild_required) {
1690
        gActive_car_list_rebuild_required = 0;
1691
        gNum_active_cars = 0;
1692
 
1693
        if (!gProgram_state.current_car.disabled || gAction_replay_mode) {
1694
            gActive_car_list[gNum_active_cars] = &gProgram_state.current_car;
1695
            gNum_active_cars++;
1696
            gProgram_state.current_car.active = 1;
1697
        }
1698
 
1699
        if (gNet_mode == eNet_mode_host) {
1700
            for (i = 0; i < GetCarCount(eVehicle_net_player); i++) {
1701
                car_spec = GetCarSpec(eVehicle_net_player, i);
1702
                if (car_spec->disabled) {
1703
                    car_spec->active = 0;
1704
                } else {
1705
                    gActive_car_list[gNum_active_cars] = car_spec;
1706
                    gNum_active_cars++;
1707
                    car_spec->active = 1;
1708
                }
1709
            }
1710
        } else if (gNet_mode == eNet_mode_client) {
1711
            for (i = 0; i < GetCarCount(eVehicle_net_player); i++) {
1712
                car_spec = GetCarSpec(eVehicle_net_player, i);
1713
                if (car_spec->disabled || !IsNetCarActive(&car_spec->car_master_actor->t.t.translate.t)) {
1714
                    car_spec->active = 0;
1715
                } else {
1716
                    gActive_car_list[gNum_active_cars] = car_spec;
1717
                    gNum_active_cars++;
1718
                    car_spec->active = 1;
1719
                }
1720
            }
1721
        }
1722
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
1723
            car_spec = GetCarSpec(eVehicle_opponent, i);
1724
            if (gProgram_state.AI_vehicles.opponents[i].physics_me || gAction_replay_mode) {
1725
                gActive_car_list[gNum_active_cars] = car_spec;
1726
                gNum_active_cars++;
1727
                car_spec->active = 1;
1728
            } else {
1729
                car_spec->active = 0;
1730
            }
1731
        }
1732
        for (i = 0; gNumber_of_cops_before_faffage > i; ++i) {
1733
            car_spec = GetCarSpec(eVehicle_rozzer, i);
1734
            if (gProgram_state.AI_vehicles.cops[i].physics_me || gAction_replay_mode) {
1735
                gActive_car_list[gNum_active_cars] = car_spec;
1736
                gNum_active_cars++;
1737
                car_spec->active = 1;
1738
            }
1739
        }
1740
    }
1741
}
1742
 
1743
// IDA: void __cdecl ForceRebuildActiveCarList()
1744
void ForceRebuildActiveCarList(void) {
1745
    LOG_TRACE("()");
1746
 
1747
    gActive_car_list_rebuild_required = 1;
1748
    if (gProgram_state.racing) {
1749
        RebuildActiveCarList();
1750
    }
1751
}
1752
 
1753
// IDA: void __usercall StartToCheat(tOpponent_spec *pOpponent_spec@<EAX>)
1754
void StartToCheat(tOpponent_spec* pOpponent_spec) {
1755
    LOG_TRACE("(%p)", pOpponent_spec);
1756
 
1757
    dr_dprintf("%s: StartToCheat() - Starting to cheat", pOpponent_spec->car_spec->driver_name);
1758
    pOpponent_spec->cheating = 1;
1759
    if ((pOpponent_spec->car_spec->car_ID & 0xff00) == 0x300) {
1760
        dr_dprintf("%s: StartToCheat() - Turning physics OFF", pOpponent_spec->car_spec->driver_name);
1761
        TurnOpponentPhysicsOff(pOpponent_spec);
1762
        RebuildActiveCarList();
1763
    }
1764
}
1765
 
1766
// IDA: void __usercall OiStopCheating(tOpponent_spec *pOpponent_spec@<EAX>)
1767
void OiStopCheating(tOpponent_spec* pOpponent_spec) {
1768
    LOG_TRACE("(%p)", pOpponent_spec);
1769
 
1770
    dr_dprintf("%s: OiStopCheating() - End of cheating sesh", pOpponent_spec->car_spec->driver_name);
1771
    pOpponent_spec->cheating = 0;
1772
    if ((pOpponent_spec->car_spec->car_ID & 0xff00) == 0x300) {
1773
        dr_dprintf("%s: OiStopCheating() - Turning physics ON", pOpponent_spec->car_spec->driver_name);
1774
        TurnOpponentPhysicsOn(pOpponent_spec);
1775
        RebuildActiveCarList();
1776
    }
1777
}
1778
 
1779
// IDA: int __usercall TeleportCopToStart@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
1780
int TeleportCopToStart(tOpponent_spec* pOpponent_spec) {
1781
    br_vector3 wank;
1782
    LOG_TRACE("(%p)", pOpponent_spec);
1783
 
1784
    if (!pOpponent_spec->cheating || !CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
1785
        return 0;
1786
    }
1787
    BrVector3Sub(&wank, &gProgram_state.current_car.car_master_actor->t.t.translate.t, &pOpponent_spec->start_pos);
1788
    if (BrVector3Length(&wank) <= gIn_view_distance) {
1789
        return 0;
1790
    }
1791
    pOpponent_spec->car_spec->car_master_actor->t.t.euler.t = pOpponent_spec->start_pos;
1792
    PointActorAlongThisBloodyVector(pOpponent_spec->car_spec->car_master_actor, &pOpponent_spec->start_direction);
1793
    pOpponent_spec->physics_me = 0;
1794
    RematerialiseOpponent(pOpponent_spec, 0.0);
1795
    TurnOpponentPhysicsOff(pOpponent_spec);
1796
    RebuildActiveCarList();
1797
    NewObjective(pOpponent_spec, eOOT_wait_for_some_hapless_sod);
1798
    return 1;
1799
}
1800
 
1801
// IDA: void __usercall CalcDistanceFromHome(tOpponent_spec *pOpponent_spec@<EAX>)
1802
void CalcDistanceFromHome(tOpponent_spec* pOpponent_spec) {
1803
    br_vector3 wank;
1804
    LOG_TRACE("(%p)", pOpponent_spec);
1805
 
1806
    BrVector3Sub(&wank, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &pOpponent_spec->start_pos);
1807
    pOpponent_spec->distance_from_home = BrVector3Length(&wank);
1808
}
1809
 
1810
// IDA: int __usercall MassageOpponentPosition@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pMassage_count@<EDX>)
1811
int MassageOpponentPosition(tOpponent_spec* pOpponent_spec, int pMassage_count) {
1812
    br_matrix34* mat;
1813
    br_vector3* car_trans;
1814
    br_vector3 displacement;
1815
    br_vector3 positive_y_vector;
1816
    br_vector3 direction_v;
1817
    LOG_TRACE("(%p, %d)", pOpponent_spec, pMassage_count);
1818
 
1819
    BrVector3Set(&positive_y_vector, 0.f, 1.f, 0.f);
1820
    mat = &pOpponent_spec->car_spec->car_master_actor->t.t.mat;
1821
    car_trans = &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t;
1822
    if (pMassage_count > 22) {
1823
        return 0;
1824
    } else if (pMassage_count > 20) {
1825
        car_trans->v[1] += (pMassage_count - 20) * 2.0f;
1826
        return 1;
1827
    } else {
1828
        direction_v.v[0] = -pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2][0];
1829
        direction_v.v[1] = -pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2][1];
1830
        direction_v.v[2] = -pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2][2];
1831
        if (pMassage_count % 4 >= 2) {
1832
            BrVector3Cross(&displacement, &positive_y_vector, &direction_v);
1833
            BrVector3Normalise(&displacement, &displacement);
1834
            BrVector3Scale(&displacement, &displacement, (pMassage_count / 4) * 0.1f);
1835
        } else {
1836
            BrVector3Normalise(&displacement, &direction_v);
1837
            BrVector3Scale(&displacement, &displacement, (pMassage_count / 4) * 0.5f);
1838
        }
1839
        if (pMassage_count % 2) {
1840
            BrVector3Negate(&displacement, &displacement);
1841
        }
1842
        BrVector3Accumulate(car_trans, &displacement);
1843
        return 1;
1844
    }
1845
}
1846
 
1847
// IDA: int __usercall RematerialiseOpponentOnThisSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, br_scalar pSpeed, tS16 pSection_no)
1848
int RematerialiseOpponentOnThisSection(tOpponent_spec* pOpponent_spec, br_scalar pSpeed, tS16 pSection_no) {
1849
    br_vector3* start;
1850
    br_vector3* finish;
1851
    br_vector3 a;
1852
    br_vector3 p;
1853
    br_vector3 section_v;
1854
    br_vector3 car_to_end;
1855
    //br_vector3 intersect; // Pierre-Marie Baty -- unused variable
1856
    br_scalar t;
1857
    //br_scalar distance_to_end; // Pierre-Marie Baty -- unused variable
1858
    //br_scalar length; // Pierre-Marie Baty -- unused variable
1859
    LOG_TRACE("(%p, %f, %d)", pOpponent_spec, pSpeed, pSection_no);
1860
 
1861
    if (pOpponent_spec->physics_me) {
1862
        dr_dprintf("%s: Actually, we're already materialised", pOpponent_spec->car_spec->driver_name);
1863
        return 1;
1864
    }
1865
    start = GetOpponentsSectionStartNodePoint(pOpponent_spec, pSection_no);
1866
    finish = GetOpponentsSectionFinishNodePoint(pOpponent_spec, pSection_no);
1867
    BrVector3Sub(&section_v, finish, start);
1868
    if (BrVector3Length(&section_v) != 0.f) {
1869
        BrVector3Sub(&a, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, start);
1870
        t = BrVector3Dot(&section_v, &a) / BrVector3Dot(&section_v, &section_v);
1871
        if (t < 0.f) {
1872
            BrVector3Copy(&p, start);
1873
        } else if (t > 1.f) {
1874
            BrVector3Copy(&p, finish);
1875
        } else {
1876
            p.v[0] = start->v[0] + t * section_v.v[0];
1877
            p.v[1] = start->v[1] + t * section_v.v[1];
1878
            p.v[2] = start->v[2] + t * section_v.v[2];
1879
        }
1880
        BrVector3Copy(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &p);
1881
        BrVector3Sub(&a, finish, start);
1882
        PointActorAlongThisBloodyVector(pOpponent_spec->car_spec->car_master_actor, &a);
1883
    }
1884
    if (!RematerialiseOpponent(pOpponent_spec, pSpeed)) {
1885
        return 0;
1886
    }
1887
    BrVector3Sub(&car_to_end, finish, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
1888
    pOpponent_spec->car_spec->brake_force = 0.f;
1889
    pOpponent_spec->car_spec->acc_force = 0.f;
1890
    if (BrVector3Length(&car_to_end) >= 5.f) {
1891
        pOpponent_spec->car_spec->acc_force = pOpponent_spec->car_spec->M / 2.f;
1892
    } else {
1893
        pOpponent_spec->car_spec->acc_force = 15.f * pOpponent_spec->car_spec->M;
1894
    }
1895
    pOpponent_spec->last_in_view = gTime_stamp_for_this_munging;
1896
    return 1;
1897
}
1898
 
1899
// IDA: int __usercall RematerialiseOpponentOnNearestSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, br_scalar pSpeed)
1900
int RematerialiseOpponentOnNearestSection(tOpponent_spec* pOpponent_spec, br_scalar pSpeed) {
1901
    br_vector3 intersect;
1902
    br_vector3 direction_v;
1903
    br_vector3 car_to_end;
1904
    //br_vector3* start; // Pierre-Marie Baty -- unused variable
1905
    br_vector3* finish;
1906
    br_scalar distance;
1907
    br_scalar distance_to_end;
1908
    tS16 section_no;
1909
    LOG_TRACE("(%p, %f)", pOpponent_spec, pSpeed);
1910
 
1911
    if (pOpponent_spec->physics_me) {
1912
        dr_dprintf("%s: Actually, we're already materialised", pOpponent_spec->car_spec->driver_name);
1913
        return 1;
1914
    }
1915
    section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
1916
    finish = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p;
1917
    BrVector3Copy(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &intersect);
1918
    PointActorAlongThisBloodyVector(pOpponent_spec->car_spec->car_master_actor, &direction_v);
1919
    BrVector3Sub(&car_to_end, finish, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
1920
    if (RematerialiseOpponent(pOpponent_spec, pSpeed)) {
1921
        pOpponent_spec->car_spec->brake_force = 0.0f;
1922
        pOpponent_spec->car_spec->acc_force = 0.0f;
1923
 
1924
        distance_to_end = BrVector3Length(&car_to_end);
1925
        if (distance_to_end >= 5.0f) {
1926
            pOpponent_spec->car_spec->acc_force = pOpponent_spec->car_spec->M / 2.0f;
1927
        } else {
1928
            pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
1929
        }
1930
    }
1931
    return 0;
1932
}
1933
 
1934
// IDA: int __usercall RematerialiseOpponent@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, br_scalar pSpeed)
1935
int RematerialiseOpponent(tOpponent_spec* pOpponent_spec, br_scalar pSpeed) {
1936
    static int count;
1937
    static int total;
1938
    static int highest;
1939
    int this_total;
1940
    br_matrix34* mat;
1941
    br_matrix34 original_mat;
1942
    br_vector3 a;
1943
    br_vector3 b;
1944
    br_vector3 norm;
1945
    br_vector3 norm2;
1946
    br_scalar dist;
1947
    br_scalar dist2;
1948
    //br_scalar ts; // Pierre-Marie Baty -- unused variable
1949
    br_angle phi;
1950
    int i;
1951
    //int j; // Pierre-Marie Baty -- unused variable
1952
    int massage_count;
1953
    //br_angle theta; // Pierre-Marie Baty -- unused variable
1954
    int sensible_place;
1955
    LOG_TRACE("(%p, %f)", pOpponent_spec, pSpeed);
1956
 
1957
    this_total = 0;
1958
    mat = &pOpponent_spec->car_spec->car_master_actor->t.t.mat;
1959
    massage_count = 0;
1960
    phi = BrDegreeToAngle(90) - BrRadianToAngle(atan2f(mat->m[2][2], mat->m[2][0]));
1961
    if (pOpponent_spec->physics_me) {
1962
        dr_dprintf("%s: Actually, we're already materialised", pOpponent_spec->car_spec->driver_name);
1963
    } else {
1964
        total++;
1965
        BrMatrix34Copy(&original_mat, mat);
1966
        TurnOpponentPhysicsOn(pOpponent_spec);
1967
        RebuildActiveCarList();
1968
        while (1) {
1969
            count++;
1970
            BrVector3Scale((br_vector3*)mat->m[3], (br_vector3*)mat->m[3], WORLD_SCALE);
1971
            BrVector3Copy(&b, (br_vector3*)mat->m[3]);
1972
            BrMatrix34RotateY(mat, phi);
1973
            BrVector3Copy((br_vector3*)mat->m[3], &b);
1974
            BrVector3SetFloat(&b, 0.f, -100.f, 0.f);
1975
            BrVector3Copy(&a, (br_vector3*)mat->m[3]);
1976
            a.v[1] += 1.f;
1977
            findfloor(&a, &b, &norm, &dist);
1978
            a.v[1] += 100.f;
1979
            findfloor(&a, &b, &norm2, &dist2);
1980
            if (dist2 <= 1.f) {
1981
                BrVector3SetFloat(&b, 0.f, -5.01f, 0.f);
1982
                a.v[1] -= 100.f;
1983
                for (i = 0; i < 20; i++) {
1984
                    a.v[1] += 5.f;
1985
                    findfloor(&a, &b, &norm2, &dist2);
1986
                    if (dist2 <= 1.f) {
1987
                        break;
1988
                    }
1989
                }
1990
                dist2 = (i + 1) * 0.05f - dist2 / 20.f;
1991
            }
1992
            if (dist2 < dist) {
1993
                dist = -dist2;
1994
                BrVector3Copy(&norm, &norm2);
1995
            }
1996
            if (fabsf(dist) <= 1.f) {
1997
                mat->m[3][1] -= dist * 100.f - 2.f;
1998
                BrMatrix34PreRotateX(mat, BrRadianToAngle(asinf(BrVector3Dot((br_vector3*)mat->m[2], &norm))));
1999
                BrMatrix34PreRotateZ(mat, BrRadianToAngle(asinf(BrVector3Dot((br_vector3*)mat->m[2], &norm))));
2000
            }
2001
            BrVector3Negate(&pOpponent_spec->car_spec->direction, (br_vector3*)mat->m[2]);
2002
            BrMatrix34ApplyP(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->cmpos, mat);
2003
            BrVector3InvScale(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->pos, WORLD_SCALE);
2004
            BrVector3InvScale((br_vector3*)mat->m[3], (br_vector3*)mat->m[3], WORLD_SCALE);
2005
            BrVector3Copy(&pOpponent_spec->car_spec->v, (br_vector3*)pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2]);
2006
            BrVector3Negate(&pOpponent_spec->car_spec->v, &pOpponent_spec->car_spec->v);
2007
            BrVector3Normalise(&pOpponent_spec->car_spec->v, &pOpponent_spec->car_spec->v);
2008
            BrVector3Scale(&pOpponent_spec->car_spec->v, &pOpponent_spec->car_spec->v, pSpeed * WORLD_SCALE);
2009
            BrVector3Set(&pOpponent_spec->car_spec->omega, 0.f, 0.f, 0.f);
2010
            BrMatrix34Copy(&pOpponent_spec->car_spec->oldmat, mat);
2011
            BrMatrix34Copy(&pOpponent_spec->car_spec->old_frame_mat, mat);
2012
            BrVector3Scale((br_vector3*)pOpponent_spec->car_spec->oldmat.m[3], (br_vector3*)pOpponent_spec->car_spec->oldmat.m[3], WORLD_SCALE);
2013
            for (i = 0; i < COUNT_OF(pOpponent_spec->car_spec->oldd); i++) {
2014
                pOpponent_spec->car_spec->oldd[i] = pOpponent_spec->car_spec->ride_height;
2015
            }
2016
            pOpponent_spec->car_spec->gear = 0;
2017
            pOpponent_spec->car_spec->revs = 0.f;
2018
            pOpponent_spec->car_spec->traction_control = 1;
2019
            BrMatrix34ApplyP(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->cmpos, mat);
2020
            BrVector3InvScale(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->pos, WORLD_SCALE);
2021
            BrVector3Negate(&pOpponent_spec->car_spec->direction, (br_vector3*)pOpponent_spec->car_spec->oldmat.m[3]);
2022
            pOpponent_spec->car_spec->box_face_ref = gFace_num__car - 2;
2023
            pOpponent_spec->car_spec->doing_nothing_flag = 0;
2024
            sensible_place = TestForCarInSensiblePlace(pOpponent_spec->car_spec);
2025
            if (sensible_place) {
2026
                break;
2027
            } else {
2028
                BrMatrix34Copy(mat, &original_mat);
2029
            }
2030
            if (!MassageOpponentPosition(pOpponent_spec, massage_count++)) {
2031
                break;
2032
            }
2033
            this_total++;
2034
        }
2035
        count--;
2036
        if (sensible_place) {
2037
            dr_dprintf("%s: Rematerialised (took %d attempts, orig. pos. (%7.3f,%7.3f,%7.3f), actual pos. (%7.3f,%7.3f,%7.3f))",
2038
                pOpponent_spec->car_spec->driver_name,
2039
                this_total + 1,
2040
                original_mat.m[3][0], original_mat.m[3][1], original_mat.m[3][2],
2041
                mat->m[3][0], mat->m[3][1], mat->m[3][2]);
2042
        }
2043
        if (this_total > highest) {
2044
            highest = this_total;
2045
        }
2046
        if (count != 0) {
2047
            dr_dprintf("MassageOpponentPosition() called an avg of %.1f times (max %d) per ReMaterialisation",
2048
                count / total, highest);
2049
        }
2050
        if (sensible_place) {
2051
            ResetCarSpecialVolume((tCollision_info*)pOpponent_spec->car_spec);
2052
        } else {
2053
            TurnOpponentPhysicsOff(pOpponent_spec);
2054
            RebuildActiveCarList();
2055
            TeleportOpponentToNearestSafeLocation(pOpponent_spec);
2056
        }
2057
    }
2058
    return 1;
2059
}
2060
 
2061
// IDA: void __usercall CalcPlayerConspicuousness(tOpponent_spec *pOpponent_spec@<EAX>)
2062
void CalcPlayerConspicuousness(tOpponent_spec* pOpponent_spec) {
2063
    br_vector3 pos_in_cop_space;
2064
    br_matrix34 inverse_transform;
2065
    LOG_TRACE("(%p)", pOpponent_spec);
2066
 
2067
    if (pOpponent_spec->next_player_visibility_check >= gTime_stamp_for_this_munging) {
2068
        return;
2069
    }
2070
    pOpponent_spec->player_in_view_now = 0;
2071
    if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
2072
        pOpponent_spec->next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 100;
2073
        if (pOpponent_spec->player_to_oppo_d < 20.f) {
2074
            BrMatrix34LPInverse(&inverse_transform, &pOpponent_spec->car_spec->car_master_actor->t.t.mat);
2075
            BrMatrix34ApplyP(&pos_in_cop_space, &gProgram_state.current_car.car_master_actor->t.t.translate.t, &inverse_transform);
2076
            if (pos_in_cop_space.v[2] < 0.f && PointVisibleFromHere(&gProgram_state.current_car.car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
2077
                pOpponent_spec->player_in_view_now = 1;
2078
                pOpponent_spec->acknowledged_piv = 0;
2079
            }
2080
        }
2081
    } else {
2082
        pOpponent_spec->next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 6000;
2083
        dr_dprintf("%s: Time now: %9.2f; next vis check at %9.2f", pOpponent_spec->car_spec->driver_name, gTime_stamp_for_this_munging / 1000.f, pOpponent_spec->next_player_visibility_check / 1000.0f);
2084
        if (pOpponent_spec->player_to_oppo_d < 50.f && PointVisibleFromHere(&gProgram_state.current_car.car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
2085
            pOpponent_spec->player_in_view_now = 1;
2086
            pOpponent_spec->acknowledged_piv = 0;
2087
        }
2088
    }
2089
}
2090
 
2091
// IDA: void __usercall CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(tOpponent_spec *pOpponent_spec@<EAX>)
2092
void CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(tOpponent_spec* pOpponent_spec) {
2093
    LOG_TRACE("(%p)", pOpponent_spec);
2094
 
2095
    BrVector3Sub(&pOpponent_spec->player_to_oppo_v, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &gProgram_state.current_car.car_master_actor->t.t.translate.t);
2096
    pOpponent_spec->player_to_oppo_d = BrVector3Length(&pOpponent_spec->player_to_oppo_v);
2097
    if (pOpponent_spec->player_to_oppo_d < gIn_view_distance) {
2098
        pOpponent_spec->last_in_view = gTime_stamp_for_this_munging;
2099
    }
2100
}
2101
 
2102
// IDA: void __usercall ChallengeOccurred(int pChallenger_index@<EAX>, int pAccepted@<EDX>)
2103
void ChallengeOccurred(int pChallenger_index, int pAccepted) {
2104
    LOG_TRACE("(%d, %d)", pChallenger_index, pAccepted);
2105
 
2106
    if (pAccepted) {
2107
        gChallenger_index__opponent = pChallenger_index;
2108
    }
2109
}
2110
 
2111
// IDA: void __cdecl LoadCopCars()
2112
void LoadCopCars(void) {
2113
    int i;
2114
    LOG_TRACE("()");
2115
 
2116
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2117
        PossibleService();
2118
        gProgram_state.AI_vehicles.cops[i].car_spec = BrMemAllocate(sizeof(tCar_spec), kMem_cop_car_spec);
2119
        LoadCar(
2120
            gBIG_APC_index == i ? "BIGAPC.TXT" : "APC.TXT",
2121
            eDriver_oppo,
2122
            gProgram_state.AI_vehicles.cops[i].car_spec,
2123
            (gBIG_APC_index == i) ? 4 : 3,
2124
            "The Cops",
2125
            &gTheir_cars_storage_space);
2126
    }
2127
}
2128
 
2129
// IDA: void __usercall LoadInOppoPaths(FILE *pF@<EAX>)
2130
void LoadInOppoPaths(FILE* pF) {
2131
    char s[256];
2132
    char* res;
2133
    int data_errors;
2134
    int section_no;
2135
    //int node_no; // Pierre-Marie Baty -- unused variable
2136
    int i;
2137
    br_scalar x;
2138
    br_scalar y;
2139
    br_scalar z;
2140
    br_scalar scalars[8];
2141
    br_scalar distance;
2142
    tPath_node* node_ptr;
2143
    br_vector3 section_v;
2144
    //br_vector3 positive_y_vector; // Pierre-Marie Baty -- unused variable
2145
    br_vector3 intersect;
2146
    br_vector3 cop_to_section;
2147
    int j;
2148
    int sections_to_delete;
2149
    int delete_these[1024];
2150
 
2151
    //float x_0; // Pierre-Marie Baty -- unused variable
2152
    //float x_1; // Pierre-Marie Baty -- unused variable
2153
    //float x_2; // Pierre-Marie Baty -- unused variable
2154
 
2155
    LOG_TRACE("(%p)", pF);
2156
 
2157
    data_errors = 0;
2158
    sections_to_delete = 0;
2159
    dr_dprintf("Start of LoadInOppoPaths()...");
2160
    gProgram_state.AI_vehicles.number_of_path_nodes = 0;
2161
    gProgram_state.AI_vehicles.number_of_path_sections = 0;
2162
    gProgram_state.AI_vehicles.path_nodes = 0;
2163
    gProgram_state.AI_vehicles.path_sections = 0;
2164
    gBit_per_node = 0;
2165
    gBIG_APC_index = -1;
2166
    do {
2167
        res = GetALineAndDontArgue(pF, s);
2168
    } while (res && strcmp("START OF OPPONENT PATHS", s) != 0);
2169
    if (res) {
2170
        ReallocExtraPathNodes(GetAnInt(pF));
2171
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
2172
            GetThreeFloats(pF, &gProgram_state.AI_vehicles.path_nodes[i].p.v[0], &gProgram_state.AI_vehicles.path_nodes[i].p.v[1], &gProgram_state.AI_vehicles.path_nodes[i].p.v[2]);
2173
            gProgram_state.AI_vehicles.path_nodes[i].number_of_sections = 0;
2174
        }
2175
        ReallocExtraPathSections(GetAnInt(pF));
2176
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
2177
            PossibleService();
2178
            GetNScalars(pF, 8, scalars);
2179
            gProgram_state.AI_vehicles.path_sections[i].node_indices[0] = scalars[0];
2180
            gProgram_state.AI_vehicles.path_sections[i].node_indices[1] = scalars[1];
2181
            gProgram_state.AI_vehicles.path_sections[i].min_speed[0] = scalars[2];
2182
            gProgram_state.AI_vehicles.path_sections[i].max_speed[0] = scalars[3];
2183
            gProgram_state.AI_vehicles.path_sections[i].min_speed[1] = scalars[4];
2184
            gProgram_state.AI_vehicles.path_sections[i].max_speed[1] = scalars[5];
2185
            gProgram_state.AI_vehicles.path_sections[i].width = scalars[6];
2186
            x = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[0]
2187
                - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[0];
2188
            y = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[1]
2189
                - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[1];
2190
            z = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[2]
2191
                - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[2];
2192
 
2193
            gProgram_state.AI_vehicles.path_sections[i].length = sqrtf(x * x + y * y + z * z);
2194
            if (scalars[7] < 1000.0f) {
2195
                gProgram_state.AI_vehicles.path_sections[i].type = (tU8)scalars[7];
2196
                gProgram_state.AI_vehicles.path_sections[i].one_way = 0;
2197
            } else {
2198
                gProgram_state.AI_vehicles.path_sections[i].type = (tU8)(scalars[7] - 1000.0f);
2199
                gProgram_state.AI_vehicles.path_sections[i].one_way = 1;
2200
            }
2201
            for (j = 0; j < 2; j++) {
2202
                node_ptr = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[j]];
2203
                if (node_ptr->number_of_sections >= 8u) {
2204
                    dr_dprintf(
2205
                        "ERROR: Too many sections (including section #%d) attached to node #%d",
2206
                        i,
2207
                        gProgram_state.AI_vehicles.path_sections[i].node_indices[j]);
2208
                    data_errors = 1;
2209
                } else {
2210
                    node_ptr->sections[node_ptr->number_of_sections] = (tS16)i;
2211
                    node_ptr->number_of_sections++;
2212
                }
2213
            }
2214
            x = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[0]
2215
                - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[0];
2216
            y = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[1]
2217
                - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[1];
2218
            z = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[2]
2219
                - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[2];
2220
            if (z * z + x * x + y * y == 0.0f) {
2221
                dr_dprintf(
2222
                    "ERROR: Opponent path section #%d has zero length (nodes #%d and #%d are in same position). Secti"
2223
                    "on and one node will be deleted.",
2224
                    j,
2225
                    gProgram_state.AI_vehicles.path_sections[i].node_indices[0],
2226
                    gProgram_state.AI_vehicles.path_sections[i].node_indices[1]);
2227
                delete_these[sections_to_delete] = j;
2228
                sections_to_delete++;
2229
            }
2230
        }
2231
 
2232
        if (data_errors) {
2233
            PDFatalError("Opponent path data inconsistencies. Unable to rectumify them.");
2234
        }
2235
        if (sections_to_delete != 0) {
2236
            for (j = 0; j < sections_to_delete; j++) {
2237
                dr_dprintf("Deleting section #%d (was #%d)", delete_these[j], j + delete_these[j]);
2238
                DeleteSection(delete_these[j]);
2239
                DeleteOrphanNodes();
2240
                for (section_no = j; section_no < sections_to_delete; section_no++) {
2241
                    delete_these[j]--;
2242
                }
2243
            }
2244
            WriteOutOppoPaths();
2245
            sprintf(
2246
                s,
2247
                "Errors in opponent path data. All have been corrected and written out to '%s'. Refer to diagnostic file "
2248
                "for more details.",
2249
                gOppo_path_filename);
2250
            PDFatalError(s);
2251
        }
2252
        if (gAusterity_mode || gNet_mode != eNet_mode_none) {
2253
            gProgram_state.AI_vehicles.number_of_cops = GetAnInt(pF);
2254
            for (j = 0; j < gProgram_state.AI_vehicles.number_of_cops; j++) {
2255
                GetALineAndDontArgue(pF, s);
2256
            }
2257
            gProgram_state.AI_vehicles.number_of_cops = 0;
2258
        } else {
2259
            gProgram_state.AI_vehicles.number_of_cops = GetAnInt(pF);
2260
            for (j = 0; j < gProgram_state.AI_vehicles.number_of_cops; j++) {
2261
                PossibleService();
2262
                GetNScalars(pF, 6, scalars);
2263
                BrVector3Set(&gProgram_state.AI_vehicles.cop_start_points[j], scalars[0], scalars[1], scalars[2]);
2264
 
2265
                if (scalars[3] == 9.0f && scalars[4] == 9.0f && scalars[5] == 9.0f) {
2266
                    gBIG_APC_index = j;
2267
                }
2268
 
2269
                FindNearestPathSection(&gProgram_state.AI_vehicles.cop_start_points[j], &cop_to_section, &intersect, &distance);
2270
                BrVector3Set(&gProgram_state.AI_vehicles.cop_start_vectors[j],
2271
                    cop_to_section.v[2] * 1.0f - cop_to_section.v[1] * 0.0f,
2272
                    cop_to_section.v[0] * 0.0f - cop_to_section.v[2] * 0.0f,
2273
                    cop_to_section.v[1] * 0.0f - cop_to_section.v[0] * 1.0f);
2274
                BrVector3Sub(&section_v, &intersect, &gProgram_state.AI_vehicles.cop_start_points[j]);
2275
                if (BrVector3Dot(&gProgram_state.AI_vehicles.cop_start_vectors[j], &section_v) < 0.0f) {
2276
                    BrVector3Negate(&gProgram_state.AI_vehicles.cop_start_vectors[j], &gProgram_state.AI_vehicles.cop_start_vectors[j]);
2277
                }
2278
            }
2279
        }
2280
        do {
2281
            GetALineAndDontArgue(pF, s);
2282
        } while (strcmp("END OF OPPONENT PATHS", s) != 0);
2283
        if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
2284
            gBit_per_node = BrMemAllocate((gProgram_state.AI_vehicles.number_of_path_nodes + 7) / 8, kMem_oppo_bit_per_node);
2285
        } else {
2286
            gBit_per_node = NULL;
2287
        }
2288
        dr_dprintf("End of LoadInOppoPaths(), totals:");
2289
        dr_dprintf("Nodes: %d", gProgram_state.AI_vehicles.number_of_path_nodes);
2290
        dr_dprintf("Sections: %d", gProgram_state.AI_vehicles.number_of_path_sections);
2291
        ConsistencyCheck();
2292
    }
2293
}
2294
 
2295
// IDA: void __cdecl DisposeOpponentPaths()
2296
void DisposeOpponentPaths(void) {
2297
    LOG_TRACE("()");
2298
 
2299
    if (gProgram_state.AI_vehicles.path_nodes != NULL) {
2300
        BrMemFree(gProgram_state.AI_vehicles.path_nodes);
2301
    }
2302
    if (gProgram_state.AI_vehicles.path_sections != NULL) {
2303
        BrMemFree(gProgram_state.AI_vehicles.path_sections);
2304
    }
2305
    if (gBit_per_node != NULL) {
2306
        BrMemFree(gBit_per_node);
2307
    }
2308
    gBit_per_node = NULL;
2309
    gProgram_state.AI_vehicles.number_of_path_nodes = 0;
2310
    gProgram_state.AI_vehicles.number_of_path_sections = 0;
2311
    gProgram_state.AI_vehicles.path_nodes = NULL;
2312
    gProgram_state.AI_vehicles.path_sections = NULL;
2313
}
2314
 
2315
// IDA: void __usercall MungeOpponents(tU32 pFrame_period@<EAX>)
2316
void MungeOpponents(tU32 pFrame_period) {
2317
    int i;
2318
    int un_stun_flag;
2319
    LOG_TRACE("(%d)", pFrame_period);
2320
 
2321
    un_stun_flag = 0;
2322
 
2323
    if (gProgram_state.AI_vehicles.number_of_opponents == 0 && gNumber_of_cops_before_faffage == 0) {
2324
        return;
2325
    }
2326
    gAcme_frame_count++;
2327
    gTime_stamp_for_this_munging = GetTotalTime();
2328
    gFrame_period_for_this_munging = pFrame_period;
2329
    gFrame_period_for_this_munging_in_secs = pFrame_period / 1000.f;
2330
    if (!gAcknowledged_start && !gCountdown) {
2331
        gAcknowledged_start = 1;
2332
        if (!gStart_jumped) {
2333
            un_stun_flag = 1;
2334
        }
2335
    }
2336
    if (gProgram_state.current_car.no_of_processes_recording_my_trail == 0) {
2337
        StartRecordingTrail(&gProgram_state.current_car);
2338
    } else {
2339
        RecordNextTrailNode(&gProgram_state.current_car);
2340
    }
2341
    TrackElasticateyPath();
2342
    if (gProcessing_opponents) {
2343
        gNum_of_opponents_pursuing = 0;
2344
        gNum_of_opponents_getting_near = 0;
2345
        gNum_of_opponents_completing_race = 0;
2346
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2347
            if (!gProgram_state.AI_vehicles.opponents[i].finished_for_this_race) {
2348
                switch (gProgram_state.AI_vehicles.opponents[i].current_objective) {
2349
                case eOOT_pursue_and_twat:
2350
                    gNum_of_opponents_pursuing++;
2351
                    break;
2352
                case eOOT_get_near_player:
2353
                    gNum_of_opponents_getting_near++;
2354
                    break;
2355
                case eOOT_complete_race:
2356
                    gNum_of_opponents_completing_race++;
2357
                    break;
2358
                default:
2359
                    break;
2360
                }
2361
            }
2362
        }
2363
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2364
            if (!gProgram_state.AI_vehicles.opponents[i].finished_for_this_race) {
2365
                if (un_stun_flag) {
2366
                    UnStunTheBugger(&gProgram_state.AI_vehicles.opponents[i]);
2367
                }
2368
                CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(&gProgram_state.AI_vehicles.opponents[i]);
2369
                CalcPlayerConspicuousness(&gProgram_state.AI_vehicles.opponents[i]);
2370
                ProcessThisOpponent(&gProgram_state.AI_vehicles.opponents[i]);
2371
                ClearTwattageOccurrenceVariables(&gProgram_state.AI_vehicles.opponents[i]);
2372
            }
2373
        }
2374
        for (i = 0; i < gNumber_of_cops_before_faffage; i++) {
2375
            if (!gProgram_state.AI_vehicles.cops[i].finished_for_this_race) {
2376
                if (un_stun_flag) {
2377
                    UnStunTheBugger(&gProgram_state.AI_vehicles.cops[i]);
2378
                }
2379
                CalcDistanceFromHome(&gProgram_state.AI_vehicles.cops[i]);
2380
                CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(&gProgram_state.AI_vehicles.cops[i]);
2381
                CalcPlayerConspicuousness(&gProgram_state.AI_vehicles.cops[i]);
2382
                ProcessThisOpponent(&gProgram_state.AI_vehicles.cops[i]);
2383
                ClearTwattageOccurrenceVariables(&gProgram_state.AI_vehicles.cops[i]);
2384
                gProgram_state.AI_vehicles.cops[i].murder_reported = 0;
2385
            }
2386
        }
2387
        if (gNext_grudge_reduction < gTime_stamp_for_this_munging) {
2388
            gNext_grudge_reduction = gTime_stamp_for_this_munging + 3000;
2389
            for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2390
                if (!gProgram_state.AI_vehicles.opponents[i].finished_for_this_race) {
2391
                    if (gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player >= gGrudge_reduction_per_period) {
2392
                        gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player -= gGrudge_reduction_per_period;
2393
                    } else {
2394
                        gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player = 0;
2395
                    }
2396
                }
2397
            }
2398
        }
2399
        RebuildActiveCarList();
2400
        gFirst_frame = 0;
2401
    }
2402
}
2403
 
2404
// IDA: void __cdecl SetInitialCopPositions()
2405
void SetInitialCopPositions(void) {
2406
    int i;
2407
    LOG_TRACE("()");
2408
 
2409
    for (i = 0; i < GetCarCount(eVehicle_rozzer); i++) {
2410
        PossibleService();
2411
        BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].car_spec->car_master_actor->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
2412
        PointActorAlongThisBloodyVector(gProgram_state.AI_vehicles.cops[i].car_spec->car_master_actor, &gProgram_state.AI_vehicles.cop_start_vectors[i]);
2413
        gProgram_state.AI_vehicles.cops[i].physics_me = 0;
2414
        RematerialiseOpponent(&gProgram_state.AI_vehicles.cops[i], 0.f);
2415
        InitCarSkidStuff(gProgram_state.AI_vehicles.cops[i].car_spec);
2416
    }
2417
}
2418
 
2419
// IDA: void __usercall InitOpponents(tRace_info *pRace_info@<EAX>)
2420
void InitOpponents(tRace_info* pRace_info) {
2421
    int i;
2422
    int opponent_number;
2423
    int rank_dependent_difficulty;
2424
    //int skill_dependent_difficulty; // Pierre-Marie Baty -- unused variable
2425
    br_bounds bounds;
2426
    tCar_spec* car_spec;
2427
    tOpponent_spec* opponent_spec;
2428
    LOG_TRACE("(%p)", pRace_info);
2429
 
2430
    gNext_grudge_reduction = gTime_stamp_for_this_munging + 8000;
2431
    gGrudge_reduction_per_period = 3 - gProgram_state.skill_level;
2432
    gProcessing_opponents = 1;
2433
    gFirst_frame = 1;
2434
    gAcknowledged_start = 0;
2435
    gStart_jumped = 0;
2436
    gViewable_car_list[0] = &gProgram_state.current_car;
2437
    gNum_viewable_cars = 1;
2438
    BrActorToBounds(&bounds, gProgram_state.track_spec.the_actor);
2439
    gMinimum_yness_before_knackerisation = bounds.min.v[1] - 2.f;
2440
    gDefinite_no_cop_pursuit_speed = 17.8788f;
2441
    gDefinite_cop_pursuit_speed = 44.697f;
2442
    gCop_pursuit_speed_percentage_multiplier = 100.f / (gDefinite_cop_pursuit_speed - gDefinite_no_cop_pursuit_speed);
2443
    gHead_on_cos_value = cosf(.5235668f);
2444
    gAcme_frame_count = 0;
2445
    gProgram_state.current_car.no_of_processes_recording_my_trail = 0;
2446
    rank_dependent_difficulty = (101.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank));
2447
    // FIXME: unsure about gBig_bang
2448
    gBig_bang = 70.f - (float)(3 * rank_dependent_difficulty + 10 * gProgram_state.skill_level) * gOpponent_nastyness_frigger;
2449
    gIn_view_distance = gCamera_yon + 10.f;
2450
    if (gCamera_yon + 10.f <= 45.f) {
2451
        gIn_view_distance = 45.f;
2452
    }
2453
    gTime_stamp_for_this_munging = GetTotalTime();
2454
    gFrame_period_for_this_munging = 1;
2455
    gFrame_period_for_this_munging_in_secs = gFrame_period_for_this_munging / 1000.f;
2456
    if (gNet_mode == eNet_mode_none) {
2457
        gProgram_state.AI_vehicles.number_of_opponents = pRace_info->number_of_racers - 1;
2458
    } else {
2459
        gProgram_state.AI_vehicles.number_of_opponents = 0;
2460
    }
2461
    gNumber_of_cops_before_faffage = gProgram_state.AI_vehicles.number_of_cops;
2462
    for (i = 0, opponent_number = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++, opponent_number++) {
2463
        PossibleService();
2464
        if (pRace_info->opponent_list[opponent_number].index < 0) {
2465
            opponent_number++;
2466
        }
2467
        gProgram_state.AI_vehicles.opponents[i].car_spec = pRace_info->opponent_list[opponent_number].car_spec;
2468
        gProgram_state.AI_vehicles.opponents[i].car_spec->car_ID = i | 0x200;
2469
        dr_dprintf("Car '%s', car_ID %x",
2470
            gProgram_state.AI_vehicles.opponents[i].car_spec->driver_name,
2471
            gProgram_state.AI_vehicles.opponents[i].car_spec->car_ID);
2472
        gProgram_state.AI_vehicles.opponents[i].index = pRace_info->opponent_list[opponent_number].index;
2473
        gProgram_state.AI_vehicles.opponents[i].time_last_processed = gTime_stamp_for_this_munging;
2474
        gProgram_state.AI_vehicles.opponents[i].time_this_objective_started = gTime_stamp_for_this_munging;
2475
        gProgram_state.AI_vehicles.opponents[i].last_moved_ok = gTime_stamp_for_this_munging;
2476
        gProgram_state.AI_vehicles.opponents[i].last_in_view = 0;
2477
        gProgram_state.AI_vehicles.opponents[i].stun_time_ends = 0;
2478
        gProgram_state.AI_vehicles.opponents[i].next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 2000;
2479
        gProgram_state.AI_vehicles.opponents[i].next_out_of_world_check = gTime_stamp_for_this_munging + 500;
2480
        gProgram_state.AI_vehicles.opponents[i].cunting_buttfuck_timer = 0;
2481
        gProgram_state.AI_vehicles.opponents[i].finished_for_this_race = 0;
2482
        gProgram_state.AI_vehicles.opponents[i].physics_me = 1;
2483
        gProgram_state.AI_vehicles.opponents[i].pursue_from_start = 0;
2484
        gProgram_state.AI_vehicles.opponents[i].cheating = 0;
2485
        gProgram_state.AI_vehicles.opponents[i].knackeredness_detected = 0;
2486
        gProgram_state.AI_vehicles.opponents[i].players_section_when_last_calced_full_path = -1;
2487
        gProgram_state.AI_vehicles.opponents[i].car_spec->last_person_to_hit_us = NULL;
2488
        gProgram_state.AI_vehicles.opponents[i].car_spec->last_person_we_hit = NULL;
2489
        gProgram_state.AI_vehicles.opponents[i].car_spec->last_collision_time = 0;
2490
        gProgram_state.AI_vehicles.opponents[i].car_spec->last_time_we_touched_a_player = 0;
2491
        gProgram_state.AI_vehicles.opponents[i].car_spec->grudge_raised_recently = 1;
2492
        gProgram_state.AI_vehicles.opponents[i].car_spec->no_of_processes_recording_my_trail = 0;
2493
        gProgram_state.AI_vehicles.opponents[i].nnext_sections = 0;
2494
        gProgram_state.AI_vehicles.opponents[i].new_objective_required = 1;
2495
        gProgram_state.AI_vehicles.opponents[i].current_objective = eOOT_none;
2496
        gProgram_state.AI_vehicles.opponents[i].has_moved_at_some_point = 0;
2497
        gProgram_state.AI_vehicles.opponents[i].player_in_view_now = 0;
2498
        gProgram_state.AI_vehicles.opponents[i].acknowledged_piv = 0;
2499
        gProgram_state.AI_vehicles.opponents[i].nastiness = (gProgram_state.skill_level / 2.f
2500
                                                                + ((float)(gOpponents[gProgram_state.AI_vehicles.opponents[i].index].strength_rating - 1)) / 4.f
2501
                                                                + (99.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank)) / 98.f)
2502
            / 3.f * gOpponent_nastyness_frigger;
2503
        BrVector3Set(&gProgram_state.AI_vehicles.opponents[i].pos_last_frame, 0.f, 0.f, 0.f);
2504
        gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player = 10;
2505
        gViewable_car_list[gNum_viewable_cars] = gProgram_state.AI_vehicles.opponents[i].car_spec;
2506
        gNum_viewable_cars++;
2507
        StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], 10000);
2508
    }
2509
    if (gChallenger_index__opponent >= 0) {
2510
        car_spec = GetCarSpecFromGlobalOppoIndex(gChallenger_index__opponent);
2511
        opponent_spec = GetOpponentSpecFromCarSpec(car_spec);
2512
        if (opponent_spec == NULL) {
2513
            dr_dprintf("ERROR - can't record dare - no opponent_spec for car_spec");
2514
        } else {
2515
            opponent_spec->pursue_from_start = 1;
2516
        }
2517
        gChallenger_index__opponent = -1;
2518
    }
2519
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2520
        PossibleService();
2521
        gProgram_state.AI_vehicles.cops[i].car_spec->car_ID = i | 0x300;
2522
        gProgram_state.AI_vehicles.cops[i].index = 3;
2523
        gProgram_state.AI_vehicles.cops[i].time_last_processed = gTime_stamp_for_this_munging;
2524
        gProgram_state.AI_vehicles.cops[i].time_this_objective_started = gTime_stamp_for_this_munging;
2525
        gProgram_state.AI_vehicles.cops[i].last_moved_ok = gTime_stamp_for_this_munging;
2526
        gProgram_state.AI_vehicles.cops[i].last_in_view = 0;
2527
        gProgram_state.AI_vehicles.cops[i].stun_time_ends = 0;
2528
        gProgram_state.AI_vehicles.cops[i].next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 5000;
2529
        gProgram_state.AI_vehicles.cops[i].next_out_of_world_check = gTime_stamp_for_this_munging + 500;
2530
        gProgram_state.AI_vehicles.cops[i].cunting_buttfuck_timer = 0;
2531
        gProgram_state.AI_vehicles.cops[i].finished_for_this_race = 0;
2532
        gProgram_state.AI_vehicles.cops[i].physics_me = 1;
2533
        gProgram_state.AI_vehicles.cops[i].pursue_from_start = 0;
2534
        gProgram_state.AI_vehicles.cops[i].cheating = 0;
2535
        gProgram_state.AI_vehicles.cops[i].murder_reported = 0;
2536
        gProgram_state.AI_vehicles.cops[i].finished_for_this_race = 0;
2537
        gProgram_state.AI_vehicles.cops[i].players_section_when_last_calced_full_path = -1;
2538
        gProgram_state.AI_vehicles.cops[i].nnext_sections = 0;
2539
        gProgram_state.AI_vehicles.cops[i].new_objective_required = 1;
2540
        gProgram_state.AI_vehicles.cops[i].current_objective = eOOT_none;
2541
        gProgram_state.AI_vehicles.cops[i].player_in_view_now = 0;
2542
        gProgram_state.AI_vehicles.cops[i].acknowledged_piv = 0;
2543
        gProgram_state.AI_vehicles.cops[i].nastiness = (gProgram_state.skill_level / 2.f
2544
                                                           + (99.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank)) / 98.f
2545
                                                           + 2.25f)
2546
            / 3.f * gOpponent_nastyness_frigger;
2547
        BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].start_pos, &gProgram_state.AI_vehicles.cop_start_points[i]);
2548
        BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].start_direction, &gProgram_state.AI_vehicles.cop_start_vectors[i]);
2549
        BrVector3Set(&gProgram_state.AI_vehicles.cops[i].pos_last_frame, 0.f, 0.f, 0.f);
2550
        gViewable_car_list[gNum_viewable_cars] = gProgram_state.AI_vehicles.cops[i].car_spec;
2551
        gNum_viewable_cars++;
2552
        gProgram_state.AI_vehicles.cops[i].car_spec->last_person_to_hit_us = NULL;
2553
        gProgram_state.AI_vehicles.cops[i].car_spec->last_person_we_hit = NULL;
2554
        gProgram_state.AI_vehicles.cops[i].car_spec->last_collision_time = 0;
2555
        gProgram_state.AI_vehicles.cops[i].car_spec->last_time_we_touched_a_player = 0;
2556
        gProgram_state.AI_vehicles.cops[i].car_spec->grudge_raised_recently = 1;
2557
        gOpponents[gProgram_state.AI_vehicles.cops[i].index].psyche.grudge_against_player = 10;
2558
        StunTheBugger(&gProgram_state.AI_vehicles.cops[i], 10000);
2559
    }
2560
    gActive_car_list_rebuild_required = 1;
2561
    RebuildActiveCarList();
2562
}
2563
 
2564
// IDA: void __cdecl DisposeOpponents()
2565
void DisposeOpponents(void) {
2566
    int i;
2567
    LOG_TRACE("()");
2568
 
2569
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2570
        DisposeCar(gProgram_state.AI_vehicles.cops[i].car_spec, (i == gBIG_APC_index) ? 4 : 3);
2571
        BrMemFree(gProgram_state.AI_vehicles.cops[i].car_spec);
2572
    }
2573
}
2574
 
2575
// IDA: void __usercall WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(int pWhat_the_countdown_was@<EAX>)
2576
void WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(int pWhat_the_countdown_was) {
2577
    int i;
2578
    LOG_TRACE("(%d)", pWhat_the_countdown_was);
2579
 
2580
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2581
        UnStunTheBugger(&gProgram_state.AI_vehicles.opponents[i]);
2582
        if (IRandomBetween(1000, 2500) < 1000 * pWhat_the_countdown_was) {
2583
            StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], IRandomBetween(1000, 2500));
2584
        } else {
2585
            StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], 1000 * pWhat_the_countdown_was);
2586
        }
2587
    }
2588
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2589
        UnStunTheBugger(&gProgram_state.AI_vehicles.cops[i]);
2590
        if (IRandomBetween(1000, 2500) < 1000 * pWhat_the_countdown_was) {
2591
            StunTheBugger(&gProgram_state.AI_vehicles.cops[i], IRandomBetween(1000, 2500));
2592
        } else {
2593
            StunTheBugger(&gProgram_state.AI_vehicles.cops[i], 1000 * pWhat_the_countdown_was);
2594
        }
2595
    }
2596
    gStart_jumped = 1;
2597
    gAcknowledged_start = 1;
2598
}
2599
 
2600
// IDA: void __usercall ReportMurderToPoliceDepartment(tCar_spec *pCar_spec@<EAX>)
2601
void ReportMurderToPoliceDepartment(tCar_spec* pCar_spec) {
2602
    int i;
2603
    LOG_TRACE("(%p)", pCar_spec);
2604
 
2605
    if (pCar_spec == &gProgram_state.current_car) {
2606
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2607
            gProgram_state.AI_vehicles.cops[i].murder_reported = 1;
2608
        }
2609
    }
2610
}
2611
 
2612
// IDA: int __usercall GetCarCount@<EAX>(tVehicle_type pCategory@<EAX>)
2613
int GetCarCount(tVehicle_type pCategory) {
2614
    LOG_TRACE("(%d)", pCategory);
2615
 
2616
    switch (pCategory) {
2617
    case eVehicle_self:
2618
        return 1;
2619
 
2620
    case eVehicle_net_player:
2621
        if (gNet_mode) {
2622
            return gNumber_of_net_players - 1;
2623
        } else {
2624
            return 0;
2625
        }
2626
        break;
2627
    case eVehicle_opponent:
2628
        return gProgram_state.AI_vehicles.number_of_opponents;
2629
 
2630
    case eVehicle_rozzer:
2631
        return gNumber_of_cops_before_faffage;
2632
 
2633
    case eVehicle_drone:
2634
        return 0;
2635
 
2636
    case eVehicle_not_really:
2637
        return gNum_active_non_cars;
2638
 
2639
    default:
2640
        return 0;
2641
    }
2642
}
2643
 
2644
// IDA: tCar_spec* __usercall GetCarSpec@<EAX>(tVehicle_type pCategory@<EAX>, int pIndex@<EDX>)
2645
tCar_spec* GetCarSpec(tVehicle_type pCategory, int pIndex) {
2646
    LOG_TRACE("(%d, %d)", pCategory, pIndex);
2647
 
2648
    switch (pCategory) {
2649
 
2650
    case eVehicle_self:
2651
        return &gProgram_state.current_car;
2652
 
2653
    case eVehicle_net_player:
2654
        if (gThis_net_player_index > pIndex) {
2655
            return gNet_players[pIndex].car;
2656
        } else {
2657
            return gNet_players[pIndex + 1].car;
2658
        }
2659
 
2660
    case eVehicle_opponent:
2661
        return gProgram_state.AI_vehicles.opponents[pIndex].car_spec;
2662
 
2663
    case eVehicle_rozzer:
2664
        return gProgram_state.AI_vehicles.cops[pIndex].car_spec;
2665
 
2666
    case eVehicle_drone:
2667
        PDEnterDebugger("OPPONENT.C: GetCarSpec() can't return drone car_specs");
2668
        return 0;
2669
 
2670
    case eVehicle_not_really:
2671
        return (tCar_spec*)gActive_non_car_list[pIndex];
2672
 
2673
    default:
2674
        return 0;
2675
    }
2676
}
2677
 
2678
// IDA: char* __usercall GetDriverName@<EAX>(tVehicle_type pCategory@<EAX>, int pIndex@<EDX>)
2679
char* GetDriverName(tVehicle_type pCategory, int pIndex) {
2680
    LOG_TRACE("(%d, %d)", pCategory, pIndex);
2681
 
2682
    switch (pCategory) {
2683
    case eVehicle_self:
2684
        return gProgram_state.player_name[gProgram_state.frank_or_anniness];
2685
    case eVehicle_opponent:
2686
        return gOpponents[gProgram_state.AI_vehicles.opponents[pIndex].index].name;
2687
    case eVehicle_rozzer:
2688
        return "Faceless Cop";
2689
    case eVehicle_drone:
2690
        return "Innocent Civilian";
2691
    case eVehicle_not_really:
2692
    default:
2693
        return NULL;
2694
    }
2695
}
2696
 
2697
// IDA: tOpponent_spec* __usercall GetOpponentSpecFromCarSpec@<EAX>(tCar_spec *pCar_spec@<EAX>)
2698
tOpponent_spec* GetOpponentSpecFromCarSpec(tCar_spec* pCar_spec) {
2699
    int i;
2700
    LOG_TRACE("(%p)", pCar_spec);
2701
 
2702
    if ((pCar_spec->car_ID & 0xff00) == 0x200) {
2703
        for (i = 0; i < GetCarCount(eVehicle_opponent); i++) {
2704
            if (gProgram_state.AI_vehicles.opponents[i].car_spec == pCar_spec) {
2705
                return &gProgram_state.AI_vehicles.opponents[i];
2706
            }
2707
        }
2708
    } else if ((pCar_spec->car_ID & 0xff00) == 0x300) {
2709
        for (i = 0; i < GetCarCount(eVehicle_rozzer); i++) {
2710
            if (gProgram_state.AI_vehicles.cops[i].car_spec == pCar_spec) {
2711
                return &gProgram_state.AI_vehicles.cops[i];
2712
            }
2713
        }
2714
    }
2715
    return NULL;
2716
}
2717
 
2718
// IDA: tCar_spec* __usercall GetCarSpecFromGlobalOppoIndex@<EAX>(int pIndex@<EAX>)
2719
tCar_spec* GetCarSpecFromGlobalOppoIndex(int pIndex) {
2720
    int i;
2721
    LOG_TRACE("(%d)", pIndex);
2722
 
2723
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2724
        if (gProgram_state.AI_vehicles.opponents[i].index == pIndex) {
2725
            return gProgram_state.AI_vehicles.opponents[i].car_spec;
2726
        }
2727
    }
2728
    return NULL;
2729
}
2730
 
2731
// IDA: int __usercall GetOpponentsRealSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pSection_no@<EDX>)
2732
int GetOpponentsRealSection(tOpponent_spec* pOpponent_spec, int pSection_no) {
2733
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection_no);
2734
 
2735
    if (pSection_no >= 20000) {
2736
        return pOpponent_spec->next_sections[pSection_no - 20000].section_no;
2737
    } else if (pSection_no >= 10000) {
2738
        return -1;
2739
    } else {
2740
        return pSection_no;
2741
    }
2742
}
2743
 
2744
// IDA: int __usercall GetOpponentsFirstSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
2745
int GetOpponentsFirstSection(tOpponent_spec* pOpponent_spec) {
2746
    LOG_TRACE("(%p)", pOpponent_spec);
2747
 
2748
    if (pOpponent_spec->current_objective != eOOT_pursue_and_twat) {
2749
        return 20000;
2750
    }
2751
    if (pOpponent_spec->pursue_car_data.state == ePCS_following_trail) {
2752
        return pOpponent_spec->follow_path_data.section_no;
2753
    }
2754
    if (pOpponent_spec->pursue_car_data.state == ePCS_following_line_of_sight) {
2755
        return 10000;
2756
    }
2757
    return 20000;
2758
}
2759
 
2760
// IDA: int __usercall GetOpponentsNextSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pCurrent_section@<EDX>)
2761
int GetOpponentsNextSection(tOpponent_spec* pOpponent_spec, tS16 pCurrent_section) {
2762
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCurrent_section);
2763
 
2764
    if (pCurrent_section < 20000) {
2765
        if (pCurrent_section < 15000) {
2766
            return -1;
2767
        } else {
2768
            return CalcNextTrailSection(pOpponent_spec, pCurrent_section);
2769
        }
2770
    } else if (pCurrent_section - 19999 >= pOpponent_spec->nnext_sections || (!pOpponent_spec->cheating && gProgram_state.AI_vehicles.path_sections[pCurrent_section - 19999].type == ePST_cheat_only)) {
2771
        return -1;
2772
    } else {
2773
        return pCurrent_section + 1;
2774
    }
2775
}
2776
 
2777
// IDA: tS16 __usercall GetOpponentsSectionStartNode@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2778
tS16 GetOpponentsSectionStartNode(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2779
    tS16 section_no;
2780
    int node_index_index;
2781
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2782
 
2783
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
20 pmbaty 2784
      node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction == 0;
2785
      if (pSection - 20000 > pOpponent_spec->nnext_sections) {
2786
          section_no = -1;
2787
      } else {
2788
          section_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].node_indices[node_index_index];
2789
          return section_no;
2790
      }
1 pmbaty 2791
    }
2792
    dr_dprintf("BIG ERROR - GetOpponentsSectionStartNode() - section not found in next_section array for opponent %s",
2793
        pOpponent_spec->car_spec->driver_name);
2794
    PDEnterDebugger("BIG ERROR - GetOpponentsSectionStartNode()");
2795
    return -1;
2796
}
2797
 
2798
// IDA: tS16 __usercall GetOpponentsSectionFinishNode@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2799
tS16 GetOpponentsSectionFinishNode(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2800
    //tS16 section_no; // Pierre-Marie Baty -- unused variable
2801
    //int node_index_index; // Pierre-Marie Baty -- unused variable
2802
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2803
 
20 pmbaty 2804
   if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2805
       return gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].node_indices[pOpponent_spec->next_sections[pSection - 20000].direction];
2806
   }
2807
   dr_dprintf("BIG ERROR - GetOpponentsSectionFinishNode() - section not found in next_section array for opponent %s",
2808
       pOpponent_spec->car_spec->driver_name);
2809
   PDEnterDebugger("BIG ERROR - GetOpponentsSectionFinishNode()");
2810
   return 0;
1 pmbaty 2811
}
2812
 
2813
// IDA: br_vector3* __usercall GetOpponentsSectionStartNodePoint@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2814
br_vector3* GetOpponentsSectionStartNodePoint(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2815
    tS16 section_no;
2816
    tS16 node_no;
2817
    int node_index_index;
2818
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2819
 
2820
    if (pSection >= 20000 && pOpponent_spec->nnext_sections > pSection - 20000) {
2821
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2822
        node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction;
2823
        node_no = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[node_index_index == 0];
2824
        return &gProgram_state.AI_vehicles.path_nodes[node_no].p;
2825
    }
2826
 
2827
    if (pSection >= 15000) {
2828
        return &pOpponent_spec->pursue_car_data.pursuee->my_trail.trail_nodes[pSection - 15000];
2829
    }
2830
    if (pSection == 10000) {
2831
        return &pOpponent_spec->pursue_car_data.direct_line_nodes[0].p;
2832
    }
2833
    dr_dprintf("BIG ERROR - GetOpponentsSectionStartNodePoint() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
2834
    PDEnterDebugger("BIG ERROR - GetOpponentsSectionStartNodePoint()");
2835
    return 0;
2836
}
2837
 
2838
// IDA: br_vector3* __usercall GetOpponentsSectionFinishNodePoint@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2839
br_vector3* GetOpponentsSectionFinishNodePoint(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2840
    tS16 section_no;
2841
    tS16 node_no;
2842
    int node_index_index;
2843
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2844
 
2845
    if (pSection >= 20000 && pOpponent_spec->nnext_sections > pSection - 20000) {
2846
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2847
        node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction;
2848
        node_no = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[node_index_index];
2849
        return &gProgram_state.AI_vehicles.path_nodes[node_no].p;
2850
    } else if (pSection >= 15000) {
2851
        return &pOpponent_spec->pursue_car_data.pursuee->my_trail.trail_nodes[(pSection + 1) - 15000];
2852
    } else if (pSection == 10000) {
2853
        return &pOpponent_spec->pursue_car_data.direct_line_nodes[1].p;
2854
    } else {
2855
        dr_dprintf("BIG ERROR - GetOpponentsSectionFinishNodePoint() - section not found in next_section array for opponent %s",
2856
            pOpponent_spec->car_spec->driver_name);
2857
        PDEnterDebugger("BIG ERROR - GetOpponentsSectionFinishNodePoint()");
2858
        return NULL;
2859
    }
2860
}
2861
 
2862
// IDA: br_scalar __usercall GetOpponentsSectionWidth@<ST0>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2863
br_scalar GetOpponentsSectionWidth(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2864
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2865
 
2866
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2867
        return gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].width;
2868
    }
2869
    if (pSection >= 15000) {
2870
        return 0.5f;
2871
    }
2872
    if (pSection == 10000) {
2873
        return pOpponent_spec->pursue_car_data.direct_line_section.width;
2874
    }
2875
    return gProgram_state.AI_vehicles.path_sections[pSection].width;
2876
}
2877
 
2878
// IDA: int __usercall GetOpponentsSectionMinSpeed@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>, int pTowards_finish@<EBX>)
2879
int GetOpponentsSectionMinSpeed(tOpponent_spec* pOpponent_spec, tS16 pSection, int pTowards_finish) {
2880
    tS16 section_no;
2881
    int direction;
2882
    LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection, pTowards_finish);
2883
 
2884
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2885
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2886
        direction = pOpponent_spec->next_sections[pSection - 20000].direction;
2887
        return gProgram_state.AI_vehicles.path_sections[section_no].min_speed[pTowards_finish == direction];
2888
    }
2889
    if (pSection >= 15000) {
2890
        return 0;
2891
    }
2892
    if (pSection == 10000) {
2893
        return pOpponent_spec->pursue_car_data.direct_line_section.min_speed[pTowards_finish];
2894
    }
2895
    dr_dprintf("WARNING - GetOpponentsSectionMinSpeed() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
2896
    PDEnterDebugger("WARNING - GetOpponentsSectionMinSpeed()");
2897
    return 0;
2898
}
2899
 
2900
// IDA: int __usercall GetOpponentsSectionMaxSpeed@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>, int pTowards_finish@<EBX>)
2901
int GetOpponentsSectionMaxSpeed(tOpponent_spec* pOpponent_spec, tS16 pSection, int pTowards_finish) {
2902
    tS16 section_no;
2903
    int direction;
2904
    LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection, pTowards_finish);
2905
 
2906
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2907
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2908
        direction = pOpponent_spec->next_sections[pSection - 20000].direction;
2909
        return gProgram_state.AI_vehicles.path_sections[section_no].max_speed[pTowards_finish == direction];
2910
    }
2911
    if (pSection >= 15000) {
2912
        return 255;
2913
    }
2914
    if (pSection == 10000) {
2915
        return pOpponent_spec->pursue_car_data.direct_line_section.max_speed[pTowards_finish];
2916
    }
2917
    dr_dprintf("WARNING - GetOpponentsSectionMaxSpeed() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
2918
    PDEnterDebugger("WARNING - GetOpponentsSectionMaxSpeed()");
2919
    return 255;
2920
}
2921
 
2922
// IDA: void __usercall InitOpponentPsyche(int pOpponent_index@<EAX>)
2923
void InitOpponentPsyche(int pOpponent_index) {
2924
    gOpponents[pOpponent_index].psyche.grudge_against_player = 0;
2925
}
2926
 
2927
// IDA: void __usercall ClearTwattageOccurrenceVariables(tOpponent_spec *pOpponent_spec@<EAX>)
2928
void ClearTwattageOccurrenceVariables(tOpponent_spec* pOpponent_spec) {
2929
    LOG_TRACE("(%p)", pOpponent_spec);
2930
 
2931
    pOpponent_spec->car_spec->big_bang = 0;
2932
    pOpponent_spec->car_spec->scary_bang = 0;
2933
    pOpponent_spec->car_spec->grudge_raised_recently = 0;
2934
    pOpponent_spec->car_spec->last_person_to_hit_us = NULL;
2935
    pOpponent_spec->car_spec->last_person_we_hit = NULL;
2936
}
2937
 
2938
// IDA: void __usercall TwoCarsHitEachOther(tCar_spec *pA_car@<EAX>, tCar_spec *pAnother_car@<EDX>)
2939
void TwoCarsHitEachOther(tCar_spec* pA_car, tCar_spec* pAnother_car) {
2940
    LOG_TRACE("(%p, %p)", pA_car, pAnother_car);
2941
 
2942
    if (pA_car->driver == eDriver_local_human) {
2943
        pAnother_car->last_time_we_touched_a_player = gTime_stamp_for_this_munging;
2944
    }
2945
    if (pAnother_car->driver == eDriver_local_human) {
2946
        pA_car->last_time_we_touched_a_player = gTime_stamp_for_this_munging;
2947
    }
2948
}
2949
 
2950
// IDA: void __usercall RecordOpponentTwattageOccurrence(tCar_spec *pTwatter@<EAX>, tCar_spec *pTwattee@<EDX>)
2951
void RecordOpponentTwattageOccurrence(tCar_spec* pTwatter, tCar_spec* pTwattee) {
2952
    int bangness;
2953
    int twatter_index;
2954
    int twattee_index;
2955
    int grudginess_caused_by_damage;
2956
    int new_grudge_value;
2957
    float damage;
2958
    //char str[256]; // Pierre-Marie Baty -- unused variable
2959
    tOpponent_spec* twattee_opponent_spec;
2960
    tOpponent_spec* twatter_opponent_spec;
2961
    LOG_TRACE("(%p, %p)", pTwatter, pTwattee);
2962
 
2963
    if (pTwatter->driver != eDriver_oppo && pTwattee->driver != eDriver_oppo) {
2964
        return;
2965
    }
2966
    damage = pTwattee->damage_magnitude_accumulator;
2967
    bangness = MIN(sqrtf(damage * 300000.0f), 100);
2968
    grudginess_caused_by_damage = bangness / 10 + 50 * CAR_SPEC_IS_ROZZER(pTwattee);
2969
    dr_dprintf("Frame %0.6d: %s hit %s, damage %f, bangness %d, gBig_bang %d, grudginess %d",
2970
        gAcme_frame_count,
2971
        pTwatter->driver_name,
2972
        pTwattee->driver_name,
2973
        damage,
2974
        bangness,
2975
        gBig_bang,
2976
        grudginess_caused_by_damage);
2977
    if (gMin_bangness <= bangness) {
2978
        if (gMax_bangness < bangness) {
2979
            gMax_bangness = bangness;
2980
            dr_dprintf("(New gMax_bangness - %d)", bangness);
2981
        }
2982
    } else {
2983
        gMin_bangness = bangness;
2984
        dr_dprintf("(New gMin_bangness - %d)", bangness);
2985
    }
2986
    if (bangness >= 5) {
2987
        pTwatter->last_collision_time = gTime_stamp_for_this_munging;
2988
        pTwatter->last_person_we_hit = pTwattee;
2989
        pTwattee->last_collision_time = gTime_stamp_for_this_munging;
2990
        pTwattee->last_person_to_hit_us = pTwatter;
2991
        pTwattee->grudge_raised_recently = 1;
2992
        if (bangness >= gBig_bang || CAR_SPEC_IS_ROZZER(pTwattee)) {
2993
            pTwattee->big_bang = 1;
2994
        }
2995
        if (bangness >= 80) {
2996
            pTwattee->scary_bang = 1;
2997
        }
2998
        if (pTwatter->driver == eDriver_local_human) {
2999
            twattee_opponent_spec = GetOpponentSpecFromCarSpec(pTwattee);
3000
            if (pTwattee->scary_bang) {
3001
                StunTheBugger(twattee_opponent_spec, 30 * bangness + 1000);
3002
            }
3003
            new_grudge_value = grudginess_caused_by_damage + gOpponents[twattee_opponent_spec->index].psyche.grudge_against_player;
3004
            if (new_grudge_value > 100) {
3005
                new_grudge_value = 100;
3006
            }
3007
            gOpponents[twattee_opponent_spec->index].psyche.grudge_against_player = new_grudge_value;
3008
        } else if (pTwattee->driver == eDriver_local_human) {
3009
            twatter_opponent_spec = GetOpponentSpecFromCarSpec(pTwatter);
3010
            if (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat && twatter_opponent_spec->pursue_car_data.pursuee == pTwattee) {
3011
                twatter_opponent_spec->pursue_car_data.time_last_twatted_em = gTime_stamp_for_this_munging;
3012
            }
3013
            twatter_index = twatter_opponent_spec->index;
3014
            new_grudge_value = gOpponents[twatter_index].psyche.grudge_against_player - (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat ? 0 : 2 * grudginess_caused_by_damage);
3015
            if (new_grudge_value < 0) {
3016
                new_grudge_value = 0;
3017
            }
3018
            gOpponents[twatter_index].psyche.grudge_against_player = new_grudge_value;
3019
        } else {
3020
            twatter_opponent_spec = GetOpponentSpecFromCarSpec(pTwatter);
3021
            twattee_opponent_spec = GetOpponentSpecFromCarSpec(pTwattee);
3022
            if (pTwattee->scary_bang) {
3023
                StunTheBugger(twattee_opponent_spec, 30 * bangness + 1000);
3024
            }
3025
            twattee_index = twattee_opponent_spec->index;
3026
            if (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat && twatter_opponent_spec->pursue_car_data.pursuee == pTwattee) {
3027
                twatter_opponent_spec->pursue_car_data.time_last_twatted_em = gTime_stamp_for_this_munging;
3028
            }
3029
            if (CAR_SPEC_IS_OPPONENT(pTwatter) && CAR_SPEC_IS_ROZZER(pTwattee)) {
3030
                new_grudge_value = grudginess_caused_by_damage + gOpponents[twattee_index].psyche.grudge_against_player;
3031
                if (new_grudge_value > 100) {
3032
                    new_grudge_value = 100;
3033
                }
3034
                gOpponents[twattee_index].psyche.grudge_against_player = new_grudge_value;
3035
            }
3036
        }
3037
    }
3038
}
3039
 
3040
// IDA: void __cdecl ToggleOpponentTest()
3041
void ToggleOpponentTest(void) {
3042
    LOG_TRACE("()");
3043
 
3044
    gTest_toggle = !gTest_toggle;
3045
}
3046
 
3047
// IDA: void __cdecl ToggleOpponentProcessing()
3048
void ToggleOpponentProcessing(void) {
3049
    int i;
3050
    LOG_TRACE("()");
3051
 
3052
    gProcessing_opponents = !gProcessing_opponents;
3053
    if (gProcessing_opponents) {
3054
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3055
            ObjectiveComplete(&gProgram_state.AI_vehicles.opponents[i]);
3056
        }
3057
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3058
            ObjectiveComplete(&gProgram_state.AI_vehicles.cops[i]);
3059
        }
3060
        NewTextHeadupSlot(4, 0, 2000, -1, "OPPONENTS SWITCHED ON");
3061
    } else {
3062
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3063
            gProgram_state.AI_vehicles.opponents[i].physics_me = 0;
3064
        }
3065
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3066
            gProgram_state.AI_vehicles.opponents[i].physics_me = 0;
3067
        }
3068
        gActive_car_list_rebuild_required = 1;
3069
        RebuildActiveCarList();
3070
        NewTextHeadupSlot(4, 0, 2000, -1, "OPPONENTS SWITCHED OFF");
3071
    }
3072
}
3073
 
3074
// IDA: void __cdecl ToggleMellowOpponents()
3075
void ToggleMellowOpponents(void) {
3076
    int i;
3077
    LOG_TRACE("()");
3078
 
3079
    gMellow_opponents = !gMellow_opponents;
3080
    if (gMellow_opponents) {
3081
        NewTextHeadupSlot(4, 0, 3000, -1, "Opponents all nice and fluffy");
3082
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3083
            ObjectiveComplete(&gProgram_state.AI_vehicles.opponents[i]);
3084
        }
3085
    } else {
3086
        NewTextHeadupSlot(4, 0, 2000, -1, "Opponents hostile again");
3087
    }
3088
}
3089
 
3090
// IDA: void __cdecl RepairOpponentsSystems()
3091
void RepairOpponentsSystems(void) {
3092
    int i;
3093
    LOG_TRACE("()");
3094
 
3095
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3096
        if (!gProgram_state.AI_vehicles.opponents[i].pursue_from_start) {
3097
            TotallyRepairACar(gProgram_state.AI_vehicles.opponents[i].car_spec);
3098
            TurnOpponentPhysicsOff(&gProgram_state.AI_vehicles.opponents[i]);
3099
            gProgram_state.AI_vehicles.opponents[i].knackeredness_detected = 0;
3100
        }
3101
    }
3102
    NewTextHeadupSlot(4, 0, 3000, -1, "Opponents systems repaired (but not bodywork)");
3103
}
3104
 
3105
// IDA: void __usercall CopyVertex(br_vertex *pDest_vertex@<EAX>, br_vertex *pSrc_vertex@<EDX>)
3106
//  Suffix added to avoid duplicate symbol
3107
void CopyVertex__opponent(br_vertex* pDest_vertex, br_vertex* pSrc_vertex) {
3108
    LOG_TRACE("(%p, %p)", pDest_vertex, pSrc_vertex);
3109
 
3110
    BrVector3Copy(&pDest_vertex->p, &pSrc_vertex->p);
3111
    pDest_vertex->map.v[0] = pSrc_vertex->map.v[0];
3112
    pDest_vertex->map.v[1] = pSrc_vertex->map.v[1];
3113
    pDest_vertex->index = pSrc_vertex->index;
3114
    pDest_vertex->red = pSrc_vertex->red;
3115
    pDest_vertex->grn = pSrc_vertex->grn;
3116
    pDest_vertex->blu = pSrc_vertex->blu;
3117
}
3118
 
3119
// IDA: void __usercall CopyFace(br_face *pDest_face@<EAX>, br_face *pSrc_face@<EDX>)
3120
//  Suffix added to avoid duplicate symbol
3121
void CopyFace__opponent(br_face* pDest_face, br_face* pSrc_face) {
3122
    LOG_TRACE("(%p, %p)", pDest_face, pSrc_face);
3123
 
3124
    pDest_face->vertices[0] = pSrc_face->vertices[0];
3125
    pDest_face->vertices[1] = pSrc_face->vertices[1];
3126
    pDest_face->vertices[2] = pSrc_face->vertices[2];
3127
    pDest_face->material = pSrc_face->material;
3128
    pDest_face->smoothing = pSrc_face->smoothing;
3129
    pDest_face->flags = pSrc_face->flags;
3130
}
3131
 
3132
// IDA: void __usercall DeleteSection(tS16 pSection_to_delete@<EAX>)
3133
void DeleteSection(tS16 pSection_to_delete) {
3134
    tS16 section_no;
3135
    tS16 section_no_index;
3136
    tS16 node_no;
3137
    tS16 node_no_index;
3138
    tS16 found_it;
3139
    LOG_TRACE("(%d)", pSection_to_delete);
3140
 
3141
    for (node_no = 0; node_no < 2; node_no++) {
3142
        node_no_index = gProgram_state.AI_vehicles.path_sections[pSection_to_delete].node_indices[node_no];
3143
        if (node_no_index >= 0) {
3144
            found_it = 0;
3145
            for (section_no = 0; section_no < (gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections - 1); section_no++) {
3146
                if (gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no] == pSection_to_delete) {
3147
                    found_it = 1;
3148
                }
3149
                if (found_it) {
3150
                    gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no] = gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no + 1];
3151
                }
3152
            }
3153
            if (gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections != 0) {
3154
                gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections--;
3155
            }
3156
        }
3157
    }
3158
    for (section_no = pSection_to_delete; section_no < (gProgram_state.AI_vehicles.number_of_path_sections - 1); section_no++) {
3159
        gProgram_state.AI_vehicles.path_sections[section_no] = gProgram_state.AI_vehicles.path_sections[section_no + 1];
3160
    }
3161
    gProgram_state.AI_vehicles.number_of_path_sections--;
3162
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3163
        for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index++) {
3164
            if (pSection_to_delete < gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index]) {
3165
                gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index]--;
3166
            }
3167
        }
3168
    }
3169
}
3170
 
3171
// IDA: void __usercall DeleteNode(tS16 pNode_to_delete@<EAX>, int pAnd_sections@<EDX>)
3172
void DeleteNode(tS16 pNode_to_delete, int pAnd_sections) {
3173
    tS16 node_no;
3174
    tS16 section_no;
3175
    tS16 section1;
3176
    tS16 section2;
3177
    LOG_TRACE("(%d, %d)", pNode_to_delete, pAnd_sections);
3178
 
3179
    dr_dprintf("Node to be deleted #%d", pNode_to_delete);
3180
    if (pAnd_sections) {
3181
        while (gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].number_of_sections != 0) {
3182
            DeleteSection(gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0]);
3183
        }
3184
    } else {
3185
        if (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0]].node_indices[0] == pNode_to_delete) {
3186
            section1 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[1];
3187
            section2 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0];
3188
        } else {
3189
            section1 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0];
3190
            section2 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[1];
3191
        }
3192
        dr_dprintf("Section 1#%d(#%d,#%d), section 2#%d(#%d,#%d)", section1,
3193
            gProgram_state.AI_vehicles.path_sections[section1].node_indices[0],
3194
            gProgram_state.AI_vehicles.path_sections[section1].node_indices[1],
3195
            section2,
3196
            gProgram_state.AI_vehicles.path_sections[section2].node_indices[0],
3197
            gProgram_state.AI_vehicles.path_sections[section2].node_indices[1]);
3198
        gProgram_state.AI_vehicles.path_sections[section1].min_speed[1] = gProgram_state.AI_vehicles.path_sections[section2].min_speed[1];
3199
        gProgram_state.AI_vehicles.path_sections[section1].max_speed[1] = gProgram_state.AI_vehicles.path_sections[section2].max_speed[1];
3200
        node_no = gProgram_state.AI_vehicles.path_sections[section2].node_indices[1];
3201
        gProgram_state.AI_vehicles.path_sections[section1].node_indices[1] = node_no;
3202
        dr_dprintf("Section 1's new end node is #%d", node_no);
3203
        if (gProgram_state.AI_vehicles.path_nodes[node_no].sections[0] == section2) {
3204
            gProgram_state.AI_vehicles.path_nodes[node_no].sections[0] = section1;
3205
        } else {
3206
            gProgram_state.AI_vehicles.path_nodes[node_no].sections[1] = section1;
3207
        }
3208
        gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].number_of_sections = 0;
3209
        gProgram_state.AI_vehicles.path_sections[section2].node_indices[0] = -1;
3210
        gProgram_state.AI_vehicles.path_sections[section2].node_indices[1] = -1;
3211
        DeleteSection(section2);
3212
    }
3213
    for (node_no = pNode_to_delete; node_no < (gProgram_state.AI_vehicles.number_of_path_nodes - 1); node_no++) {
3214
        gProgram_state.AI_vehicles.path_nodes[node_no] = gProgram_state.AI_vehicles.path_nodes[node_no + 1];
3215
    }
3216
    for (section_no = 0; section_no < gProgram_state.AI_vehicles.number_of_path_sections; section_no++) {
3217
        if (pNode_to_delete < gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]) {
3218
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]--;
3219
        }
3220
        if (pNode_to_delete < gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]) {
3221
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]--;
3222
        }
3223
    }
3224
    gProgram_state.AI_vehicles.number_of_path_nodes--;
3225
}
3226
 
3227
// IDA: void __cdecl DeleteOrphanNodes()
3228
void DeleteOrphanNodes(void) {
3229
    tS16 node_no;
3230
    LOG_TRACE("()");
3231
 
3232
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3233
        while (node_no < gProgram_state.AI_vehicles.number_of_path_nodes && gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections == 0) {
3234
            DeleteNode(node_no, 1);
3235
        }
3236
    }
3237
}
3238
 
3239
// IDA: void __usercall InsertThisNodeInThisSectionHere(tS16 pInserted_node@<EAX>, tS16 pSection_no@<EDX>, br_vector3 *pWhere@<EBX>)
3240
void InsertThisNodeInThisSectionHere(tS16 pInserted_node, tS16 pSection_no, br_vector3* pWhere) {
3241
    tS16 new_section;
3242
    tS16 section_no_index;
3243
    tS16 node1;
3244
    //tS16 node2; // Pierre-Marie Baty -- unused variable
3245
    //tS16 node3; // Pierre-Marie Baty -- unused variable
3246
    LOG_TRACE("(%d, %d, %p)", pInserted_node, pSection_no, pWhere);
3247
 
3248
    section_no_index = gProgram_state.AI_vehicles.path_sections[pSection_no].node_indices[1];
3249
    new_section = ReallocExtraPathSections(1);
3250
    gProgram_state.AI_vehicles.path_sections[new_section].node_indices[0] = pInserted_node;
3251
    gProgram_state.AI_vehicles.path_sections[new_section].node_indices[1] = section_no_index;
3252
    gProgram_state.AI_vehicles.path_sections[new_section].min_speed[0] = 0;
3253
    gProgram_state.AI_vehicles.path_sections[new_section].max_speed[0] = 255;
20 pmbaty 3254
    gProgram_state.AI_vehicles.path_sections[new_section].min_speed[1] =
3255
        gProgram_state.AI_vehicles.path_sections[pSection_no].min_speed[1];
3256
    gProgram_state.AI_vehicles.path_sections[new_section].max_speed[1] =
3257
        gProgram_state.AI_vehicles.path_sections[pSection_no].max_speed[1];
3258
    gProgram_state.AI_vehicles.path_sections[new_section].width =
3259
        gProgram_state.AI_vehicles.path_sections[pSection_no].width;
3260
    gProgram_state.AI_vehicles.path_sections[new_section].type =
3261
        gProgram_state.AI_vehicles.path_sections[pSection_no].type;
3262
    gProgram_state.AI_vehicles.path_sections[new_section].one_way =
3263
        gProgram_state.AI_vehicles.path_sections[pSection_no].one_way;
1 pmbaty 3264
    gProgram_state.AI_vehicles.path_sections[pSection_no].node_indices[1] = pInserted_node;
3265
    gProgram_state.AI_vehicles.path_sections[pSection_no].min_speed[1] = 0;
3266
    gProgram_state.AI_vehicles.path_sections[pSection_no].max_speed[1] = 255;
3267
    BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[pInserted_node].p, pWhere);
3268
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].sections
20 pmbaty 3269
        [gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections] = pSection_no;
3270
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections =
3271
        gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections + 1;
1 pmbaty 3272
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].sections
20 pmbaty 3273
        [gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections] = new_section;
3274
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections =
3275
        gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections + 1;
1 pmbaty 3276
    for (node1 = 0; node1 < gProgram_state.AI_vehicles.path_nodes[section_no_index].number_of_sections; node1++) {
3277
        if (gProgram_state.AI_vehicles.path_nodes[section_no_index].sections[node1] == pSection_no) {
3278
            gProgram_state.AI_vehicles.path_nodes[section_no_index].sections[node1] = new_section;
3279
        }
3280
    }
3281
}
3282
 
3283
// IDA: void __cdecl TrackElasticateyPath()
3284
void TrackElasticateyPath(void) {
3285
    LOG_TRACE("()");
3286
 
3287
    if (gAlready_elasticating && gNext_elastication < gTime_stamp_for_this_munging) {
3288
        gNext_elastication = gTime_stamp_for_this_munging + 2000;
3289
        BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p, &gSelf->t.t.translate.t);
3290
        RebuildOppoPathModel();
3291
        if (gNext_write_during_elastication < gTime_stamp_for_this_munging) {
3292
            gNext_write_during_elastication = gTime_stamp_for_this_munging + 10000;
3293
            WriteOutOppoPaths();
3294
        }
3295
    }
3296
}
3297
 
3298
// IDA: void __usercall RecalcNearestPathSectionSpeed(int pMax_not_min@<EAX>, int pAdjustment@<EDX>)
3299
void RecalcNearestPathSectionSpeed(int pMax_not_min, int pAdjustment) {
3300
    tS16 section_no;
3301
    br_vector3 direction_v;
3302
    br_vector3 intersect;
3303
    br_vector3 wank;
3304
    br_scalar distance;
3305
    br_scalar dist_to_start;
3306
    br_scalar dist_to_finish;
3307
    char str[128];
3308
    int new_speed;
3309
    int nearest_end;
3310
    LOG_TRACE("(%d, %d)", pMax_not_min, pAdjustment);
3311
 
3312
    if (gOppo_paths_shown) {
3313
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
3314
        if (!gAlready_elasticating && distance > 10.f) {
3315
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any paths close enough");
3316
        } else {
3317
            BrVector3Sub(&wank, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p);
3318
            dist_to_start = BrVector3Length(&wank);
3319
            BrVector3Sub(&wank, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p);
3320
            dist_to_finish = BrVector3Length(&wank);
3321
            nearest_end = dist_to_finish < dist_to_start ? 1 : 0;
3322
            if (pMax_not_min) {
3323
                new_speed = gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end];
3324
            } else {
3325
                new_speed = gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end];
3326
            }
3327
            new_speed += 5 * pAdjustment;
3328
            if (5 * pAdjustment < 0 && new_speed > 100) {
3329
                new_speed = 100;
3330
            } else if (5 * pAdjustment > 0 && new_speed > 100) {
3331
                new_speed = 255;
3332
            }
3333
            if (new_speed < 0) {
3334
                new_speed = 0;
3335
            } else if (new_speed > 255) {
3336
                new_speed = 255;
3337
            }
3338
            if (pMax_not_min) {
3339
                gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end] = new_speed;
3340
            } else {
3341
                gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end] = new_speed;
3342
            }
3343
            if (nearest_end != 0) {
3344
                sprintf(str, "Towards section finish - Min Speed %d mph - Max speed %d mph",
3345
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end]),
3346
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end]));
3347
            } else {
3348
                sprintf(str, "Towards section start - Min Speed %d mph - Max speed %d mph",
3349
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0]),
3350
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0]));
3351
            }
3352
            ShowOppoPaths();
3353
            NewTextHeadupSlot(4, 0, 2000, -1, str);
3354
        }
3355
    }
3356
}
3357
 
3358
// IDA: void __cdecl RecalcNearestPathSectionWidth(br_scalar pAdjustment)
3359
void RecalcNearestPathSectionWidth(br_scalar pAdjustment) {
3360
    tS16 section_no;
3361
    br_vector3 direction_v;
3362
    br_vector3 intersect;
3363
    br_scalar distance;
3364
    char str[128];
3365
    LOG_TRACE("(%f)", pAdjustment);
3366
 
3367
    if (gOppo_paths_shown) {
3368
        if (!gAlready_elasticating) {
3369
            section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
3370
            if (distance > 10.f) {
3371
                NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any paths close enough");
3372
                return;
3373
            }
3374
        } else {
3375
            section_no = gMobile_section;
3376
        }
3377
        gProgram_state.AI_vehicles.path_sections[section_no].width += (int)pAdjustment * pAdjustment + pAdjustment;
3378
        if (gProgram_state.AI_vehicles.path_sections[section_no].width < .05f) {
3379
            gProgram_state.AI_vehicles.path_sections[section_no].width = .05f;
3380
        }
3381
        ShowOppoPaths();
3382
        sprintf(str, "Width %2.1f BRU", 2.f * gProgram_state.AI_vehicles.path_sections[section_no].width);
3383
        NewTextHeadupSlot(4, 0, 2000, -1, str);
3384
    }
3385
}
3386
 
3387
// IDA: void __usercall CalcNegativeXVector(br_vector3 *pNegative_x_vector@<EAX>, br_vector3 *pStart@<EDX>, br_vector3 *pFinish@<EBX>, br_scalar pLength)
3388
void CalcNegativeXVector(br_vector3* pNegative_x_vector, br_vector3* pStart, br_vector3* pFinish, br_scalar pLength) {
3389
    br_vector3 positive_y_vector;
3390
    //br_vector3 path_vector; // Pierre-Marie Baty -- unused variable
3391
    LOG_TRACE("(%p, %p, %p, %f)", pNegative_x_vector, pStart, pFinish, pLength);
3392
 
3393
    positive_y_vector.v[0] = pFinish->v[0] - pStart->v[0];
3394
    positive_y_vector.v[1] = pFinish->v[1] - pStart->v[1];
3395
    positive_y_vector.v[2] = pFinish->v[2] - pStart->v[2];
3396
    pNegative_x_vector->v[0] = 1.0 * positive_y_vector.v[2] - positive_y_vector.v[1] * 0.0;
3397
    pNegative_x_vector->v[1] = 0.0 * positive_y_vector.v[0] - positive_y_vector.v[2] * 0.0;
3398
    pNegative_x_vector->v[2] = positive_y_vector.v[1] * 0.0 - 1.0 * positive_y_vector.v[0];
3399
 
3400
    BrVector3Normalise(pNegative_x_vector, pNegative_x_vector);
3401
    BrVector3Scale(pNegative_x_vector, pNegative_x_vector, pLength);
3402
}
3403
 
3404
// IDA: void __usercall MakeVertexAndOffsetIt(br_model *pModel@<EAX>, int pVertex_num@<EDX>, br_scalar pX, br_scalar pY, br_scalar pZ, br_vector3 *pOffset)
3405
void MakeVertexAndOffsetIt(br_model* pModel, int pVertex_num, br_scalar pX, br_scalar pY, br_scalar pZ, br_vector3* pOffset) {
3406
    LOG_TRACE("(%p, %d, %f, %f, %f, %p)", pModel, pVertex_num, pX, pY, pZ, pOffset);
3407
 
3408
    BrVector3Set(&pModel->vertices[pVertex_num].p, pX, pY, pZ);
3409
    BrVector3Accumulate(&pModel->vertices[pVertex_num].p, pOffset);
3410
}
3411
 
3412
// IDA: void __usercall MakeFaceAndTextureIt(br_model *pModel@<EAX>, int pFace_num@<EDX>, int pV0@<EBX>, int pV1@<ECX>, int pV2, br_material *pMaterial)
3413
void MakeFaceAndTextureIt(br_model* pModel, int pFace_num, int pV0, int pV1, int pV2, br_material* pMaterial) {
3414
    LOG_TRACE("(%p, %d, %d, %d, %d, %p)", pModel, pFace_num, pV0, pV1, pV2, pMaterial);
3415
 
3416
    pModel->faces[pFace_num].vertices[0] = pV0;
3417
    pModel->faces[pFace_num].vertices[1] = pV1;
3418
    pModel->faces[pFace_num].vertices[2] = pV2;
3419
    pModel->faces[pFace_num].smoothing = -1;
3420
    pModel->faces[pFace_num].material = pMaterial;
3421
}
3422
 
3423
// IDA: void __usercall MakeSection(br_uint_16 pFirst_vertex@<EAX>, br_uint_16 pFirst_face@<EDX>, br_vector3 *pStart@<EBX>, br_vector3 *pFinish@<ECX>, br_scalar pWidth, br_material *pMaterial_centre_lt, br_material *pMaterial_centre_dk, br_material *pMaterial_edges_start_lt, br_material *pMaterial_edges_start_dk, br_material *pMaterial_edges_finish_lt, br_material *pMaterial_edges_finish_dk)
3424
void MakeSection(br_uint_16 pFirst_vertex, br_uint_16 pFirst_face, br_vector3* pStart, br_vector3* pFinish, br_scalar pWidth, br_material* pMaterial_centre_lt, br_material* pMaterial_centre_dk, br_material* pMaterial_edges_start_lt, br_material* pMaterial_edges_start_dk, br_material* pMaterial_edges_finish_lt, br_material* pMaterial_edges_finish_dk) {
3425
    int i;
3426
    br_vector3 offset_v;
3427
    br_vector3 centre_length_v;
3428
    br_material* the_material_start_lt;
3429
    br_material* the_material_start_dk;
3430
    br_material* the_material_finish_lt;
3431
    br_material* the_material_finish_dk;
3432
    br_scalar height;
3433
    LOG_TRACE("(%d, %d, %p, %p, %f, %p, %p, %p, %p, %p, %p)", pFirst_vertex, pFirst_face, pStart, pFinish, pWidth, pMaterial_centre_lt, pMaterial_centre_dk, pMaterial_edges_start_lt, pMaterial_edges_start_dk, pMaterial_edges_finish_lt, pMaterial_edges_finish_dk);
3434
 
3435
    CalcNegativeXVector(&offset_v, pStart, pFinish, pWidth);
3436
    for (i = 0; i < 3; i++) {
3437
        the_material_start_lt = pMaterial_edges_start_lt;
3438
        the_material_start_dk = pMaterial_edges_start_dk;
3439
        the_material_finish_lt = pMaterial_edges_finish_lt;
3440
        the_material_finish_dk = pMaterial_edges_finish_dk;
3441
        height = .15f;
3442
        if (i == 1) {
3443
            BrVector3Negate(&offset_v, &offset_v);
3444
        } else if (i == 2) {
3445
            height = .3f;
3446
            BrVector3Set(&offset_v, 0.f, 0.f, 0.f);
3447
            the_material_finish_lt = pMaterial_centre_lt;
3448
            the_material_start_lt = pMaterial_centre_lt;
3449
            the_material_finish_dk = pMaterial_centre_dk;
3450
            the_material_start_dk = pMaterial_centre_dk;
3451
        }
3452
        centre_length_v.v[0] = pStart->v[0] + (pFinish->v[0] - pStart->v[0]) / 2.f;
3453
        centre_length_v.v[1] = pStart->v[1] + (pFinish->v[1] - pStart->v[1]) / 2.f;
3454
        centre_length_v.v[2] = pStart->v[2] + (pFinish->v[2] - pStart->v[2]) / 2.f;
3455
 
3456
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 0, pStart->v[0], pStart->v[1], pStart->v[2], &offset_v);
3457
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 1, pStart->v[0], pStart->v[1] + height, pStart->v[2], &offset_v);
3458
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 2, centre_length_v.v[0], centre_length_v.v[1] + height, centre_length_v.v[2], &offset_v);
3459
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 3, centre_length_v.v[0], centre_length_v.v[1], centre_length_v.v[2], &offset_v);
3460
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 4, pFinish->v[0], pFinish->v[1] + height, pFinish->v[2], &offset_v);
3461
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 5, pFinish->v[0], pFinish->v[1], pFinish->v[2], &offset_v);
3462
 
3463
        MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 0, pFirst_vertex + 6 * i + 0, pFirst_vertex + 6 * i + 1, pFirst_vertex + 6 * i + 2, the_material_start_lt);
3464
        MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 1, pFirst_vertex + 6 * i + 0, pFirst_vertex + 6 * i + 2, pFirst_vertex + 6 * i + 3, the_material_start_dk);
3465
        MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 2, pFirst_vertex + 6 * i + 3, pFirst_vertex + 6 * i + 2, pFirst_vertex + 6 * i + 4, the_material_finish_lt);
3466
        MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 3, pFirst_vertex + 6 * i + 3, pFirst_vertex + 6 * i + 4, pFirst_vertex + 6 * i + 5, the_material_finish_dk);
3467
    }
3468
}
3469
 
3470
// IDA: void __usercall MakeCube(br_uint_16 pFirst_vertex@<EAX>, br_uint_16 pFirst_face@<EDX>, br_vector3 *pPoint@<EBX>, br_material *pMaterial_1@<ECX>, br_material *pMaterial_2, br_material *pMaterial_3)
3471
void MakeCube(br_uint_16 pFirst_vertex, br_uint_16 pFirst_face, br_vector3* pPoint, br_material* pMaterial_1, br_material* pMaterial_2, br_material* pMaterial_3) {
3472
    br_vector3 offset_v;
3473
    br_vector3 point;
3474
    LOG_TRACE("(%d, %d, %p, %p, %p, %p)", pFirst_vertex, pFirst_face, pPoint, pMaterial_1, pMaterial_2, pMaterial_3);
3475
 
3476
    BrVector3Set(&point, pPoint->v[0], pPoint->v[1] + .15f, pPoint->v[2]);
3477
 
3478
    BrVector3Set(&offset_v, .1f, .1f, .1f);
3479
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 0, point.v[0], point.v[1], point.v[2], &offset_v);
3480
    BrVector3Set(&offset_v, .1f, -.1f, .1f);
3481
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 1, point.v[0], point.v[1], point.v[2], &offset_v);
3482
    BrVector3Set(&offset_v, -.1f, -.1f, .1f);
3483
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 2, point.v[0], point.v[1], point.v[2], &offset_v);
3484
    BrVector3Set(&offset_v, -.1f, .1f, .1f);
3485
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 3, point.v[0], point.v[1], point.v[2], &offset_v);
3486
    BrVector3Set(&offset_v, .1f, .1f, -.1f);
3487
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 4, point.v[0], point.v[1], point.v[2], &offset_v);
3488
    BrVector3Set(&offset_v, .1f, -.1f, -.1f);
3489
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 5, point.v[0], point.v[1], point.v[2], &offset_v);
3490
    BrVector3Set(&offset_v, -.1f, -.1f, -.1f);
3491
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6, point.v[0], point.v[1], point.v[2], &offset_v);
3492
    BrVector3Set(&offset_v, -.1f, .1f, -.1f);
3493
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 7, point.v[0], point.v[1], point.v[2], &offset_v);
3494
 
3495
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 0, pFirst_vertex + 3, pFirst_vertex + 2, pFirst_vertex + 1, pMaterial_1);
3496
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 1, pFirst_vertex + 0, pFirst_vertex + 3, pFirst_vertex + 1, pMaterial_1);
3497
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 2, pFirst_vertex + 1, pFirst_vertex + 5, pFirst_vertex + 4, pMaterial_2);
3498
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 3, pFirst_vertex + 1, pFirst_vertex + 4, pFirst_vertex + 0, pMaterial_2);
3499
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4, pFirst_vertex + 0, pFirst_vertex + 4, pFirst_vertex + 3, pMaterial_3);
3500
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 5, pFirst_vertex + 3, pFirst_vertex + 4, pFirst_vertex + 7, pMaterial_3);
3501
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 6, pFirst_vertex + 4, pFirst_vertex + 5, pFirst_vertex + 7, pMaterial_1);
3502
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 7, pFirst_vertex + 5, pFirst_vertex + 6, pFirst_vertex + 7, pMaterial_1);
3503
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 8, pFirst_vertex + 2, pFirst_vertex + 7, pFirst_vertex + 6, pMaterial_2);
3504
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 9, pFirst_vertex + 2, pFirst_vertex + 3, pFirst_vertex + 7, pMaterial_2);
3505
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 10, pFirst_vertex + 1, pFirst_vertex + 2, pFirst_vertex + 6, pMaterial_3);
3506
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 11, pFirst_vertex + 1, pFirst_vertex + 6, pFirst_vertex + 5, pMaterial_3);
3507
}
3508
 
3509
// IDA: void __usercall CalcNumberOfFacesAndVerticesForOppoPathModel(br_uint_16 *pFace_index_ptr@<EAX>, br_uint_16 *pVertex_index_ptr@<EDX>)
3510
void CalcNumberOfFacesAndVerticesForOppoPathModel(br_uint_16* pFace_index_ptr, br_uint_16* pVertex_index_ptr) {
3511
    LOG_TRACE("(%p, %p)", pFace_index_ptr, pVertex_index_ptr);
3512
 
3513
    *pFace_index_ptr = gProgram_state.AI_vehicles.number_of_path_sections * 12 + gProgram_state.AI_vehicles.number_of_cops * 12;
3514
    *pVertex_index_ptr = gProgram_state.AI_vehicles.number_of_path_sections * 18 + gProgram_state.AI_vehicles.number_of_cops * 8;
3515
}
3516
 
3517
// IDA: void __usercall ReallocModelFacesAndVertices(br_model *pModel@<EAX>, int pNum_faces@<EDX>, int pNum_vertices@<EBX>)
3518
void ReallocModelFacesAndVertices(br_model* pModel, int pNum_faces, int pNum_vertices) {
3519
    br_vertex* new_vertices;
3520
    br_face* new_faces;
3521
    int i;
3522
    LOG_TRACE("(%p, %d, %d)", pModel, pNum_faces, pNum_vertices);
3523
 
3524
    new_vertices = BrResAllocate(pModel, pNum_vertices * sizeof(br_vertex), BR_MEMORY_VERTICES);
3525
    memset(new_vertices, 0, pNum_vertices * sizeof(br_vertex));
3526
    if (pModel->nvertices != 0) {
3527
        for (i = 0; i < ((pNum_vertices <= pModel->nvertices) ? pNum_vertices : pModel->nvertices); i++) {
3528
            CopyVertex__opponent(&new_vertices[i], &pModel->vertices[i]);
3529
        }
3530
        BrResRemove(pModel->vertices);
3531
        BrResFree(pModel->vertices);
3532
    }
3533
    pModel->vertices = new_vertices;
3534
    pModel->nvertices = pNum_vertices;
3535
 
3536
    new_faces = BrResAllocate(pModel, pNum_faces * sizeof(br_face), BR_MEMORY_FACES);
3537
    memset(new_faces, 0, pNum_faces * sizeof(br_face));
3538
    if (pModel->nfaces != 0) {
3539
        for (i = 0; i < ((pNum_faces <= pModel->nfaces) ? pNum_faces : pModel->nfaces); i++) {
3540
            CopyFace__opponent(&new_faces[i], &pModel->faces[i]);
3541
        }
3542
        BrResRemove(pModel->faces);
3543
        BrResFree(pModel->faces);
3544
    }
3545
    pModel->faces = new_faces;
3546
    pModel->nfaces = pNum_faces;
3547
}
3548
 
3549
// IDA: br_material* __usercall CreateSimpleMaterial@<EAX>(int pColour_index@<EAX>)
3550
br_material* CreateSimpleMaterial(int pColour_index) {
3551
    br_material* return_me;
3552
    LOG_TRACE("(%d)", pColour_index);
3553
 
3554
    return_me = BrMaterialAllocate(NULL);
3555
    return_me->index_base = pColour_index;
3556
    return_me->index_range = 1;
3557
    return_me->flags = BR_MATF_TWO_SIDED;
3558
    return_me->index_shade = NULL;
3559
    return_me->colour_map = NULL;
3560
    return_me->identifier = NULL;
3561
    BrMaterialAdd(return_me);
3562
    return return_me;
3563
}
3564
 
3565
// IDA: void __cdecl AllocateMatsForOppoPathModel()
3566
void AllocateMatsForOppoPathModel(void) {
3567
    LOG_TRACE("()");
3568
 
3569
    gMat_dk_yel = CreateSimpleMaterial(50);
3570
    gMat_md_yel = CreateSimpleMaterial(51);
3571
    gMat_lt_yel = CreateSimpleMaterial(52);
3572
    gMat_dk_red = CreateSimpleMaterial(3);
3573
    gMat_lt_red = CreateSimpleMaterial(4);
3574
    gMat_dk_grn = CreateSimpleMaterial(66);
3575
    gMat_lt_grn = CreateSimpleMaterial(68);
3576
    gMat_dk_blu = CreateSimpleMaterial(162);
3577
    gMat_lt_blu = CreateSimpleMaterial(164);
3578
    gMat_dk_turq = CreateSimpleMaterial(130);
3579
    gMat_lt_turq = CreateSimpleMaterial(132);
3580
    gMat_dk_gry = CreateSimpleMaterial(253);
3581
    gMat_md_gry = CreateSimpleMaterial(254);
3582
    gMat_lt_gry = CreateSimpleMaterial(255);
3583
 
3584
    gMats_allocated = 1;
3585
}
3586
 
3587
// IDA: void __cdecl RebuildOppoPathModel()
3588
void RebuildOppoPathModel(void) {
3589
    static int nvertices_last_time = 0;
3590
    static int nfaces_last_time = 0;
3591
    int i;
3592
    //int at_least_one; // Pierre-Marie Baty -- unused variable
3593
    br_uint_16 nfaces;
3594
    br_uint_16 nvertices;
3595
    //br_uint_16 first_face; // Pierre-Marie Baty -- unused variable
3596
    //br_uint_16 first_vertex; // Pierre-Marie Baty -- unused variable
3597
    br_material* centre_mat_lt;
3598
    br_material* centre_mat_dk;
3599
    br_material* edge_mat_start_lt;
3600
    br_material* edge_mat_start_dk;
3601
    br_material* edge_mat_finish_lt;
3602
    br_material* edge_mat_finish_dk;
3603
    LOG_TRACE("()");
3604
 
3605
    if (gProgram_state.AI_vehicles.number_of_path_nodes < 2) {
3606
        if (gOppo_path_model != NULL) {
3607
            BrModelRemove(gOppo_path_model);
3608
            BrModelFree(gOppo_path_model);
3609
            gOppo_path_model = NULL;
3610
        }
3611
        if (gOppo_path_actor != NULL) {
3612
            gOppo_path_actor->type = BR_ACTOR_NONE;
3613
            gOppo_path_actor->render_style = BR_RSTYLE_NONE;
3614
        }
3615
    } else {
3616
        if (!gMats_allocated) {
3617
            AllocateMatsForOppoPathModel();
3618
        }
3619
        if (gOppo_path_actor == NULL) {
3620
            gOppo_path_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
3621
            BrActorAdd(gNon_track_actor, gOppo_path_actor);
3622
        }
3623
        if (gOppo_path_model == NULL) {
3624
            gOppo_path_actor->model = BrModelAllocate("OppoPathModel", 3, 1);
3625
            gOppo_path_model = gOppo_path_actor->model;
3626
            gOppo_path_model->flags |= BR_MODF_DONT_WELD | BR_MODF_KEEP_ORIGINAL | BR_MODF_GENERATE_TAGS;
3627
            BrModelAdd(gOppo_path_model);
3628
        }
3629
        gOppo_path_actor->model = gOppo_path_model;
3630
        gOppo_path_actor->type = BR_ACTOR_MODEL;
3631
        gOppo_path_actor->render_style = BR_RSTYLE_FACES;
3632
        CalcNumberOfFacesAndVerticesForOppoPathModel(&nfaces, &nvertices);
3633
        if (nvertices_last_time < nvertices || nfaces_last_time < nfaces) {
3634
            ReallocModelFacesAndVertices(gOppo_path_model, nfaces, nvertices);
3635
            nvertices_last_time = nvertices;
3636
            nfaces_last_time = nfaces;
3637
        } else {
3638
            gOppo_path_model->nvertices = nvertices;
3639
            gOppo_path_model->nfaces = nfaces;
3640
        }
3641
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
3642
            centre_mat_lt = gMat_lt_grn;
3643
            centre_mat_dk = gMat_dk_grn;
3644
            edge_mat_start_lt = gMat_lt_grn;
3645
            edge_mat_start_dk = gMat_dk_grn;
3646
            edge_mat_finish_lt = gMat_lt_grn;
3647
            edge_mat_finish_dk = gMat_dk_grn;
3648
            if (gProgram_state.AI_vehicles.path_sections[i].type == 1) {
3649
                centre_mat_lt = gMat_lt_red;
3650
                centre_mat_dk = gMat_dk_red;
3651
            } else if (gProgram_state.AI_vehicles.path_sections[i].type == 2) {
3652
                centre_mat_lt = gMat_lt_blu;
3653
                centre_mat_dk = gMat_dk_blu;
3654
            }
3655
            if (gProgram_state.AI_vehicles.path_sections[i].one_way) {
3656
                centre_mat_lt = gMat_lt_yel;
3657
            }
20 pmbaty 3658
            if ((gProgram_state.AI_vehicles.path_sections[i].min_speed[0] != 0) ||
3659
                (gProgram_state.AI_vehicles.path_sections[i].max_speed[0] != 255)) {
1 pmbaty 3660
                edge_mat_start_lt = gMat_lt_turq;
3661
                edge_mat_start_dk = gMat_dk_turq;
3662
            }
20 pmbaty 3663
            if ((gProgram_state.AI_vehicles.path_sections[i].min_speed[1] != 0) ||
3664
                (gProgram_state.AI_vehicles.path_sections[i].max_speed[1] != 255)) {
1 pmbaty 3665
                edge_mat_finish_lt = gMat_lt_turq;
3666
                edge_mat_finish_dk = gMat_dk_turq;
3667
            }
3668
            if (gAlready_elasticating && gMobile_section == i) {
3669
                BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p,
3670
                    &gSelf->t.t.translate.t);
3671
            }
3672
            MakeSection(18 * i, 12 * i,
3673
                &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p,
3674
                &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p,
3675
                gProgram_state.AI_vehicles.path_sections[i].width,
20 pmbaty 3676
                centre_mat_lt,centre_mat_dk,
3677
                edge_mat_start_lt,edge_mat_start_dk,
3678
                edge_mat_finish_lt,edge_mat_finish_dk);
1 pmbaty 3679
        }
3680
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3681
            MakeCube(18 * gProgram_state.AI_vehicles.number_of_path_sections + 8 * i,
3682
                12 * gProgram_state.AI_vehicles.number_of_path_sections + 12 * i,
3683
                gProgram_state.AI_vehicles.cop_start_points + i,
3684
                gMat_lt_turq,
3685
                gMat_lt_turq,
3686
                gMat_dk_turq);
3687
        }
3688
        BrModelUpdate(gOppo_path_model, BR_MODU_ALL);
3689
    }
3690
}
3691
 
3692
// IDA: int __cdecl ConsistencyCheck()
3693
int ConsistencyCheck(void) {
3694
    tS16 node_no;
3695
    tS16 section_no;
3696
    tS16 start_node;
3697
    tS16 finish_node;
3698
    tS16 section_no_index;
3699
    tS16 section_no_index1;
3700
    int found_how_many;
3701
    int failed;
3702
    tU8* nodes_referenced_by_sections_array = NULL;
3703
    tU8* sections_referenced_by_nodes_array = NULL;
3704
    LOG_TRACE("()");
3705
 
3706
    failed = 0;
3707
    if (gProgram_state.AI_vehicles.number_of_path_nodes != 0) {
3708
        nodes_referenced_by_sections_array = BrMemAllocate(gProgram_state.AI_vehicles.number_of_path_nodes, kMem_nodes_array);
3709
        memset(nodes_referenced_by_sections_array, 0, gProgram_state.AI_vehicles.number_of_path_nodes);
3710
    }
3711
    if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
3712
        sections_referenced_by_nodes_array = BrMemAllocate(gProgram_state.AI_vehicles.number_of_path_sections, kMem_sections_array);
3713
        memset(sections_referenced_by_nodes_array, 0, gProgram_state.AI_vehicles.number_of_path_sections);
3714
    }
3715
    for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.number_of_path_sections; section_no_index++) {
3716
        start_node = gProgram_state.AI_vehicles.path_sections[section_no_index].node_indices[0];
3717
        finish_node = gProgram_state.AI_vehicles.path_sections[section_no_index].node_indices[1];
3718
        if (finish_node == start_node) {
3719
            dr_dprintf("CONSISTENCY FAILURE: Section #%d has both ends attached to same node!", section_no_index);
3720
            failed = 1;
3721
        }
3722
        if (start_node >= 0 && gProgram_state.AI_vehicles.number_of_path_nodes - 1 >= start_node) {
3723
            nodes_referenced_by_sections_array[start_node] = 1;
3724
            nodes_referenced_by_sections_array[finish_node] = 1;
3725
            found_how_many = 0;
3726
            for (section_no_index1 = 0; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[start_node].number_of_sections; section_no_index1++) {
3727
                if (gProgram_state.AI_vehicles.path_nodes[start_node].sections[section_no_index1] == section_no_index) {
3728
                    found_how_many++;
3729
                }
3730
            }
3731
            if (found_how_many == 0) {
3732
                dr_dprintf(
3733
                    "CONSISTENCY FAILURE: Section #%d references node #%d but not vice-versa",
3734
                    section_no_index,
3735
                    start_node);
3736
                failed = 1;
3737
            }
3738
        } else {
3739
            dr_dprintf(
3740
                "CONSISTENCY FAILURE: Section #%d references invalid node (#%d) - should be in range 0..%d",
3741
                section_no_index,
3742
                start_node,
3743
                gProgram_state.AI_vehicles.number_of_path_nodes - 1);
3744
            failed = 1;
3745
        }
3746
        if (finish_node >= 0 && gProgram_state.AI_vehicles.number_of_path_nodes - 1 >= finish_node) {
3747
            found_how_many = 0;
3748
            for (section_no_index1 = 0; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[finish_node].number_of_sections; section_no_index1++) {
3749
                if (gProgram_state.AI_vehicles.path_nodes[finish_node].sections[section_no_index1] == section_no_index) {
3750
                    found_how_many++;
3751
                }
3752
            }
3753
            if (found_how_many == 0) {
3754
                dr_dprintf(
3755
                    "CONSISTENCY FAILURE: Section #%d references node #%d but not vice-versa",
3756
                    section_no_index,
3757
                    finish_node);
3758
                failed = 1;
3759
            }
3760
        } else {
3761
            dr_dprintf(
3762
                "CONSISTENCY FAILURE: Section #%d references invalid node (#%d) - should be in range 0..%d",
3763
                section_no_index,
3764
                finish_node,
3765
                gProgram_state.AI_vehicles.number_of_path_nodes - 1);
3766
            failed = 1;
3767
        }
3768
    }
3769
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3770
        for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index++) {
3771
            section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index];
3772
            if (section_no >= 0 && gProgram_state.AI_vehicles.number_of_path_sections - 1 >= section_no) {
3773
                sections_referenced_by_nodes_array[section_no] = 1;
3774
                if (gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] != node_no
3775
                    && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no) {
3776
                    dr_dprintf(
3777
                        "CONSISTENCY FAILURE: Node #%d references section #%d but not vice-versa",
3778
                        node_no,
3779
                        section_no);
3780
                    failed = 1;
3781
                }
3782
            } else {
3783
                dr_dprintf(
3784
                    "CONSISTENCY FAILURE: Node #%d references invalid section (#%d) - should be in range 0..%d",
3785
                    node_no,
3786
                    section_no,
3787
                    gProgram_state.AI_vehicles.number_of_path_sections - 1);
3788
                failed = 1;
3789
            }
3790
            found_how_many = 0;
3791
            for (section_no_index1 = section_no; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index1++) {
3792
                if (gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index1] == section_no) {
3793
                    found_how_many++;
3794
                }
3795
            }
3796
            if (found_how_many > 1) {
3797
                dr_dprintf(
3798
                    "CONSISTENCY FAILURE: Node #%d references section #%d multiple times",
3799
                    node_no,
3800
                    section_no);
3801
                failed = 1;
3802
            }
3803
        }
3804
    }
3805
    for (section_no = 0; section_no < gProgram_state.AI_vehicles.number_of_path_sections; section_no++) {
3806
        if (!sections_referenced_by_nodes_array[section_no]) {
3807
            dr_dprintf("CONSISTENCY FAILURE: Section #%d not referenced by any nodes", section_no);
3808
            failed = 1;
3809
        }
3810
    }
3811
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3812
        if (!nodes_referenced_by_sections_array[node_no]) {
3813
            dr_dprintf("CONSISTENCY FAILURE: Node #%d not referenced by any sections", node_no);
3814
            failed = 1;
3815
        }
3816
    }
3817
    if (gProgram_state.AI_vehicles.number_of_path_nodes != 0) {
3818
        BrMemFree(nodes_referenced_by_sections_array);
3819
    }
3820
    if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
3821
        BrMemFree(sections_referenced_by_nodes_array);
3822
    }
3823
    if (failed) {
3824
        dr_dprintf(
3825
            "CONSISTENCY FAILURE INFORMATION: Allegedly %d sections and %d nodes",
3826
            gProgram_state.AI_vehicles.number_of_path_sections,
3827
            gProgram_state.AI_vehicles.number_of_path_nodes);
3828
        dr_dprintf("^^^ CONSISTENCY FAILURE ^^^");
3829
        PDEnterDebugger("OPPONENT PATH CONSISTENCY FAILURE - refer to DIAGNOST.TXT");
3830
    }
3831
    return !failed;
3832
}
3833
 
3834
// IDA: void __cdecl ShowOppoPaths()
3835
void ShowOppoPaths(void) {
3836
    char str[256];
3837
    LOG_TRACE("()");
3838
 
3839
    if (!gOppo_paths_shown) {
3840
        if (gOppo_path_actor != NULL) {
3841
            gOppo_path_actor->render_style = BR_RSTYLE_NONE;
3842
        }
3843
        NewTextHeadupSlot(4, 0, 1000, -1, "Not displaying any paths");
3844
    } else {
3845
        RebuildOppoPathModel();
3846
        sprintf(str, "Total %d nodes, %d sections",
3847
            gProgram_state.AI_vehicles.number_of_path_nodes,
3848
            gProgram_state.AI_vehicles.number_of_path_sections);
3849
        NewTextHeadupSlot(4, 0, 1000, -1, str);
3850
    }
3851
    if (ConsistencyCheck()) {
3852
        WriteOutOppoPaths();
3853
    }
3854
}
3855
 
3856
#include <errno.h>
3857
#include <string.h>
3858
 
3859
// IDA: void __cdecl WriteOutOppoPaths()
3860
void WriteOutOppoPaths(void) {
3861
    char the_path[256];
3862
    char str[13];
3863
    FILE* f;
3864
    int i;
3865
    LOG_TRACE("()");
3866
 
3867
    if (!gMade_path_filename) {
3868
        for (i = 0; 1; i++) {
3869
#ifdef DETHRACE_FIX_BUGS
3870
            sprintf(str, "OPATH%03d.TXT", i);
3871
#else
3872
            sprintf(str, "OPATH%0.3d.TXT", i);
3873
#endif
3874
            PathCat(the_path, gApplication_path, str);
3875
#ifdef DETHRACE_FIX_BUGS
3876
            // OldDRfopen refuses to open unknown .TXT files
3877
            f = fopen(the_path, "r");
3878
#else
3879
            f = DRfopen(the_path, "r+");
3880
#endif
3881
            if (f == NULL) {
3882
                break;
3883
            }
3884
            fclose(f);
3885
        }
3886
        strcpy(gOppo_path_filename, the_path);
3887
        gMade_path_filename = 1;
3888
    }
3889
#ifdef DETHRACE_FIX_BUGS
3890
    f = fopen(gOppo_path_filename, "w");
3891
#else
3892
    f = DRfopen(gOppo_path_filename, "wt");
3893
#endif
20 pmbaty 3894
    if (f == NULL) { printf("f is NULL, errno=%d, msg=\"%s\"\n", errno, strerror(errno));}
1 pmbaty 3895
    fprintf(f, "%s\n", "START OF OPPONENT PATHS");
3896
    fprintf(f, "\n%-3d                             // Number of path nodes\n",
3897
        gProgram_state.AI_vehicles.number_of_path_nodes);
3898
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
3899
        fprintf(f, "%9.3f,%9.3f,%9.3f   // Node #%d\n",
3900
            gProgram_state.AI_vehicles.path_nodes[i].p.v[0],
3901
            gProgram_state.AI_vehicles.path_nodes[i].p.v[1],
3902
            gProgram_state.AI_vehicles.path_nodes[i].p.v[2],
3903
            i);
3904
    }
3905
    fprintf(f, "\n%-3d                                           // Number of path sections\n",
3906
        gProgram_state.AI_vehicles.number_of_path_sections);
3907
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
3908
        fprintf(f, "%4d,%4d,%4d,%4d,%4d,%4d,%7.1f,%5d   // Section #%d\n",
3909
            gProgram_state.AI_vehicles.path_sections[i].node_indices[0],
3910
            gProgram_state.AI_vehicles.path_sections[i].node_indices[1],
3911
            gProgram_state.AI_vehicles.path_sections[i].min_speed[0],
3912
            gProgram_state.AI_vehicles.path_sections[i].max_speed[0],
3913
            gProgram_state.AI_vehicles.path_sections[i].min_speed[1],
3914
            gProgram_state.AI_vehicles.path_sections[i].max_speed[1],
3915
            gProgram_state.AI_vehicles.path_sections[i].width,
3916
            gProgram_state.AI_vehicles.path_sections[i].one_way ? (gProgram_state.AI_vehicles.path_sections[i].type + 1000) : gProgram_state.AI_vehicles.path_sections[i].type,
3917
            i);
3918
    }
3919
    fprintf(f, "\n%-2d                                                            // Number of cop start points\n",
3920
        gProgram_state.AI_vehicles.number_of_cops);
3921
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3922
        fprintf(f, "%9.3f,%9.3f,%9.3f,%9.3f,%9.3f,%9.3f   // Cop start point #%d\n",
3923
            gProgram_state.AI_vehicles.cop_start_points[i].v[0],
3924
            gProgram_state.AI_vehicles.cop_start_points[i].v[1],
3925
            gProgram_state.AI_vehicles.cop_start_points[i].v[2],
3926
            0.f, 0.f, 0.f, i);
3927
    }
3928
    fprintf(f, "END OF OPPONENT PATHS");
3929
    fclose(f);
3930
}
3931
 
3932
// IDA: int __cdecl NewNodeOKHere()
3933
int NewNodeOKHere(void) {
3934
    br_vector3 last_node_to_this;
3935
    LOG_TRACE("()");
3936
 
3937
    if (gAlready_elasticating) {
3938
        BrVector3Sub(&last_node_to_this,
3939
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p,
3940
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0]].p);
3941
        return BrVector3Length(&last_node_to_this) != 0.f;
3942
    }
3943
    return 1;
3944
}
3945
 
3946
// IDA: void __cdecl ShowHideOppoPaths()
3947
void ShowHideOppoPaths(void) {
3948
    LOG_TRACE("()");
3949
 
3950
    if (!gAlready_elasticating) {
3951
        gOppo_paths_shown = !gOppo_paths_shown;
3952
        ShowOppoPaths();
3953
    }
3954
}
3955
 
3956
// IDA: void __cdecl DropElasticateyNode()
3957
void DropElasticateyNode(void) {
3958
    char str[256];
3959
    tS16 old_node;
3960
    tS16 new_node;
3961
    tS16 section_no_index;
3962
    br_scalar distance;
3963
    int all_the_same_type;
3964
    int one_wayness;
3965
    //tPath_section_type_enum section_type; // Pierre-Marie Baty -- unused variable
3966
    tPath_section_type_enum original_type;
3967
    LOG_TRACE("()");
3968
 
3969
    all_the_same_type = 1;
3970
    if (!NewNodeOKHere()) {
3971
        return;
3972
    }
3973
    if (gAlready_elasticating) {
3974
        old_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
3975
        BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[old_node].p,
3976
            &gProgram_state.current_car.car_master_actor->t.t.translate.t);
3977
        original_type = gProgram_state.AI_vehicles.path_sections[gMobile_section].type;
3978
        one_wayness = gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way;
3979
        new_node = ReallocExtraPathNodes(1);
3980
        gMobile_section = ReallocExtraPathSections(1);
3981
    } else {
3982
        if (!gOppo_paths_shown) {
3983
            NewTextHeadupSlot(4, 0, 2000, -1, "You must show paths before adding to them (F5)");
3984
            return;
3985
        }
3986
        if (gProgram_state.AI_vehicles.number_of_path_nodes == 0) {
3987
            NewTextHeadupSlot(4, 0, 2000, -1, "Not implemented yet. Go away.");
3988
            return;
3989
        }
3990
        old_node = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
3991
        if (distance > 10.f) {
3992
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any nodes close enough");
3993
            return;
3994
        }
3995
        original_type = 0;
3996
        if (gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections != 0) {
3997
            for (section_no_index = 1; section_no_index < gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections; section_no_index++) {
20 pmbaty 3998
                if (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[section_no_index]].type !=
3999
                        gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].type) {
1 pmbaty 4000
                    all_the_same_type = 0;
4001
                }
4002
            }
4003
            if (all_the_same_type) {
20 pmbaty 4004
                original_type = gProgram_state.AI_vehicles.path_sections [gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].type;
1 pmbaty 4005
            }
4006
        }
4007
        gAlready_elasticating = 1;
4008
        new_node = ReallocExtraPathNodes(1);
4009
        gMobile_section = ReallocExtraPathSections(1);
4010
        one_wayness = 0;
4011
    }
4012
    gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0] = old_node;
4013
    gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = new_node;
4014
    gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0] = 0;
4015
    gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1] = 0;
4016
    gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0] = 255;
4017
    gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1] = 255;
4018
    gProgram_state.AI_vehicles.path_sections[gMobile_section].type = original_type;
4019
    gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way = one_wayness;
4020
    if (gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections == 0) {
4021
        gProgram_state.AI_vehicles.path_sections[gMobile_section].width = 1.f;
4022
    } else {
20 pmbaty 4023
        gProgram_state.AI_vehicles.path_sections[gMobile_section].width =
4024
            gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].width;
1 pmbaty 4025
    }
4026
    gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections = 0;
4027
    gProgram_state.AI_vehicles.path_nodes[new_node].sections[gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections] = gMobile_section;
4028
    gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections += 1;
4029
    gProgram_state.AI_vehicles.path_nodes[old_node].sections[gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections] = gMobile_section;
4030
    gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections += 1;
4031
    ShowOppoPaths();
4032
    sprintf(str, "New section #%d, new node #%d", gMobile_section, new_node);
4033
    NewTextHeadupSlot(4, 0, 2000, -1, str);
4034
}
4035
 
4036
// IDA: void __cdecl InsertAndElasticate()
4037
void InsertAndElasticate(void) {
4038
    tS16 inserted_node;
4039
    tS16 elasticatey_node;
4040
    tS16 section_no;
4041
    tS16 new_section;
4042
    br_vector3 direction_v;
4043
    br_vector3 intersect;
4044
    br_vector3 wank;
4045
    br_scalar distance;
4046
    int not_perp;
4047
    int one_wayness;
4048
    char str[256];
4049
    tPath_section_type_enum section_type;
4050
    LOG_TRACE("()");
4051
 
4052
    not_perp = 0;
4053
    if (NewNodeOKHere()) {
4054
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4055
        BrVector3Sub(&wank,
4056
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p,
4057
            &intersect);
4058
        if (BrVector3Length(&wank) == 0.f) {
4059
            not_perp = 1;
4060
        }
4061
        BrVector3Sub(&wank,
4062
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p,
4063
            &intersect);
4064
        if (BrVector3Length(&wank) == 0.f) {
4065
            not_perp = 1;
4066
        }
4067
        if (not_perp || distance > 10.f) {
4068
            NewTextHeadupSlot(4, 0, 2000, -1, "Get nearer to the section");
4069
        } else {
4070
            new_section = ReallocExtraPathSections(1);
4071
            if (gAlready_elasticating) {
4072
                inserted_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
4073
                section_type = gProgram_state.AI_vehicles.path_sections[gMobile_section].type;
4074
                one_wayness = gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way;
4075
                elasticatey_node = ReallocExtraPathNodes(1);
4076
                gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections = 0;
4077
                gProgram_state.AI_vehicles.path_sections[new_section].width = gProgram_state.AI_vehicles.path_sections[gMobile_section].width;
4078
            } else {
4079
                inserted_node = ReallocExtraPathNodes(2);
4080
                gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections = 0;
4081
                elasticatey_node = inserted_node + 1;
4082
                gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections = 0;
4083
                gProgram_state.AI_vehicles.path_sections[new_section].width = gProgram_state.AI_vehicles.path_sections[section_no].width;
4084
                section_type = gProgram_state.AI_vehicles.path_sections[section_no].type;
4085
                one_wayness = gProgram_state.AI_vehicles.path_sections[section_no].one_way;
4086
            }
4087
            InsertThisNodeInThisSectionHere(inserted_node, section_no, &gSelf->t.t.translate.t);
4088
            gMobile_section = new_section;
4089
            gProgram_state.AI_vehicles.path_sections[new_section].node_indices[0] = inserted_node;
4090
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = elasticatey_node;
4091
            gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0] = 0;
4092
            gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1] = 0;
4093
            gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0] = 255;
4094
            gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1] = 255;
4095
            gProgram_state.AI_vehicles.path_sections[gMobile_section].type = section_type;
4096
            gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way = one_wayness;
4097
            gProgram_state.AI_vehicles.path_nodes[inserted_node].sections[gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections] = gMobile_section;
4098
            gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections += 1;
4099
            gProgram_state.AI_vehicles.path_nodes[elasticatey_node].sections[gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections] = gMobile_section;
4100
            gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections += 1;
4101
            gAlready_elasticating = 1;
4102
            ShowOppoPaths();
4103
            sprintf(str, "New section %d, new node #%d inserted into section #%d",
4104
                gMobile_section, inserted_node, section_no);
4105
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4106
        }
4107
    }
4108
}
4109
 
4110
// IDA: void __cdecl InsertAndDontElasticate()
4111
void InsertAndDontElasticate(void) {
4112
    tS16 inserted_node;
4113
    tS16 section_no;
4114
    br_vector3 direction_v;
4115
    br_vector3 intersect;
4116
    br_vector3 wank;
4117
    br_scalar distance;
4118
    int not_perp;
4119
    char str[256];
4120
    LOG_TRACE("()");
4121
 
4122
    not_perp = 0;
4123
    if (NewNodeOKHere()) {
4124
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4125
        BrVector3Sub(&wank, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p, &intersect);
4126
        if (BrVector3Length(&wank) == 0.f) {
4127
            not_perp = 1;
4128
        }
4129
        BrVector3Sub(&wank, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p, &intersect);
4130
        if (BrVector3Length(&wank) == 0.f) {
4131
            not_perp = 1;
4132
        }
4133
        if (not_perp || distance > 10.f) {
4134
            NewTextHeadupSlot(4, 0, 2000, -1, "Get nearer to the section");
4135
        } else {
4136
            if (gAlready_elasticating) {
4137
                gAlready_elasticating = 0;
4138
                inserted_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
4139
            } else {
4140
                inserted_node = ReallocExtraPathNodes(1);
4141
                gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections = 0;
4142
            }
4143
            InsertThisNodeInThisSectionHere(inserted_node, section_no, &gSelf->t.t.translate.t);
4144
            ShowOppoPaths();
4145
            sprintf(str, "New node #%d inserted into section #%d", inserted_node, section_no);
4146
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4147
        }
4148
    }
4149
}
4150
 
4151
// IDA: void __cdecl DropDeadEndNode()
4152
void DropDeadEndNode(void) {
4153
    char str[256];
4154
    LOG_TRACE("()");
4155
 
4156
    if (NewNodeOKHere() && gAlready_elasticating) {
4157
        gAlready_elasticating = 0;
4158
        BrVector3Copy(
4159
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p,
4160
            &gSelf->t.t.translate.t);
4161
        ShowOppoPaths();
4162
        sprintf(str, "New section #%d, finish node #%d",
4163
            gMobile_section,
4164
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
4165
        NewTextHeadupSlot(4, 0, 4000, -1, str);
4166
    }
4167
}
4168
 
4169
// IDA: void __cdecl DropNodeOnNodeAndStopElasticating()
4170
void DropNodeOnNodeAndStopElasticating(void) {
4171
    int node_no;
4172
    char str[256];
4173
    br_scalar distance;
4174
    LOG_TRACE("()");
4175
 
4176
    if (gAlready_elasticating) {
4177
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4178
        if (gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0] == node_no || distance > 10.f) {
4179
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any nodes close enough");
4180
        } else if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections >= COUNT_OF(gProgram_state.AI_vehicles.path_nodes[node_no].sections)) {
4181
            sprintf(str, "Sorry, node #%d already has %d sections attached", node_no, (int)COUNT_OF(gProgram_state.AI_vehicles.path_nodes[node_no].sections));
4182
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4183
        } else {
4184
            gAlready_elasticating = 0;
4185
            gProgram_state.AI_vehicles.number_of_path_nodes -= 1;
4186
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = node_no;
4187
            gProgram_state.AI_vehicles.path_nodes[node_no].sections[gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections] = gMobile_section;
4188
            gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections += 1;
4189
            ShowOppoPaths();
4190
            sprintf(str, "New section #%d, attached to existing node #%d",
4191
                gMobile_section,
4192
                gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
4193
            NewTextHeadupSlot(4, 0, 4000, -1, str);
4194
        }
4195
    }
4196
}
4197
 
4198
// IDA: void __cdecl WidenOppoPathSection()
4199
void WidenOppoPathSection(void) {
4200
    LOG_TRACE("()");
4201
 
20 pmbaty 4202
 
4203
   if (gOppo_paths_shown) {
4204
       RecalcNearestPathSectionWidth(.05f);
4205
   }
1 pmbaty 4206
}
4207
 
4208
// IDA: void __cdecl NarrowOppoPathSection()
4209
void NarrowOppoPathSection(void) {
4210
    LOG_TRACE("()");
4211
 
4212
    if (gOppo_paths_shown) {
4213
        RecalcNearestPathSectionWidth(-.05f);
4214
    }
4215
}
4216
 
4217
// IDA: void __cdecl IncreaseSectionMinSpeed()
4218
void IncreaseSectionMinSpeed(void) {
4219
    LOG_TRACE("()");
4220
 
4221
    if (gOppo_paths_shown) {
4222
        RecalcNearestPathSectionSpeed(0, 1);
4223
    }
4224
}
4225
 
4226
// IDA: void __cdecl DecreaseSectionMinSpeed()
4227
void DecreaseSectionMinSpeed(void) {
4228
    LOG_TRACE("()");
4229
 
4230
    if (gOppo_paths_shown) {
4231
        RecalcNearestPathSectionSpeed(0, -1);
4232
    }
4233
}
4234
 
4235
// IDA: void __cdecl IncreaseSectionMaxSpeed()
4236
void IncreaseSectionMaxSpeed(void) {
4237
    LOG_TRACE("()");
4238
 
20 pmbaty 4239
   if (gOppo_paths_shown) {
4240
       RecalcNearestPathSectionSpeed(1, 1);
4241
   }
1 pmbaty 4242
}
4243
 
4244
// IDA: void __cdecl DecreaseSectionMaxSpeed()
4245
void DecreaseSectionMaxSpeed(void) {
4246
    LOG_TRACE("()");
4247
 
4248
    if (gOppo_paths_shown) {
4249
        RecalcNearestPathSectionSpeed(1, -1);
4250
    }
4251
}
4252
 
4253
// IDA: void __cdecl PullOppoPoint()
4254
void PullOppoPoint(void) {
4255
    tS16 node_no;
4256
    br_scalar distance;
4257
    LOG_TRACE("()");
4258
 
4259
    if (gOppo_paths_shown) {
4260
        if (gAlready_elasticating) {
4261
            NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're elasticating");
4262
        } else {
4263
            node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4264
            if (distance > 10.f) {
4265
                NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any paths close enough");
4266
            } else {
4267
                BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[node_no].p, &gSelf->t.t.translate.t);
4268
                ShowOppoPaths();
4269
                NewTextHeadupSlot(4, 0, 2000, -1, "Bing!");
4270
            }
4271
        }
4272
    }
4273
}
4274
 
4275
// IDA: void __cdecl ShowNodeInfo()
4276
void ShowNodeInfo(void) {
4277
    tS16 node_no;
4278
    char str[256];
4279
    br_scalar distance;
4280
    LOG_TRACE("()");
4281
 
4282
    if (!gOppo_paths_shown) {
4283
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4284
    } else if (gAlready_elasticating) {
4285
        sprintf(str, "Next point will be #%d",
4286
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
4287
        NewTextHeadupSlot(4, 0, 2000, -1, str);
4288
    } else {
4289
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4290
        if (distance > 10.f) {
4291
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any nodes close enough");
4292
        } else {
4293
            sprintf(str, "Nearest node #%d has %d attached sections",
4294
                node_no,
4295
                gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections);
4296
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4297
        }
4298
    }
4299
}
4300
 
4301
// IDA: void __cdecl ShowSectionInfo1()
4302
void ShowSectionInfo1(void) {
4303
    tS16 section_no;
4304
    char str[256];
4305
    br_scalar distance;
4306
    br_vector3 direction_v;
4307
    br_vector3 intersect;
4308
    LOG_TRACE("()");
4309
 
4310
    if (!gOppo_paths_shown) {
20 pmbaty 4311
      NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4312
    } else if (gAlready_elasticating) {
4313
        sprintf(str, "This section will be #%d attached to nodes #%d and #%d",
4314
            gMobile_section,
4315
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0],
4316
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
4317
        NewTextHeadupSlot(4, 0, 2000, -1, str);
4318
    } else {
4319
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4320
        if (distance > 10.f) {
4321
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any sections close enough");
4322
        } else {
4323
            sprintf(str, "Nearest section #%d, start node #%d, finish node #%d",
4324
                section_no,
4325
                gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0],
4326
                gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
4327
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4328
        }
4329
    }
4330
}
4331
 
4332
// IDA: void __cdecl ShowSectionInfo2()
4333
void ShowSectionInfo2(void) {
4334
    tS16 section_no;
4335
    char str[256];
4336
    br_scalar distance;
4337
    br_vector3 direction_v;
4338
    br_vector3 intersect;
4339
    LOG_TRACE("()");
4340
 
4341
    if (!gOppo_paths_shown) {
4342
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4343
    } else if (gAlready_elasticating) {
4344
        sprintf(str, "Towards start - min %d max %d, finish - min %d, max %d mph",
4345
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0]),
4346
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0]),
4347
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1]),
4348
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1]));
4349
        NewTextHeadupSlot(4, 0, 2000, -1, str);
4350
    } else {
4351
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4352
        if (distance > 10.f) {
4353
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any sections close enough");
4354
        } else {
4355
            sprintf(str, "Towards start - min %d max %d, finish - min %d, max %d mph",
4356
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0]),
4357
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0]),
4358
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1]),
4359
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1]));
4360
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4361
        }
4362
    }
4363
}
4364
 
4365
// IDA: void __cdecl DeleteOppoPathSection()
4366
void DeleteOppoPathSection(void) {
4367
    br_scalar distance;
4368
    br_vector3 intersect;
4369
    br_vector3 direction_v;
4370
    tS16 section_no;
4371
    LOG_TRACE("()");
4372
 
4373
    if (gOppo_paths_shown == 0) {
4374
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4375
    } else if (gAlready_elasticating) {
4376
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4377
    } else {
4378
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4379
        if (distance > 10.f) {
4380
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any sections close enough");
4381
        } else {
4382
            DeleteSection(section_no);
4383
            DeleteOrphanNodes();
4384
            ShowOppoPaths();
4385
            NewTextHeadupSlot(4, 0, 2000, -1, "Pop!");
4386
        }
4387
    }
4388
}
4389
 
4390
// IDA: void __cdecl DeleteOppoPathNodeAndSections()
4391
void DeleteOppoPathNodeAndSections(void) {
4392
    br_scalar distance;
4393
    tS16 node_no;
4394
    LOG_TRACE("()");
4395
 
4396
    if (!gOppo_paths_shown) {
4397
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
20 pmbaty 4398
    }
4399
    else if (gAlready_elasticating) {
1 pmbaty 4400
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4401
    } else {
4402
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4403
        if (distance > 10.f) {
4404
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any nodes close enough");
4405
        } else {
4406
            DeleteNode(node_no, 1);
4407
            DeleteOrphanNodes();
4408
            ShowOppoPaths();
4409
            NewTextHeadupSlot(4, 0, 2000, -1, "Blam!");
4410
        }
4411
    }
4412
}
4413
 
4414
// IDA: void __cdecl DeleteOppoPathNodeAndJoin()
4415
void DeleteOppoPathNodeAndJoin(void) {
4416
    br_scalar distance;
4417
    tS16 node_no;
4418
    LOG_TRACE("()");
4419
 
4420
    if (!gOppo_paths_shown) {
4421
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4422
    } else if (gAlready_elasticating) {
4423
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4424
    } else {
4425
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4426
        if (distance > 10.f) {
4427
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any nodes close enough");
4428
        } else if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections != 2) {
4429
            NewTextHeadupSlot(4, 0, 2000, -1, "Node must have exactly 2 sections attached");
4430
        } else if ((gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[0]].node_indices[0] == node_no
20 pmbaty 4431
                    && gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[1]].node_indices[1] == node_no) ||
4432
                (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[1]].node_indices[0] == node_no
4433
                    && gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[0]].node_indices[1] == node_no)) {
1 pmbaty 4434
            ConsistencyCheck();
20 pmbaty 4435
            DeleteNode(node_no,0);
1 pmbaty 4436
            ConsistencyCheck();
4437
            DeleteOrphanNodes();
4438
            ConsistencyCheck();
4439
            ShowOppoPaths();
4440
            NewTextHeadupSlot(4, 0, 2000, -1, "Blam!");
20 pmbaty 4441
        }
4442
        else {
1 pmbaty 4443
            NewTextHeadupSlot(4, 0, 2000, -1, "Sections must point in same direction");
4444
        }
4445
    }
4446
}
4447
 
4448
// IDA: void __cdecl ReverseSectionDirection()
4449
void ReverseSectionDirection(void) {
4450
    tS16 temp;
4451
    tU8 speed_temp;
4452
    br_scalar distance;
4453
    br_vector3 intersect;
4454
    br_vector3 direction_v;
4455
    tS16 section_no;
4456
    LOG_TRACE("()");
4457
 
4458
    if (!gOppo_paths_shown) {
4459
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4460
    } else if (gAlready_elasticating) {
4461
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4462
    } else {
20 pmbaty 4463
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t,  &direction_v, &intersect, &distance);
1 pmbaty 4464
        if (distance > 10.f) {
4465
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any sections close enough");
4466
        } else {
4467
            temp = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0];
20 pmbaty 4468
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] =
4469
                gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1];
1 pmbaty 4470
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] = temp;
4471
 
4472
            speed_temp = gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0];
20 pmbaty 4473
            gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0] =
4474
                gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1];
1 pmbaty 4475
            gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1] = speed_temp;
4476
 
4477
            speed_temp = gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0];
20 pmbaty 4478
            gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0] =
4479
                gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1];
1 pmbaty 4480
            gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1] = speed_temp;
4481
 
4482
            ShowOppoPaths();
4483
        }
4484
    }
4485
}
4486
 
4487
// IDA: void __cdecl CycleSectionType()
4488
void CycleSectionType(void) {
4489
    br_scalar distance;
4490
    br_vector3 intersect;
4491
    br_vector3 direction_v;
4492
    tS16 section_no;
4493
    char str[256];
4494
    LOG_TRACE("()");
4495
 
4496
    if (!gOppo_paths_shown) {
4497
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4498
    } else if (gAlready_elasticating) {
4499
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4500
    } else {
4501
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4502
        if (distance > 10.f) {
4503
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any sections close enough");
4504
        } else {
20 pmbaty 4505
            gProgram_state.AI_vehicles.path_sections[section_no].type =
4506
                (gProgram_state.AI_vehicles.path_sections[section_no].type + 1) % 3;
4507
            sprintf(str, "%s section",  gPath_section_type_names[gProgram_state.AI_vehicles.path_sections[section_no].type]);
1 pmbaty 4508
            ShowOppoPaths();
4509
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4510
        }
4511
    }
4512
}
4513
 
4514
// IDA: void __cdecl ToggleOneWayNess()
4515
void ToggleOneWayNess(void) {
4516
    br_scalar distance;
4517
    br_vector3 intersect;
4518
    br_vector3 direction_v;
4519
    tS16 section_no;
4520
    LOG_TRACE("()");
4521
 
4522
    if (!gOppo_paths_shown) {
4523
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4524
    } else if (gAlready_elasticating) {
4525
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4526
    } else {
4527
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4528
        if (distance > 10.f) {
4529
            NewTextHeadupSlot(4, 0, 2000, -1, "Can't find any sections close enough");
4530
        } else {
4531
            if (gProgram_state.AI_vehicles.path_sections[section_no].one_way) {
4532
                gProgram_state.AI_vehicles.path_sections[section_no].one_way = 0;
20 pmbaty 4533
            }
4534
            else {
1 pmbaty 4535
                gProgram_state.AI_vehicles.path_sections[section_no].one_way = 1;
4536
            }
4537
            ShowOppoPaths();
4538
            if (gProgram_state.AI_vehicles.path_sections[section_no].one_way) {
4539
                NewTextHeadupSlot(4, 0, 2000, -1, "ONE-WAY");
4540
            } else {
4541
                NewTextHeadupSlot(4, 0, 2000, -1, "TWO-WAY");
4542
            }
4543
        }
4544
    }
4545
}
4546
 
4547
// IDA: void __cdecl CopStartPointInfo()
4548
void CopStartPointInfo(void) {
4549
    char str[256];
4550
    int i;
4551
    int closest;
4552
    br_scalar closest_distance;
4553
    br_scalar distance;
4554
    br_vector3 car_to_point;
4555
    LOG_TRACE("()");
4556
 
4557
    closest = -1;
4558
    closest_distance = FLT_MAX;
4559
    if (!gOppo_paths_shown) {
20 pmbaty 4560
      NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4561
    } else {
4562
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
4563
            BrVector3Sub(&car_to_point, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
4564
            distance = BrVector3LengthSquared(&car_to_point);
4565
            if (distance < closest_distance) {
4566
                closest = i;
4567
                closest_distance = distance;
4568
            }
4569
        }
4570
        if (closest < 0 || closest_distance > 10.f) {
4571
            NewTextHeadupSlot(4, 0, 2000, -1, "No cop start points close enough");
4572
        } else {
4573
            sprintf(str, "Nearest cop start point #%d", closest);
4574
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4575
        }
4576
    }
4577
}
4578
 
4579
// IDA: void __cdecl DropCopStartPoint()
4580
void DropCopStartPoint(void) {
4581
    char str[256];
4582
    LOG_TRACE("()");
4583
 
4584
    if (!gOppo_paths_shown) {
4585
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4586
    } else if (gAlready_elasticating) {
4587
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4588
    } else {
4589
        if (gProgram_state.AI_vehicles.number_of_cops < COUNT_OF(gProgram_state.AI_vehicles.cop_start_points)) {
4590
            BrVector3Copy(&gProgram_state.AI_vehicles.cop_start_points[gProgram_state.AI_vehicles.number_of_cops], &gSelf->t.t.translate.t);
4591
            gProgram_state.AI_vehicles.number_of_cops += 1;
4592
            ShowOppoPaths();
4593
            sprintf(str, "New cop start point dropped (%d of %d)", gProgram_state.AI_vehicles.number_of_cops, (int)COUNT_OF(gProgram_state.AI_vehicles.cop_start_points));
4594
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4595
        } else {
4596
            sprintf(str, "Sorry, no more than %d cop start points", (int)COUNT_OF(gProgram_state.AI_vehicles.cop_start_points));
4597
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4598
        }
4599
    }
4600
}
4601
 
4602
// IDA: void __cdecl DeleteCopStartPoint()
4603
void DeleteCopStartPoint(void) {
4604
    char str[256];
4605
    int i;
4606
    int closest;
4607
    br_scalar closest_distance;
4608
    br_scalar distance;
4609
    br_vector3 car_to_point;
4610
    LOG_TRACE("()");
4611
 
4612
    closest = -1;
4613
    closest_distance = FLT_MAX;
4614
    if (!gOppo_paths_shown) {
4615
        NewTextHeadupSlot(4, 0, 2000, -1, "Show paths first (F5)");
4616
    } else if (gAlready_elasticating) {
4617
        NewTextHeadupSlot(4, 0, 2000, -1, "Not while you're creating a new section");
4618
    } else {
4619
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
4620
            BrVector3Sub(&car_to_point, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
4621
            distance = BrVector3Length(&car_to_point);
4622
            if (distance < closest_distance) {
4623
                closest = i;
4624
                closest_distance = distance;
4625
            }
4626
        }
4627
        if (closest < 0 || closest_distance > 10.f) {
4628
            NewTextHeadupSlot(4, 0, 2000, -1, "No cop start points close enough");
4629
        } else {
4630
            for (i = closest; i < gProgram_state.AI_vehicles.number_of_cops - 1; i++) {
4631
                BrVector3Copy(&gProgram_state.AI_vehicles.cop_start_points[i],
4632
                    &gProgram_state.AI_vehicles.cop_start_points[i + 1]);
4633
            }
4634
            gProgram_state.AI_vehicles.number_of_cops -= 1;
4635
            ShowOppoPaths();
4636
            sprintf(str, "Deleted cop start point #%d", closest);
4637
            NewTextHeadupSlot(4, 0, 2000, -1, str);
4638
        }
4639
    }
4640
}
4641
 
4642
// IDA: void __cdecl CycleCopStartPointType()
4643
void CycleCopStartPointType(void) {
4644
    LOG_TRACE("()");
20 pmbaty 4645
 
1 pmbaty 4646
}