Subversion Repositories Games.Carmageddon

Rev

Rev 20 | 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) {
21 pmbaty 1239
                    NewTextHeadupSlot(eHeadupSlot_misc, 250, 2500, -4, GetMiscString(kMiscString_CarAddedToChangeCarList));
1 pmbaty 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;
21 pmbaty 2535
        gProgram_state.AI_vehicles.cops[i].knackeredness_detected = 0;
1 pmbaty 2536
        gProgram_state.AI_vehicles.cops[i].murder_reported = 0;
2537
        gProgram_state.AI_vehicles.cops[i].finished_for_this_race = 0;
2538
        gProgram_state.AI_vehicles.cops[i].players_section_when_last_calced_full_path = -1;
2539
        gProgram_state.AI_vehicles.cops[i].nnext_sections = 0;
2540
        gProgram_state.AI_vehicles.cops[i].new_objective_required = 1;
2541
        gProgram_state.AI_vehicles.cops[i].current_objective = eOOT_none;
2542
        gProgram_state.AI_vehicles.cops[i].player_in_view_now = 0;
2543
        gProgram_state.AI_vehicles.cops[i].acknowledged_piv = 0;
2544
        gProgram_state.AI_vehicles.cops[i].nastiness = (gProgram_state.skill_level / 2.f
2545
                                                           + (99.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank)) / 98.f
2546
                                                           + 2.25f)
2547
            / 3.f * gOpponent_nastyness_frigger;
2548
        BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].start_pos, &gProgram_state.AI_vehicles.cop_start_points[i]);
2549
        BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].start_direction, &gProgram_state.AI_vehicles.cop_start_vectors[i]);
2550
        BrVector3Set(&gProgram_state.AI_vehicles.cops[i].pos_last_frame, 0.f, 0.f, 0.f);
2551
        gViewable_car_list[gNum_viewable_cars] = gProgram_state.AI_vehicles.cops[i].car_spec;
2552
        gNum_viewable_cars++;
2553
        gProgram_state.AI_vehicles.cops[i].car_spec->last_person_to_hit_us = NULL;
2554
        gProgram_state.AI_vehicles.cops[i].car_spec->last_person_we_hit = NULL;
2555
        gProgram_state.AI_vehicles.cops[i].car_spec->last_collision_time = 0;
2556
        gProgram_state.AI_vehicles.cops[i].car_spec->last_time_we_touched_a_player = 0;
2557
        gProgram_state.AI_vehicles.cops[i].car_spec->grudge_raised_recently = 1;
2558
        gOpponents[gProgram_state.AI_vehicles.cops[i].index].psyche.grudge_against_player = 10;
2559
        StunTheBugger(&gProgram_state.AI_vehicles.cops[i], 10000);
2560
    }
2561
    gActive_car_list_rebuild_required = 1;
2562
    RebuildActiveCarList();
2563
}
2564
 
2565
// IDA: void __cdecl DisposeOpponents()
2566
void DisposeOpponents(void) {
2567
    int i;
2568
    LOG_TRACE("()");
2569
 
2570
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2571
        DisposeCar(gProgram_state.AI_vehicles.cops[i].car_spec, (i == gBIG_APC_index) ? 4 : 3);
2572
        BrMemFree(gProgram_state.AI_vehicles.cops[i].car_spec);
2573
    }
2574
}
2575
 
2576
// IDA: void __usercall WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(int pWhat_the_countdown_was@<EAX>)
2577
void WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(int pWhat_the_countdown_was) {
2578
    int i;
2579
    LOG_TRACE("(%d)", pWhat_the_countdown_was);
2580
 
2581
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2582
        UnStunTheBugger(&gProgram_state.AI_vehicles.opponents[i]);
2583
        if (IRandomBetween(1000, 2500) < 1000 * pWhat_the_countdown_was) {
2584
            StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], IRandomBetween(1000, 2500));
2585
        } else {
2586
            StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], 1000 * pWhat_the_countdown_was);
2587
        }
2588
    }
2589
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2590
        UnStunTheBugger(&gProgram_state.AI_vehicles.cops[i]);
2591
        if (IRandomBetween(1000, 2500) < 1000 * pWhat_the_countdown_was) {
2592
            StunTheBugger(&gProgram_state.AI_vehicles.cops[i], IRandomBetween(1000, 2500));
2593
        } else {
2594
            StunTheBugger(&gProgram_state.AI_vehicles.cops[i], 1000 * pWhat_the_countdown_was);
2595
        }
2596
    }
2597
    gStart_jumped = 1;
2598
    gAcknowledged_start = 1;
2599
}
2600
 
2601
// IDA: void __usercall ReportMurderToPoliceDepartment(tCar_spec *pCar_spec@<EAX>)
2602
void ReportMurderToPoliceDepartment(tCar_spec* pCar_spec) {
2603
    int i;
2604
    LOG_TRACE("(%p)", pCar_spec);
2605
 
2606
    if (pCar_spec == &gProgram_state.current_car) {
2607
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
2608
            gProgram_state.AI_vehicles.cops[i].murder_reported = 1;
2609
        }
2610
    }
2611
}
2612
 
2613
// IDA: int __usercall GetCarCount@<EAX>(tVehicle_type pCategory@<EAX>)
2614
int GetCarCount(tVehicle_type pCategory) {
2615
    LOG_TRACE("(%d)", pCategory);
2616
 
2617
    switch (pCategory) {
2618
    case eVehicle_self:
2619
        return 1;
2620
 
2621
    case eVehicle_net_player:
2622
        if (gNet_mode) {
2623
            return gNumber_of_net_players - 1;
2624
        } else {
2625
            return 0;
2626
        }
2627
        break;
2628
    case eVehicle_opponent:
2629
        return gProgram_state.AI_vehicles.number_of_opponents;
2630
 
2631
    case eVehicle_rozzer:
2632
        return gNumber_of_cops_before_faffage;
2633
 
2634
    case eVehicle_drone:
2635
        return 0;
2636
 
2637
    case eVehicle_not_really:
2638
        return gNum_active_non_cars;
2639
 
2640
    default:
2641
        return 0;
2642
    }
2643
}
2644
 
2645
// IDA: tCar_spec* __usercall GetCarSpec@<EAX>(tVehicle_type pCategory@<EAX>, int pIndex@<EDX>)
2646
tCar_spec* GetCarSpec(tVehicle_type pCategory, int pIndex) {
2647
    LOG_TRACE("(%d, %d)", pCategory, pIndex);
2648
 
2649
    switch (pCategory) {
2650
 
2651
    case eVehicle_self:
2652
        return &gProgram_state.current_car;
2653
 
2654
    case eVehicle_net_player:
2655
        if (gThis_net_player_index > pIndex) {
2656
            return gNet_players[pIndex].car;
2657
        } else {
2658
            return gNet_players[pIndex + 1].car;
2659
        }
2660
 
2661
    case eVehicle_opponent:
2662
        return gProgram_state.AI_vehicles.opponents[pIndex].car_spec;
2663
 
2664
    case eVehicle_rozzer:
2665
        return gProgram_state.AI_vehicles.cops[pIndex].car_spec;
2666
 
2667
    case eVehicle_drone:
2668
        PDEnterDebugger("OPPONENT.C: GetCarSpec() can't return drone car_specs");
2669
        return 0;
2670
 
2671
    case eVehicle_not_really:
2672
        return (tCar_spec*)gActive_non_car_list[pIndex];
2673
 
2674
    default:
2675
        return 0;
2676
    }
2677
}
2678
 
2679
// IDA: char* __usercall GetDriverName@<EAX>(tVehicle_type pCategory@<EAX>, int pIndex@<EDX>)
2680
char* GetDriverName(tVehicle_type pCategory, int pIndex) {
2681
    LOG_TRACE("(%d, %d)", pCategory, pIndex);
2682
 
2683
    switch (pCategory) {
2684
    case eVehicle_self:
2685
        return gProgram_state.player_name[gProgram_state.frank_or_anniness];
2686
    case eVehicle_opponent:
2687
        return gOpponents[gProgram_state.AI_vehicles.opponents[pIndex].index].name;
2688
    case eVehicle_rozzer:
2689
        return "Faceless Cop";
2690
    case eVehicle_drone:
2691
        return "Innocent Civilian";
2692
    case eVehicle_not_really:
2693
    default:
2694
        return NULL;
2695
    }
2696
}
2697
 
2698
// IDA: tOpponent_spec* __usercall GetOpponentSpecFromCarSpec@<EAX>(tCar_spec *pCar_spec@<EAX>)
2699
tOpponent_spec* GetOpponentSpecFromCarSpec(tCar_spec* pCar_spec) {
2700
    int i;
2701
    LOG_TRACE("(%p)", pCar_spec);
2702
 
2703
    if ((pCar_spec->car_ID & 0xff00) == 0x200) {
2704
        for (i = 0; i < GetCarCount(eVehicle_opponent); i++) {
2705
            if (gProgram_state.AI_vehicles.opponents[i].car_spec == pCar_spec) {
2706
                return &gProgram_state.AI_vehicles.opponents[i];
2707
            }
2708
        }
2709
    } else if ((pCar_spec->car_ID & 0xff00) == 0x300) {
2710
        for (i = 0; i < GetCarCount(eVehicle_rozzer); i++) {
2711
            if (gProgram_state.AI_vehicles.cops[i].car_spec == pCar_spec) {
2712
                return &gProgram_state.AI_vehicles.cops[i];
2713
            }
2714
        }
2715
    }
2716
    return NULL;
2717
}
2718
 
2719
// IDA: tCar_spec* __usercall GetCarSpecFromGlobalOppoIndex@<EAX>(int pIndex@<EAX>)
2720
tCar_spec* GetCarSpecFromGlobalOppoIndex(int pIndex) {
2721
    int i;
2722
    LOG_TRACE("(%d)", pIndex);
2723
 
2724
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
2725
        if (gProgram_state.AI_vehicles.opponents[i].index == pIndex) {
2726
            return gProgram_state.AI_vehicles.opponents[i].car_spec;
2727
        }
2728
    }
2729
    return NULL;
2730
}
2731
 
2732
// IDA: int __usercall GetOpponentsRealSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pSection_no@<EDX>)
2733
int GetOpponentsRealSection(tOpponent_spec* pOpponent_spec, int pSection_no) {
2734
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection_no);
2735
 
2736
    if (pSection_no >= 20000) {
2737
        return pOpponent_spec->next_sections[pSection_no - 20000].section_no;
2738
    } else if (pSection_no >= 10000) {
2739
        return -1;
2740
    } else {
2741
        return pSection_no;
2742
    }
2743
}
2744
 
2745
// IDA: int __usercall GetOpponentsFirstSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
2746
int GetOpponentsFirstSection(tOpponent_spec* pOpponent_spec) {
2747
    LOG_TRACE("(%p)", pOpponent_spec);
2748
 
2749
    if (pOpponent_spec->current_objective != eOOT_pursue_and_twat) {
2750
        return 20000;
2751
    }
2752
    if (pOpponent_spec->pursue_car_data.state == ePCS_following_trail) {
2753
        return pOpponent_spec->follow_path_data.section_no;
2754
    }
2755
    if (pOpponent_spec->pursue_car_data.state == ePCS_following_line_of_sight) {
2756
        return 10000;
2757
    }
2758
    return 20000;
2759
}
2760
 
2761
// IDA: int __usercall GetOpponentsNextSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pCurrent_section@<EDX>)
2762
int GetOpponentsNextSection(tOpponent_spec* pOpponent_spec, tS16 pCurrent_section) {
2763
    LOG_TRACE("(%p, %d)", pOpponent_spec, pCurrent_section);
2764
 
2765
    if (pCurrent_section < 20000) {
2766
        if (pCurrent_section < 15000) {
2767
            return -1;
2768
        } else {
2769
            return CalcNextTrailSection(pOpponent_spec, pCurrent_section);
2770
        }
2771
    } 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)) {
2772
        return -1;
2773
    } else {
2774
        return pCurrent_section + 1;
2775
    }
2776
}
2777
 
2778
// IDA: tS16 __usercall GetOpponentsSectionStartNode@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2779
tS16 GetOpponentsSectionStartNode(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2780
    tS16 section_no;
2781
    int node_index_index;
2782
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2783
 
2784
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
20 pmbaty 2785
      node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction == 0;
2786
      if (pSection - 20000 > pOpponent_spec->nnext_sections) {
2787
          section_no = -1;
2788
      } else {
2789
          section_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].node_indices[node_index_index];
2790
          return section_no;
2791
      }
1 pmbaty 2792
    }
2793
    dr_dprintf("BIG ERROR - GetOpponentsSectionStartNode() - section not found in next_section array for opponent %s",
2794
        pOpponent_spec->car_spec->driver_name);
2795
    PDEnterDebugger("BIG ERROR - GetOpponentsSectionStartNode()");
2796
    return -1;
2797
}
2798
 
2799
// IDA: tS16 __usercall GetOpponentsSectionFinishNode@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2800
tS16 GetOpponentsSectionFinishNode(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2801
    //tS16 section_no; // Pierre-Marie Baty -- unused variable
2802
    //int node_index_index; // Pierre-Marie Baty -- unused variable
2803
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2804
 
20 pmbaty 2805
   if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2806
       return gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].node_indices[pOpponent_spec->next_sections[pSection - 20000].direction];
2807
   }
2808
   dr_dprintf("BIG ERROR - GetOpponentsSectionFinishNode() - section not found in next_section array for opponent %s",
2809
       pOpponent_spec->car_spec->driver_name);
2810
   PDEnterDebugger("BIG ERROR - GetOpponentsSectionFinishNode()");
2811
   return 0;
1 pmbaty 2812
}
2813
 
2814
// IDA: br_vector3* __usercall GetOpponentsSectionStartNodePoint@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2815
br_vector3* GetOpponentsSectionStartNodePoint(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2816
    tS16 section_no;
2817
    tS16 node_no;
2818
    int node_index_index;
2819
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2820
 
2821
    if (pSection >= 20000 && pOpponent_spec->nnext_sections > pSection - 20000) {
2822
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2823
        node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction;
2824
        node_no = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[node_index_index == 0];
2825
        return &gProgram_state.AI_vehicles.path_nodes[node_no].p;
2826
    }
2827
 
2828
    if (pSection >= 15000) {
2829
        return &pOpponent_spec->pursue_car_data.pursuee->my_trail.trail_nodes[pSection - 15000];
2830
    }
2831
    if (pSection == 10000) {
2832
        return &pOpponent_spec->pursue_car_data.direct_line_nodes[0].p;
2833
    }
2834
    dr_dprintf("BIG ERROR - GetOpponentsSectionStartNodePoint() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
2835
    PDEnterDebugger("BIG ERROR - GetOpponentsSectionStartNodePoint()");
2836
    return 0;
2837
}
2838
 
2839
// IDA: br_vector3* __usercall GetOpponentsSectionFinishNodePoint@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2840
br_vector3* GetOpponentsSectionFinishNodePoint(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2841
    tS16 section_no;
2842
    tS16 node_no;
2843
    int node_index_index;
2844
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2845
 
2846
    if (pSection >= 20000 && pOpponent_spec->nnext_sections > pSection - 20000) {
2847
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2848
        node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction;
2849
        node_no = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[node_index_index];
2850
        return &gProgram_state.AI_vehicles.path_nodes[node_no].p;
2851
    } else if (pSection >= 15000) {
2852
        return &pOpponent_spec->pursue_car_data.pursuee->my_trail.trail_nodes[(pSection + 1) - 15000];
2853
    } else if (pSection == 10000) {
2854
        return &pOpponent_spec->pursue_car_data.direct_line_nodes[1].p;
2855
    } else {
2856
        dr_dprintf("BIG ERROR - GetOpponentsSectionFinishNodePoint() - section not found in next_section array for opponent %s",
2857
            pOpponent_spec->car_spec->driver_name);
2858
        PDEnterDebugger("BIG ERROR - GetOpponentsSectionFinishNodePoint()");
2859
        return NULL;
2860
    }
2861
}
2862
 
2863
// IDA: br_scalar __usercall GetOpponentsSectionWidth@<ST0>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
2864
br_scalar GetOpponentsSectionWidth(tOpponent_spec* pOpponent_spec, tS16 pSection) {
2865
    LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
2866
 
2867
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2868
        return gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].width;
2869
    }
2870
    if (pSection >= 15000) {
2871
        return 0.5f;
2872
    }
2873
    if (pSection == 10000) {
2874
        return pOpponent_spec->pursue_car_data.direct_line_section.width;
2875
    }
2876
    return gProgram_state.AI_vehicles.path_sections[pSection].width;
2877
}
2878
 
2879
// IDA: int __usercall GetOpponentsSectionMinSpeed@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>, int pTowards_finish@<EBX>)
2880
int GetOpponentsSectionMinSpeed(tOpponent_spec* pOpponent_spec, tS16 pSection, int pTowards_finish) {
2881
    tS16 section_no;
2882
    int direction;
2883
    LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection, pTowards_finish);
2884
 
2885
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2886
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2887
        direction = pOpponent_spec->next_sections[pSection - 20000].direction;
2888
        return gProgram_state.AI_vehicles.path_sections[section_no].min_speed[pTowards_finish == direction];
2889
    }
2890
    if (pSection >= 15000) {
2891
        return 0;
2892
    }
2893
    if (pSection == 10000) {
2894
        return pOpponent_spec->pursue_car_data.direct_line_section.min_speed[pTowards_finish];
2895
    }
2896
    dr_dprintf("WARNING - GetOpponentsSectionMinSpeed() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
2897
    PDEnterDebugger("WARNING - GetOpponentsSectionMinSpeed()");
2898
    return 0;
2899
}
2900
 
2901
// IDA: int __usercall GetOpponentsSectionMaxSpeed@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>, int pTowards_finish@<EBX>)
2902
int GetOpponentsSectionMaxSpeed(tOpponent_spec* pOpponent_spec, tS16 pSection, int pTowards_finish) {
2903
    tS16 section_no;
2904
    int direction;
2905
    LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection, pTowards_finish);
2906
 
2907
    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
2908
        section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
2909
        direction = pOpponent_spec->next_sections[pSection - 20000].direction;
2910
        return gProgram_state.AI_vehicles.path_sections[section_no].max_speed[pTowards_finish == direction];
2911
    }
2912
    if (pSection >= 15000) {
2913
        return 255;
2914
    }
2915
    if (pSection == 10000) {
2916
        return pOpponent_spec->pursue_car_data.direct_line_section.max_speed[pTowards_finish];
2917
    }
2918
    dr_dprintf("WARNING - GetOpponentsSectionMaxSpeed() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
2919
    PDEnterDebugger("WARNING - GetOpponentsSectionMaxSpeed()");
2920
    return 255;
2921
}
2922
 
2923
// IDA: void __usercall InitOpponentPsyche(int pOpponent_index@<EAX>)
2924
void InitOpponentPsyche(int pOpponent_index) {
2925
    gOpponents[pOpponent_index].psyche.grudge_against_player = 0;
2926
}
2927
 
2928
// IDA: void __usercall ClearTwattageOccurrenceVariables(tOpponent_spec *pOpponent_spec@<EAX>)
2929
void ClearTwattageOccurrenceVariables(tOpponent_spec* pOpponent_spec) {
2930
    LOG_TRACE("(%p)", pOpponent_spec);
2931
 
2932
    pOpponent_spec->car_spec->big_bang = 0;
2933
    pOpponent_spec->car_spec->scary_bang = 0;
2934
    pOpponent_spec->car_spec->grudge_raised_recently = 0;
2935
    pOpponent_spec->car_spec->last_person_to_hit_us = NULL;
2936
    pOpponent_spec->car_spec->last_person_we_hit = NULL;
2937
}
2938
 
2939
// IDA: void __usercall TwoCarsHitEachOther(tCar_spec *pA_car@<EAX>, tCar_spec *pAnother_car@<EDX>)
2940
void TwoCarsHitEachOther(tCar_spec* pA_car, tCar_spec* pAnother_car) {
2941
    LOG_TRACE("(%p, %p)", pA_car, pAnother_car);
2942
 
2943
    if (pA_car->driver == eDriver_local_human) {
2944
        pAnother_car->last_time_we_touched_a_player = gTime_stamp_for_this_munging;
2945
    }
2946
    if (pAnother_car->driver == eDriver_local_human) {
2947
        pA_car->last_time_we_touched_a_player = gTime_stamp_for_this_munging;
2948
    }
2949
}
2950
 
2951
// IDA: void __usercall RecordOpponentTwattageOccurrence(tCar_spec *pTwatter@<EAX>, tCar_spec *pTwattee@<EDX>)
2952
void RecordOpponentTwattageOccurrence(tCar_spec* pTwatter, tCar_spec* pTwattee) {
2953
    int bangness;
2954
    int twatter_index;
2955
    int twattee_index;
2956
    int grudginess_caused_by_damage;
2957
    int new_grudge_value;
2958
    float damage;
2959
    //char str[256]; // Pierre-Marie Baty -- unused variable
2960
    tOpponent_spec* twattee_opponent_spec;
2961
    tOpponent_spec* twatter_opponent_spec;
2962
    LOG_TRACE("(%p, %p)", pTwatter, pTwattee);
2963
 
2964
    if (pTwatter->driver != eDriver_oppo && pTwattee->driver != eDriver_oppo) {
2965
        return;
2966
    }
2967
    damage = pTwattee->damage_magnitude_accumulator;
2968
    bangness = MIN(sqrtf(damage * 300000.0f), 100);
2969
    grudginess_caused_by_damage = bangness / 10 + 50 * CAR_SPEC_IS_ROZZER(pTwattee);
2970
    dr_dprintf("Frame %0.6d: %s hit %s, damage %f, bangness %d, gBig_bang %d, grudginess %d",
2971
        gAcme_frame_count,
2972
        pTwatter->driver_name,
2973
        pTwattee->driver_name,
2974
        damage,
2975
        bangness,
2976
        gBig_bang,
2977
        grudginess_caused_by_damage);
2978
    if (gMin_bangness <= bangness) {
2979
        if (gMax_bangness < bangness) {
2980
            gMax_bangness = bangness;
2981
            dr_dprintf("(New gMax_bangness - %d)", bangness);
2982
        }
2983
    } else {
2984
        gMin_bangness = bangness;
2985
        dr_dprintf("(New gMin_bangness - %d)", bangness);
2986
    }
2987
    if (bangness >= 5) {
2988
        pTwatter->last_collision_time = gTime_stamp_for_this_munging;
2989
        pTwatter->last_person_we_hit = pTwattee;
2990
        pTwattee->last_collision_time = gTime_stamp_for_this_munging;
2991
        pTwattee->last_person_to_hit_us = pTwatter;
2992
        pTwattee->grudge_raised_recently = 1;
2993
        if (bangness >= gBig_bang || CAR_SPEC_IS_ROZZER(pTwattee)) {
2994
            pTwattee->big_bang = 1;
2995
        }
2996
        if (bangness >= 80) {
2997
            pTwattee->scary_bang = 1;
2998
        }
2999
        if (pTwatter->driver == eDriver_local_human) {
3000
            twattee_opponent_spec = GetOpponentSpecFromCarSpec(pTwattee);
3001
            if (pTwattee->scary_bang) {
3002
                StunTheBugger(twattee_opponent_spec, 30 * bangness + 1000);
3003
            }
3004
            new_grudge_value = grudginess_caused_by_damage + gOpponents[twattee_opponent_spec->index].psyche.grudge_against_player;
3005
            if (new_grudge_value > 100) {
3006
                new_grudge_value = 100;
3007
            }
3008
            gOpponents[twattee_opponent_spec->index].psyche.grudge_against_player = new_grudge_value;
3009
        } else if (pTwattee->driver == eDriver_local_human) {
3010
            twatter_opponent_spec = GetOpponentSpecFromCarSpec(pTwatter);
3011
            if (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat && twatter_opponent_spec->pursue_car_data.pursuee == pTwattee) {
3012
                twatter_opponent_spec->pursue_car_data.time_last_twatted_em = gTime_stamp_for_this_munging;
3013
            }
3014
            twatter_index = twatter_opponent_spec->index;
3015
            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);
3016
            if (new_grudge_value < 0) {
3017
                new_grudge_value = 0;
3018
            }
3019
            gOpponents[twatter_index].psyche.grudge_against_player = new_grudge_value;
3020
        } else {
3021
            twatter_opponent_spec = GetOpponentSpecFromCarSpec(pTwatter);
3022
            twattee_opponent_spec = GetOpponentSpecFromCarSpec(pTwattee);
3023
            if (pTwattee->scary_bang) {
3024
                StunTheBugger(twattee_opponent_spec, 30 * bangness + 1000);
3025
            }
3026
            twattee_index = twattee_opponent_spec->index;
3027
            if (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat && twatter_opponent_spec->pursue_car_data.pursuee == pTwattee) {
3028
                twatter_opponent_spec->pursue_car_data.time_last_twatted_em = gTime_stamp_for_this_munging;
3029
            }
3030
            if (CAR_SPEC_IS_OPPONENT(pTwatter) && CAR_SPEC_IS_ROZZER(pTwattee)) {
3031
                new_grudge_value = grudginess_caused_by_damage + gOpponents[twattee_index].psyche.grudge_against_player;
3032
                if (new_grudge_value > 100) {
3033
                    new_grudge_value = 100;
3034
                }
3035
                gOpponents[twattee_index].psyche.grudge_against_player = new_grudge_value;
3036
            }
3037
        }
3038
    }
3039
}
3040
 
3041
// IDA: void __cdecl ToggleOpponentTest()
3042
void ToggleOpponentTest(void) {
3043
    LOG_TRACE("()");
3044
 
3045
    gTest_toggle = !gTest_toggle;
3046
}
3047
 
3048
// IDA: void __cdecl ToggleOpponentProcessing()
3049
void ToggleOpponentProcessing(void) {
3050
    int i;
3051
    LOG_TRACE("()");
3052
 
3053
    gProcessing_opponents = !gProcessing_opponents;
3054
    if (gProcessing_opponents) {
3055
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3056
            ObjectiveComplete(&gProgram_state.AI_vehicles.opponents[i]);
3057
        }
3058
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3059
            ObjectiveComplete(&gProgram_state.AI_vehicles.cops[i]);
3060
        }
21 pmbaty 3061
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "OPPONENTS SWITCHED ON");
1 pmbaty 3062
    } else {
3063
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3064
            gProgram_state.AI_vehicles.opponents[i].physics_me = 0;
3065
        }
3066
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3067
            gProgram_state.AI_vehicles.opponents[i].physics_me = 0;
3068
        }
3069
        gActive_car_list_rebuild_required = 1;
3070
        RebuildActiveCarList();
21 pmbaty 3071
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "OPPONENTS SWITCHED OFF");
1 pmbaty 3072
    }
3073
}
3074
 
3075
// IDA: void __cdecl ToggleMellowOpponents()
3076
void ToggleMellowOpponents(void) {
3077
    int i;
3078
    LOG_TRACE("()");
3079
 
3080
    gMellow_opponents = !gMellow_opponents;
3081
    if (gMellow_opponents) {
21 pmbaty 3082
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 3000, -1, "Opponents all nice and fluffy");
1 pmbaty 3083
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3084
            ObjectiveComplete(&gProgram_state.AI_vehicles.opponents[i]);
3085
        }
3086
    } else {
21 pmbaty 3087
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Opponents hostile again");
1 pmbaty 3088
    }
3089
}
3090
 
3091
// IDA: void __cdecl RepairOpponentsSystems()
3092
void RepairOpponentsSystems(void) {
3093
    int i;
3094
    LOG_TRACE("()");
3095
 
3096
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
3097
        if (!gProgram_state.AI_vehicles.opponents[i].pursue_from_start) {
3098
            TotallyRepairACar(gProgram_state.AI_vehicles.opponents[i].car_spec);
3099
            TurnOpponentPhysicsOff(&gProgram_state.AI_vehicles.opponents[i]);
3100
            gProgram_state.AI_vehicles.opponents[i].knackeredness_detected = 0;
3101
        }
3102
    }
21 pmbaty 3103
    NewTextHeadupSlot(eHeadupSlot_misc, 0, 3000, -1, "Opponents systems repaired (but not bodywork)");
1 pmbaty 3104
}
3105
 
3106
// IDA: void __usercall CopyVertex(br_vertex *pDest_vertex@<EAX>, br_vertex *pSrc_vertex@<EDX>)
3107
//  Suffix added to avoid duplicate symbol
3108
void CopyVertex__opponent(br_vertex* pDest_vertex, br_vertex* pSrc_vertex) {
3109
    LOG_TRACE("(%p, %p)", pDest_vertex, pSrc_vertex);
3110
 
3111
    BrVector3Copy(&pDest_vertex->p, &pSrc_vertex->p);
3112
    pDest_vertex->map.v[0] = pSrc_vertex->map.v[0];
3113
    pDest_vertex->map.v[1] = pSrc_vertex->map.v[1];
3114
    pDest_vertex->index = pSrc_vertex->index;
3115
    pDest_vertex->red = pSrc_vertex->red;
3116
    pDest_vertex->grn = pSrc_vertex->grn;
3117
    pDest_vertex->blu = pSrc_vertex->blu;
3118
}
3119
 
3120
// IDA: void __usercall CopyFace(br_face *pDest_face@<EAX>, br_face *pSrc_face@<EDX>)
3121
//  Suffix added to avoid duplicate symbol
3122
void CopyFace__opponent(br_face* pDest_face, br_face* pSrc_face) {
3123
    LOG_TRACE("(%p, %p)", pDest_face, pSrc_face);
3124
 
3125
    pDest_face->vertices[0] = pSrc_face->vertices[0];
3126
    pDest_face->vertices[1] = pSrc_face->vertices[1];
3127
    pDest_face->vertices[2] = pSrc_face->vertices[2];
3128
    pDest_face->material = pSrc_face->material;
3129
    pDest_face->smoothing = pSrc_face->smoothing;
3130
    pDest_face->flags = pSrc_face->flags;
3131
}
3132
 
3133
// IDA: void __usercall DeleteSection(tS16 pSection_to_delete@<EAX>)
3134
void DeleteSection(tS16 pSection_to_delete) {
3135
    tS16 section_no;
3136
    tS16 section_no_index;
3137
    tS16 node_no;
3138
    tS16 node_no_index;
3139
    tS16 found_it;
3140
    LOG_TRACE("(%d)", pSection_to_delete);
3141
 
3142
    for (node_no = 0; node_no < 2; node_no++) {
3143
        node_no_index = gProgram_state.AI_vehicles.path_sections[pSection_to_delete].node_indices[node_no];
3144
        if (node_no_index >= 0) {
3145
            found_it = 0;
3146
            for (section_no = 0; section_no < (gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections - 1); section_no++) {
3147
                if (gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no] == pSection_to_delete) {
3148
                    found_it = 1;
3149
                }
3150
                if (found_it) {
3151
                    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];
3152
                }
3153
            }
3154
            if (gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections != 0) {
3155
                gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections--;
3156
            }
3157
        }
3158
    }
3159
    for (section_no = pSection_to_delete; section_no < (gProgram_state.AI_vehicles.number_of_path_sections - 1); section_no++) {
3160
        gProgram_state.AI_vehicles.path_sections[section_no] = gProgram_state.AI_vehicles.path_sections[section_no + 1];
3161
    }
3162
    gProgram_state.AI_vehicles.number_of_path_sections--;
3163
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3164
        for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index++) {
3165
            if (pSection_to_delete < gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index]) {
3166
                gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index]--;
3167
            }
3168
        }
3169
    }
3170
}
3171
 
3172
// IDA: void __usercall DeleteNode(tS16 pNode_to_delete@<EAX>, int pAnd_sections@<EDX>)
3173
void DeleteNode(tS16 pNode_to_delete, int pAnd_sections) {
3174
    tS16 node_no;
3175
    tS16 section_no;
3176
    tS16 section1;
3177
    tS16 section2;
3178
    LOG_TRACE("(%d, %d)", pNode_to_delete, pAnd_sections);
3179
 
3180
    dr_dprintf("Node to be deleted #%d", pNode_to_delete);
3181
    if (pAnd_sections) {
3182
        while (gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].number_of_sections != 0) {
3183
            DeleteSection(gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0]);
3184
        }
3185
    } else {
3186
        if (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0]].node_indices[0] == pNode_to_delete) {
3187
            section1 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[1];
3188
            section2 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0];
3189
        } else {
3190
            section1 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0];
3191
            section2 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[1];
3192
        }
3193
        dr_dprintf("Section 1#%d(#%d,#%d), section 2#%d(#%d,#%d)", section1,
3194
            gProgram_state.AI_vehicles.path_sections[section1].node_indices[0],
3195
            gProgram_state.AI_vehicles.path_sections[section1].node_indices[1],
3196
            section2,
3197
            gProgram_state.AI_vehicles.path_sections[section2].node_indices[0],
3198
            gProgram_state.AI_vehicles.path_sections[section2].node_indices[1]);
3199
        gProgram_state.AI_vehicles.path_sections[section1].min_speed[1] = gProgram_state.AI_vehicles.path_sections[section2].min_speed[1];
3200
        gProgram_state.AI_vehicles.path_sections[section1].max_speed[1] = gProgram_state.AI_vehicles.path_sections[section2].max_speed[1];
3201
        node_no = gProgram_state.AI_vehicles.path_sections[section2].node_indices[1];
3202
        gProgram_state.AI_vehicles.path_sections[section1].node_indices[1] = node_no;
3203
        dr_dprintf("Section 1's new end node is #%d", node_no);
3204
        if (gProgram_state.AI_vehicles.path_nodes[node_no].sections[0] == section2) {
3205
            gProgram_state.AI_vehicles.path_nodes[node_no].sections[0] = section1;
3206
        } else {
3207
            gProgram_state.AI_vehicles.path_nodes[node_no].sections[1] = section1;
3208
        }
3209
        gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].number_of_sections = 0;
3210
        gProgram_state.AI_vehicles.path_sections[section2].node_indices[0] = -1;
3211
        gProgram_state.AI_vehicles.path_sections[section2].node_indices[1] = -1;
3212
        DeleteSection(section2);
3213
    }
3214
    for (node_no = pNode_to_delete; node_no < (gProgram_state.AI_vehicles.number_of_path_nodes - 1); node_no++) {
3215
        gProgram_state.AI_vehicles.path_nodes[node_no] = gProgram_state.AI_vehicles.path_nodes[node_no + 1];
3216
    }
3217
    for (section_no = 0; section_no < gProgram_state.AI_vehicles.number_of_path_sections; section_no++) {
3218
        if (pNode_to_delete < gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]) {
3219
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]--;
3220
        }
3221
        if (pNode_to_delete < gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]) {
3222
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]--;
3223
        }
3224
    }
3225
    gProgram_state.AI_vehicles.number_of_path_nodes--;
3226
}
3227
 
3228
// IDA: void __cdecl DeleteOrphanNodes()
3229
void DeleteOrphanNodes(void) {
3230
    tS16 node_no;
3231
    LOG_TRACE("()");
3232
 
3233
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3234
        while (node_no < gProgram_state.AI_vehicles.number_of_path_nodes && gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections == 0) {
3235
            DeleteNode(node_no, 1);
3236
        }
3237
    }
3238
}
3239
 
3240
// IDA: void __usercall InsertThisNodeInThisSectionHere(tS16 pInserted_node@<EAX>, tS16 pSection_no@<EDX>, br_vector3 *pWhere@<EBX>)
3241
void InsertThisNodeInThisSectionHere(tS16 pInserted_node, tS16 pSection_no, br_vector3* pWhere) {
3242
    tS16 new_section;
3243
    tS16 section_no_index;
3244
    tS16 node1;
3245
    //tS16 node2; // Pierre-Marie Baty -- unused variable
3246
    //tS16 node3; // Pierre-Marie Baty -- unused variable
3247
    LOG_TRACE("(%d, %d, %p)", pInserted_node, pSection_no, pWhere);
3248
 
3249
    section_no_index = gProgram_state.AI_vehicles.path_sections[pSection_no].node_indices[1];
3250
    new_section = ReallocExtraPathSections(1);
3251
    gProgram_state.AI_vehicles.path_sections[new_section].node_indices[0] = pInserted_node;
3252
    gProgram_state.AI_vehicles.path_sections[new_section].node_indices[1] = section_no_index;
3253
    gProgram_state.AI_vehicles.path_sections[new_section].min_speed[0] = 0;
3254
    gProgram_state.AI_vehicles.path_sections[new_section].max_speed[0] = 255;
20 pmbaty 3255
    gProgram_state.AI_vehicles.path_sections[new_section].min_speed[1] =
3256
        gProgram_state.AI_vehicles.path_sections[pSection_no].min_speed[1];
3257
    gProgram_state.AI_vehicles.path_sections[new_section].max_speed[1] =
3258
        gProgram_state.AI_vehicles.path_sections[pSection_no].max_speed[1];
3259
    gProgram_state.AI_vehicles.path_sections[new_section].width =
3260
        gProgram_state.AI_vehicles.path_sections[pSection_no].width;
3261
    gProgram_state.AI_vehicles.path_sections[new_section].type =
3262
        gProgram_state.AI_vehicles.path_sections[pSection_no].type;
3263
    gProgram_state.AI_vehicles.path_sections[new_section].one_way =
3264
        gProgram_state.AI_vehicles.path_sections[pSection_no].one_way;
1 pmbaty 3265
    gProgram_state.AI_vehicles.path_sections[pSection_no].node_indices[1] = pInserted_node;
3266
    gProgram_state.AI_vehicles.path_sections[pSection_no].min_speed[1] = 0;
3267
    gProgram_state.AI_vehicles.path_sections[pSection_no].max_speed[1] = 255;
3268
    BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[pInserted_node].p, pWhere);
3269
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].sections
20 pmbaty 3270
        [gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections] = pSection_no;
3271
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections =
3272
        gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections + 1;
1 pmbaty 3273
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].sections
20 pmbaty 3274
        [gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections] = new_section;
3275
    gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections =
3276
        gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections + 1;
1 pmbaty 3277
    for (node1 = 0; node1 < gProgram_state.AI_vehicles.path_nodes[section_no_index].number_of_sections; node1++) {
3278
        if (gProgram_state.AI_vehicles.path_nodes[section_no_index].sections[node1] == pSection_no) {
3279
            gProgram_state.AI_vehicles.path_nodes[section_no_index].sections[node1] = new_section;
3280
        }
3281
    }
3282
}
3283
 
3284
// IDA: void __cdecl TrackElasticateyPath()
3285
void TrackElasticateyPath(void) {
3286
    LOG_TRACE("()");
3287
 
3288
    if (gAlready_elasticating && gNext_elastication < gTime_stamp_for_this_munging) {
3289
        gNext_elastication = gTime_stamp_for_this_munging + 2000;
3290
        BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p, &gSelf->t.t.translate.t);
3291
        RebuildOppoPathModel();
3292
        if (gNext_write_during_elastication < gTime_stamp_for_this_munging) {
3293
            gNext_write_during_elastication = gTime_stamp_for_this_munging + 10000;
3294
            WriteOutOppoPaths();
3295
        }
3296
    }
3297
}
3298
 
3299
// IDA: void __usercall RecalcNearestPathSectionSpeed(int pMax_not_min@<EAX>, int pAdjustment@<EDX>)
3300
void RecalcNearestPathSectionSpeed(int pMax_not_min, int pAdjustment) {
3301
    tS16 section_no;
3302
    br_vector3 direction_v;
3303
    br_vector3 intersect;
3304
    br_vector3 wank;
3305
    br_scalar distance;
3306
    br_scalar dist_to_start;
3307
    br_scalar dist_to_finish;
3308
    char str[128];
3309
    int new_speed;
3310
    int nearest_end;
3311
    LOG_TRACE("(%d, %d)", pMax_not_min, pAdjustment);
3312
 
3313
    if (gOppo_paths_shown) {
3314
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
3315
        if (!gAlready_elasticating && distance > 10.f) {
21 pmbaty 3316
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any paths close enough");
1 pmbaty 3317
        } else {
3318
            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);
3319
            dist_to_start = BrVector3Length(&wank);
3320
            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);
3321
            dist_to_finish = BrVector3Length(&wank);
3322
            nearest_end = dist_to_finish < dist_to_start ? 1 : 0;
3323
            if (pMax_not_min) {
3324
                new_speed = gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end];
3325
            } else {
3326
                new_speed = gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end];
3327
            }
3328
            new_speed += 5 * pAdjustment;
3329
            if (5 * pAdjustment < 0 && new_speed > 100) {
3330
                new_speed = 100;
3331
            } else if (5 * pAdjustment > 0 && new_speed > 100) {
3332
                new_speed = 255;
3333
            }
3334
            if (new_speed < 0) {
3335
                new_speed = 0;
3336
            } else if (new_speed > 255) {
3337
                new_speed = 255;
3338
            }
3339
            if (pMax_not_min) {
3340
                gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end] = new_speed;
3341
            } else {
3342
                gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end] = new_speed;
3343
            }
3344
            if (nearest_end != 0) {
3345
                sprintf(str, "Towards section finish - Min Speed %d mph - Max speed %d mph",
3346
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end]),
3347
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end]));
3348
            } else {
3349
                sprintf(str, "Towards section start - Min Speed %d mph - Max speed %d mph",
3350
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0]),
3351
                    (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0]));
3352
            }
3353
            ShowOppoPaths();
21 pmbaty 3354
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 3355
        }
3356
    }
3357
}
3358
 
3359
// IDA: void __cdecl RecalcNearestPathSectionWidth(br_scalar pAdjustment)
3360
void RecalcNearestPathSectionWidth(br_scalar pAdjustment) {
3361
    tS16 section_no;
3362
    br_vector3 direction_v;
3363
    br_vector3 intersect;
3364
    br_scalar distance;
3365
    char str[128];
3366
    LOG_TRACE("(%f)", pAdjustment);
3367
 
3368
    if (gOppo_paths_shown) {
3369
        if (!gAlready_elasticating) {
3370
            section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
3371
            if (distance > 10.f) {
21 pmbaty 3372
                NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any paths close enough");
1 pmbaty 3373
                return;
3374
            }
3375
        } else {
3376
            section_no = gMobile_section;
3377
        }
3378
        gProgram_state.AI_vehicles.path_sections[section_no].width += (int)pAdjustment * pAdjustment + pAdjustment;
3379
        if (gProgram_state.AI_vehicles.path_sections[section_no].width < .05f) {
3380
            gProgram_state.AI_vehicles.path_sections[section_no].width = .05f;
3381
        }
3382
        ShowOppoPaths();
3383
        sprintf(str, "Width %2.1f BRU", 2.f * gProgram_state.AI_vehicles.path_sections[section_no].width);
21 pmbaty 3384
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 3385
    }
3386
}
3387
 
3388
// IDA: void __usercall CalcNegativeXVector(br_vector3 *pNegative_x_vector@<EAX>, br_vector3 *pStart@<EDX>, br_vector3 *pFinish@<EBX>, br_scalar pLength)
3389
void CalcNegativeXVector(br_vector3* pNegative_x_vector, br_vector3* pStart, br_vector3* pFinish, br_scalar pLength) {
3390
    br_vector3 positive_y_vector;
3391
    //br_vector3 path_vector; // Pierre-Marie Baty -- unused variable
3392
    LOG_TRACE("(%p, %p, %p, %f)", pNegative_x_vector, pStart, pFinish, pLength);
3393
 
3394
    positive_y_vector.v[0] = pFinish->v[0] - pStart->v[0];
3395
    positive_y_vector.v[1] = pFinish->v[1] - pStart->v[1];
3396
    positive_y_vector.v[2] = pFinish->v[2] - pStart->v[2];
3397
    pNegative_x_vector->v[0] = 1.0 * positive_y_vector.v[2] - positive_y_vector.v[1] * 0.0;
3398
    pNegative_x_vector->v[1] = 0.0 * positive_y_vector.v[0] - positive_y_vector.v[2] * 0.0;
3399
    pNegative_x_vector->v[2] = positive_y_vector.v[1] * 0.0 - 1.0 * positive_y_vector.v[0];
3400
 
3401
    BrVector3Normalise(pNegative_x_vector, pNegative_x_vector);
3402
    BrVector3Scale(pNegative_x_vector, pNegative_x_vector, pLength);
3403
}
3404
 
3405
// IDA: void __usercall MakeVertexAndOffsetIt(br_model *pModel@<EAX>, int pVertex_num@<EDX>, br_scalar pX, br_scalar pY, br_scalar pZ, br_vector3 *pOffset)
3406
void MakeVertexAndOffsetIt(br_model* pModel, int pVertex_num, br_scalar pX, br_scalar pY, br_scalar pZ, br_vector3* pOffset) {
3407
    LOG_TRACE("(%p, %d, %f, %f, %f, %p)", pModel, pVertex_num, pX, pY, pZ, pOffset);
3408
 
3409
    BrVector3Set(&pModel->vertices[pVertex_num].p, pX, pY, pZ);
3410
    BrVector3Accumulate(&pModel->vertices[pVertex_num].p, pOffset);
3411
}
3412
 
3413
// IDA: void __usercall MakeFaceAndTextureIt(br_model *pModel@<EAX>, int pFace_num@<EDX>, int pV0@<EBX>, int pV1@<ECX>, int pV2, br_material *pMaterial)
3414
void MakeFaceAndTextureIt(br_model* pModel, int pFace_num, int pV0, int pV1, int pV2, br_material* pMaterial) {
3415
    LOG_TRACE("(%p, %d, %d, %d, %d, %p)", pModel, pFace_num, pV0, pV1, pV2, pMaterial);
3416
 
3417
    pModel->faces[pFace_num].vertices[0] = pV0;
3418
    pModel->faces[pFace_num].vertices[1] = pV1;
3419
    pModel->faces[pFace_num].vertices[2] = pV2;
3420
    pModel->faces[pFace_num].smoothing = -1;
3421
    pModel->faces[pFace_num].material = pMaterial;
3422
}
3423
 
3424
// 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)
3425
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) {
3426
    int i;
3427
    br_vector3 offset_v;
3428
    br_vector3 centre_length_v;
3429
    br_material* the_material_start_lt;
3430
    br_material* the_material_start_dk;
3431
    br_material* the_material_finish_lt;
3432
    br_material* the_material_finish_dk;
3433
    br_scalar height;
3434
    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);
3435
 
3436
    CalcNegativeXVector(&offset_v, pStart, pFinish, pWidth);
3437
    for (i = 0; i < 3; i++) {
3438
        the_material_start_lt = pMaterial_edges_start_lt;
3439
        the_material_start_dk = pMaterial_edges_start_dk;
3440
        the_material_finish_lt = pMaterial_edges_finish_lt;
3441
        the_material_finish_dk = pMaterial_edges_finish_dk;
3442
        height = .15f;
3443
        if (i == 1) {
3444
            BrVector3Negate(&offset_v, &offset_v);
3445
        } else if (i == 2) {
3446
            height = .3f;
3447
            BrVector3Set(&offset_v, 0.f, 0.f, 0.f);
3448
            the_material_finish_lt = pMaterial_centre_lt;
3449
            the_material_start_lt = pMaterial_centre_lt;
3450
            the_material_finish_dk = pMaterial_centre_dk;
3451
            the_material_start_dk = pMaterial_centre_dk;
3452
        }
3453
        centre_length_v.v[0] = pStart->v[0] + (pFinish->v[0] - pStart->v[0]) / 2.f;
3454
        centre_length_v.v[1] = pStart->v[1] + (pFinish->v[1] - pStart->v[1]) / 2.f;
3455
        centre_length_v.v[2] = pStart->v[2] + (pFinish->v[2] - pStart->v[2]) / 2.f;
3456
 
3457
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 0, pStart->v[0], pStart->v[1], pStart->v[2], &offset_v);
3458
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 1, pStart->v[0], pStart->v[1] + height, pStart->v[2], &offset_v);
3459
        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);
3460
        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);
3461
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 4, pFinish->v[0], pFinish->v[1] + height, pFinish->v[2], &offset_v);
3462
        MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 5, pFinish->v[0], pFinish->v[1], pFinish->v[2], &offset_v);
3463
 
3464
        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);
3465
        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);
3466
        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);
3467
        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);
3468
    }
3469
}
3470
 
3471
// 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)
3472
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) {
3473
    br_vector3 offset_v;
3474
    br_vector3 point;
3475
    LOG_TRACE("(%d, %d, %p, %p, %p, %p)", pFirst_vertex, pFirst_face, pPoint, pMaterial_1, pMaterial_2, pMaterial_3);
3476
 
3477
    BrVector3Set(&point, pPoint->v[0], pPoint->v[1] + .15f, pPoint->v[2]);
3478
 
3479
    BrVector3Set(&offset_v, .1f, .1f, .1f);
3480
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 0, point.v[0], point.v[1], point.v[2], &offset_v);
3481
    BrVector3Set(&offset_v, .1f, -.1f, .1f);
3482
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 1, point.v[0], point.v[1], point.v[2], &offset_v);
3483
    BrVector3Set(&offset_v, -.1f, -.1f, .1f);
3484
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 2, point.v[0], point.v[1], point.v[2], &offset_v);
3485
    BrVector3Set(&offset_v, -.1f, .1f, .1f);
3486
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 3, point.v[0], point.v[1], point.v[2], &offset_v);
3487
    BrVector3Set(&offset_v, .1f, .1f, -.1f);
3488
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 4, point.v[0], point.v[1], point.v[2], &offset_v);
3489
    BrVector3Set(&offset_v, .1f, -.1f, -.1f);
3490
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 5, point.v[0], point.v[1], point.v[2], &offset_v);
3491
    BrVector3Set(&offset_v, -.1f, -.1f, -.1f);
3492
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6, point.v[0], point.v[1], point.v[2], &offset_v);
3493
    BrVector3Set(&offset_v, -.1f, .1f, -.1f);
3494
    MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 7, point.v[0], point.v[1], point.v[2], &offset_v);
3495
 
3496
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 0, pFirst_vertex + 3, pFirst_vertex + 2, pFirst_vertex + 1, pMaterial_1);
3497
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 1, pFirst_vertex + 0, pFirst_vertex + 3, pFirst_vertex + 1, pMaterial_1);
3498
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 2, pFirst_vertex + 1, pFirst_vertex + 5, pFirst_vertex + 4, pMaterial_2);
3499
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 3, pFirst_vertex + 1, pFirst_vertex + 4, pFirst_vertex + 0, pMaterial_2);
3500
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4, pFirst_vertex + 0, pFirst_vertex + 4, pFirst_vertex + 3, pMaterial_3);
3501
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 5, pFirst_vertex + 3, pFirst_vertex + 4, pFirst_vertex + 7, pMaterial_3);
3502
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 6, pFirst_vertex + 4, pFirst_vertex + 5, pFirst_vertex + 7, pMaterial_1);
3503
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 7, pFirst_vertex + 5, pFirst_vertex + 6, pFirst_vertex + 7, pMaterial_1);
3504
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 8, pFirst_vertex + 2, pFirst_vertex + 7, pFirst_vertex + 6, pMaterial_2);
3505
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 9, pFirst_vertex + 2, pFirst_vertex + 3, pFirst_vertex + 7, pMaterial_2);
3506
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 10, pFirst_vertex + 1, pFirst_vertex + 2, pFirst_vertex + 6, pMaterial_3);
3507
    MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 11, pFirst_vertex + 1, pFirst_vertex + 6, pFirst_vertex + 5, pMaterial_3);
3508
}
3509
 
3510
// IDA: void __usercall CalcNumberOfFacesAndVerticesForOppoPathModel(br_uint_16 *pFace_index_ptr@<EAX>, br_uint_16 *pVertex_index_ptr@<EDX>)
3511
void CalcNumberOfFacesAndVerticesForOppoPathModel(br_uint_16* pFace_index_ptr, br_uint_16* pVertex_index_ptr) {
3512
    LOG_TRACE("(%p, %p)", pFace_index_ptr, pVertex_index_ptr);
3513
 
3514
    *pFace_index_ptr = gProgram_state.AI_vehicles.number_of_path_sections * 12 + gProgram_state.AI_vehicles.number_of_cops * 12;
3515
    *pVertex_index_ptr = gProgram_state.AI_vehicles.number_of_path_sections * 18 + gProgram_state.AI_vehicles.number_of_cops * 8;
3516
}
3517
 
3518
// IDA: void __usercall ReallocModelFacesAndVertices(br_model *pModel@<EAX>, int pNum_faces@<EDX>, int pNum_vertices@<EBX>)
3519
void ReallocModelFacesAndVertices(br_model* pModel, int pNum_faces, int pNum_vertices) {
3520
    br_vertex* new_vertices;
3521
    br_face* new_faces;
3522
    int i;
3523
    LOG_TRACE("(%p, %d, %d)", pModel, pNum_faces, pNum_vertices);
3524
 
3525
    new_vertices = BrResAllocate(pModel, pNum_vertices * sizeof(br_vertex), BR_MEMORY_VERTICES);
3526
    memset(new_vertices, 0, pNum_vertices * sizeof(br_vertex));
3527
    if (pModel->nvertices != 0) {
3528
        for (i = 0; i < ((pNum_vertices <= pModel->nvertices) ? pNum_vertices : pModel->nvertices); i++) {
3529
            CopyVertex__opponent(&new_vertices[i], &pModel->vertices[i]);
3530
        }
3531
        BrResRemove(pModel->vertices);
3532
        BrResFree(pModel->vertices);
3533
    }
3534
    pModel->vertices = new_vertices;
3535
    pModel->nvertices = pNum_vertices;
3536
 
3537
    new_faces = BrResAllocate(pModel, pNum_faces * sizeof(br_face), BR_MEMORY_FACES);
3538
    memset(new_faces, 0, pNum_faces * sizeof(br_face));
3539
    if (pModel->nfaces != 0) {
3540
        for (i = 0; i < ((pNum_faces <= pModel->nfaces) ? pNum_faces : pModel->nfaces); i++) {
3541
            CopyFace__opponent(&new_faces[i], &pModel->faces[i]);
3542
        }
3543
        BrResRemove(pModel->faces);
3544
        BrResFree(pModel->faces);
3545
    }
3546
    pModel->faces = new_faces;
3547
    pModel->nfaces = pNum_faces;
3548
}
3549
 
3550
// IDA: br_material* __usercall CreateSimpleMaterial@<EAX>(int pColour_index@<EAX>)
3551
br_material* CreateSimpleMaterial(int pColour_index) {
3552
    br_material* return_me;
3553
    LOG_TRACE("(%d)", pColour_index);
3554
 
3555
    return_me = BrMaterialAllocate(NULL);
3556
    return_me->index_base = pColour_index;
3557
    return_me->index_range = 1;
3558
    return_me->flags = BR_MATF_TWO_SIDED;
3559
    return_me->index_shade = NULL;
3560
    return_me->colour_map = NULL;
3561
    return_me->identifier = NULL;
3562
    BrMaterialAdd(return_me);
3563
    return return_me;
3564
}
3565
 
3566
// IDA: void __cdecl AllocateMatsForOppoPathModel()
3567
void AllocateMatsForOppoPathModel(void) {
3568
    LOG_TRACE("()");
3569
 
3570
    gMat_dk_yel = CreateSimpleMaterial(50);
3571
    gMat_md_yel = CreateSimpleMaterial(51);
3572
    gMat_lt_yel = CreateSimpleMaterial(52);
3573
    gMat_dk_red = CreateSimpleMaterial(3);
3574
    gMat_lt_red = CreateSimpleMaterial(4);
3575
    gMat_dk_grn = CreateSimpleMaterial(66);
3576
    gMat_lt_grn = CreateSimpleMaterial(68);
3577
    gMat_dk_blu = CreateSimpleMaterial(162);
3578
    gMat_lt_blu = CreateSimpleMaterial(164);
3579
    gMat_dk_turq = CreateSimpleMaterial(130);
3580
    gMat_lt_turq = CreateSimpleMaterial(132);
3581
    gMat_dk_gry = CreateSimpleMaterial(253);
3582
    gMat_md_gry = CreateSimpleMaterial(254);
3583
    gMat_lt_gry = CreateSimpleMaterial(255);
3584
 
3585
    gMats_allocated = 1;
3586
}
3587
 
3588
// IDA: void __cdecl RebuildOppoPathModel()
3589
void RebuildOppoPathModel(void) {
3590
    static int nvertices_last_time = 0;
3591
    static int nfaces_last_time = 0;
3592
    int i;
3593
    //int at_least_one; // Pierre-Marie Baty -- unused variable
3594
    br_uint_16 nfaces;
3595
    br_uint_16 nvertices;
3596
    //br_uint_16 first_face; // Pierre-Marie Baty -- unused variable
3597
    //br_uint_16 first_vertex; // Pierre-Marie Baty -- unused variable
3598
    br_material* centre_mat_lt;
3599
    br_material* centre_mat_dk;
3600
    br_material* edge_mat_start_lt;
3601
    br_material* edge_mat_start_dk;
3602
    br_material* edge_mat_finish_lt;
3603
    br_material* edge_mat_finish_dk;
3604
    LOG_TRACE("()");
3605
 
3606
    if (gProgram_state.AI_vehicles.number_of_path_nodes < 2) {
3607
        if (gOppo_path_model != NULL) {
3608
            BrModelRemove(gOppo_path_model);
3609
            BrModelFree(gOppo_path_model);
3610
            gOppo_path_model = NULL;
3611
        }
3612
        if (gOppo_path_actor != NULL) {
3613
            gOppo_path_actor->type = BR_ACTOR_NONE;
3614
            gOppo_path_actor->render_style = BR_RSTYLE_NONE;
3615
        }
3616
    } else {
3617
        if (!gMats_allocated) {
3618
            AllocateMatsForOppoPathModel();
3619
        }
3620
        if (gOppo_path_actor == NULL) {
3621
            gOppo_path_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
3622
            BrActorAdd(gNon_track_actor, gOppo_path_actor);
3623
        }
3624
        if (gOppo_path_model == NULL) {
3625
            gOppo_path_actor->model = BrModelAllocate("OppoPathModel", 3, 1);
3626
            gOppo_path_model = gOppo_path_actor->model;
3627
            gOppo_path_model->flags |= BR_MODF_DONT_WELD | BR_MODF_KEEP_ORIGINAL | BR_MODF_GENERATE_TAGS;
3628
            BrModelAdd(gOppo_path_model);
3629
        }
3630
        gOppo_path_actor->model = gOppo_path_model;
3631
        gOppo_path_actor->type = BR_ACTOR_MODEL;
3632
        gOppo_path_actor->render_style = BR_RSTYLE_FACES;
3633
        CalcNumberOfFacesAndVerticesForOppoPathModel(&nfaces, &nvertices);
3634
        if (nvertices_last_time < nvertices || nfaces_last_time < nfaces) {
3635
            ReallocModelFacesAndVertices(gOppo_path_model, nfaces, nvertices);
3636
            nvertices_last_time = nvertices;
3637
            nfaces_last_time = nfaces;
3638
        } else {
3639
            gOppo_path_model->nvertices = nvertices;
3640
            gOppo_path_model->nfaces = nfaces;
3641
        }
3642
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
3643
            centre_mat_lt = gMat_lt_grn;
3644
            centre_mat_dk = gMat_dk_grn;
3645
            edge_mat_start_lt = gMat_lt_grn;
3646
            edge_mat_start_dk = gMat_dk_grn;
3647
            edge_mat_finish_lt = gMat_lt_grn;
3648
            edge_mat_finish_dk = gMat_dk_grn;
3649
            if (gProgram_state.AI_vehicles.path_sections[i].type == 1) {
3650
                centre_mat_lt = gMat_lt_red;
3651
                centre_mat_dk = gMat_dk_red;
3652
            } else if (gProgram_state.AI_vehicles.path_sections[i].type == 2) {
3653
                centre_mat_lt = gMat_lt_blu;
3654
                centre_mat_dk = gMat_dk_blu;
3655
            }
3656
            if (gProgram_state.AI_vehicles.path_sections[i].one_way) {
3657
                centre_mat_lt = gMat_lt_yel;
3658
            }
20 pmbaty 3659
            if ((gProgram_state.AI_vehicles.path_sections[i].min_speed[0] != 0) ||
3660
                (gProgram_state.AI_vehicles.path_sections[i].max_speed[0] != 255)) {
1 pmbaty 3661
                edge_mat_start_lt = gMat_lt_turq;
3662
                edge_mat_start_dk = gMat_dk_turq;
3663
            }
20 pmbaty 3664
            if ((gProgram_state.AI_vehicles.path_sections[i].min_speed[1] != 0) ||
3665
                (gProgram_state.AI_vehicles.path_sections[i].max_speed[1] != 255)) {
1 pmbaty 3666
                edge_mat_finish_lt = gMat_lt_turq;
3667
                edge_mat_finish_dk = gMat_dk_turq;
3668
            }
3669
            if (gAlready_elasticating && gMobile_section == i) {
3670
                BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p,
3671
                    &gSelf->t.t.translate.t);
3672
            }
3673
            MakeSection(18 * i, 12 * i,
3674
                &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p,
3675
                &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p,
3676
                gProgram_state.AI_vehicles.path_sections[i].width,
20 pmbaty 3677
                centre_mat_lt,centre_mat_dk,
3678
                edge_mat_start_lt,edge_mat_start_dk,
3679
                edge_mat_finish_lt,edge_mat_finish_dk);
1 pmbaty 3680
        }
3681
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3682
            MakeCube(18 * gProgram_state.AI_vehicles.number_of_path_sections + 8 * i,
3683
                12 * gProgram_state.AI_vehicles.number_of_path_sections + 12 * i,
3684
                gProgram_state.AI_vehicles.cop_start_points + i,
3685
                gMat_lt_turq,
3686
                gMat_lt_turq,
3687
                gMat_dk_turq);
3688
        }
3689
        BrModelUpdate(gOppo_path_model, BR_MODU_ALL);
3690
    }
3691
}
3692
 
3693
// IDA: int __cdecl ConsistencyCheck()
3694
int ConsistencyCheck(void) {
3695
    tS16 node_no;
3696
    tS16 section_no;
3697
    tS16 start_node;
3698
    tS16 finish_node;
3699
    tS16 section_no_index;
3700
    tS16 section_no_index1;
3701
    int found_how_many;
3702
    int failed;
3703
    tU8* nodes_referenced_by_sections_array = NULL;
3704
    tU8* sections_referenced_by_nodes_array = NULL;
3705
    LOG_TRACE("()");
3706
 
3707
    failed = 0;
3708
    if (gProgram_state.AI_vehicles.number_of_path_nodes != 0) {
3709
        nodes_referenced_by_sections_array = BrMemAllocate(gProgram_state.AI_vehicles.number_of_path_nodes, kMem_nodes_array);
3710
        memset(nodes_referenced_by_sections_array, 0, gProgram_state.AI_vehicles.number_of_path_nodes);
3711
    }
3712
    if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
3713
        sections_referenced_by_nodes_array = BrMemAllocate(gProgram_state.AI_vehicles.number_of_path_sections, kMem_sections_array);
3714
        memset(sections_referenced_by_nodes_array, 0, gProgram_state.AI_vehicles.number_of_path_sections);
3715
    }
3716
    for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.number_of_path_sections; section_no_index++) {
3717
        start_node = gProgram_state.AI_vehicles.path_sections[section_no_index].node_indices[0];
3718
        finish_node = gProgram_state.AI_vehicles.path_sections[section_no_index].node_indices[1];
3719
        if (finish_node == start_node) {
3720
            dr_dprintf("CONSISTENCY FAILURE: Section #%d has both ends attached to same node!", section_no_index);
3721
            failed = 1;
3722
        }
3723
        if (start_node >= 0 && gProgram_state.AI_vehicles.number_of_path_nodes - 1 >= start_node) {
3724
            nodes_referenced_by_sections_array[start_node] = 1;
3725
            nodes_referenced_by_sections_array[finish_node] = 1;
3726
            found_how_many = 0;
3727
            for (section_no_index1 = 0; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[start_node].number_of_sections; section_no_index1++) {
3728
                if (gProgram_state.AI_vehicles.path_nodes[start_node].sections[section_no_index1] == section_no_index) {
3729
                    found_how_many++;
3730
                }
3731
            }
3732
            if (found_how_many == 0) {
3733
                dr_dprintf(
3734
                    "CONSISTENCY FAILURE: Section #%d references node #%d but not vice-versa",
3735
                    section_no_index,
3736
                    start_node);
3737
                failed = 1;
3738
            }
3739
        } else {
3740
            dr_dprintf(
3741
                "CONSISTENCY FAILURE: Section #%d references invalid node (#%d) - should be in range 0..%d",
3742
                section_no_index,
3743
                start_node,
3744
                gProgram_state.AI_vehicles.number_of_path_nodes - 1);
3745
            failed = 1;
3746
        }
3747
        if (finish_node >= 0 && gProgram_state.AI_vehicles.number_of_path_nodes - 1 >= finish_node) {
3748
            found_how_many = 0;
3749
            for (section_no_index1 = 0; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[finish_node].number_of_sections; section_no_index1++) {
3750
                if (gProgram_state.AI_vehicles.path_nodes[finish_node].sections[section_no_index1] == section_no_index) {
3751
                    found_how_many++;
3752
                }
3753
            }
3754
            if (found_how_many == 0) {
3755
                dr_dprintf(
3756
                    "CONSISTENCY FAILURE: Section #%d references node #%d but not vice-versa",
3757
                    section_no_index,
3758
                    finish_node);
3759
                failed = 1;
3760
            }
3761
        } else {
3762
            dr_dprintf(
3763
                "CONSISTENCY FAILURE: Section #%d references invalid node (#%d) - should be in range 0..%d",
3764
                section_no_index,
3765
                finish_node,
3766
                gProgram_state.AI_vehicles.number_of_path_nodes - 1);
3767
            failed = 1;
3768
        }
3769
    }
3770
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3771
        for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index++) {
3772
            section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index];
3773
            if (section_no >= 0 && gProgram_state.AI_vehicles.number_of_path_sections - 1 >= section_no) {
3774
                sections_referenced_by_nodes_array[section_no] = 1;
3775
                if (gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] != node_no
3776
                    && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no) {
3777
                    dr_dprintf(
3778
                        "CONSISTENCY FAILURE: Node #%d references section #%d but not vice-versa",
3779
                        node_no,
3780
                        section_no);
3781
                    failed = 1;
3782
                }
3783
            } else {
3784
                dr_dprintf(
3785
                    "CONSISTENCY FAILURE: Node #%d references invalid section (#%d) - should be in range 0..%d",
3786
                    node_no,
3787
                    section_no,
3788
                    gProgram_state.AI_vehicles.number_of_path_sections - 1);
3789
                failed = 1;
3790
            }
3791
            found_how_many = 0;
3792
            for (section_no_index1 = section_no; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index1++) {
3793
                if (gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index1] == section_no) {
3794
                    found_how_many++;
3795
                }
3796
            }
3797
            if (found_how_many > 1) {
3798
                dr_dprintf(
3799
                    "CONSISTENCY FAILURE: Node #%d references section #%d multiple times",
3800
                    node_no,
3801
                    section_no);
3802
                failed = 1;
3803
            }
3804
        }
3805
    }
3806
    for (section_no = 0; section_no < gProgram_state.AI_vehicles.number_of_path_sections; section_no++) {
3807
        if (!sections_referenced_by_nodes_array[section_no]) {
3808
            dr_dprintf("CONSISTENCY FAILURE: Section #%d not referenced by any nodes", section_no);
3809
            failed = 1;
3810
        }
3811
    }
3812
    for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
3813
        if (!nodes_referenced_by_sections_array[node_no]) {
3814
            dr_dprintf("CONSISTENCY FAILURE: Node #%d not referenced by any sections", node_no);
3815
            failed = 1;
3816
        }
3817
    }
3818
    if (gProgram_state.AI_vehicles.number_of_path_nodes != 0) {
3819
        BrMemFree(nodes_referenced_by_sections_array);
3820
    }
3821
    if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
3822
        BrMemFree(sections_referenced_by_nodes_array);
3823
    }
3824
    if (failed) {
3825
        dr_dprintf(
3826
            "CONSISTENCY FAILURE INFORMATION: Allegedly %d sections and %d nodes",
3827
            gProgram_state.AI_vehicles.number_of_path_sections,
3828
            gProgram_state.AI_vehicles.number_of_path_nodes);
3829
        dr_dprintf("^^^ CONSISTENCY FAILURE ^^^");
3830
        PDEnterDebugger("OPPONENT PATH CONSISTENCY FAILURE - refer to DIAGNOST.TXT");
3831
    }
3832
    return !failed;
3833
}
3834
 
3835
// IDA: void __cdecl ShowOppoPaths()
3836
void ShowOppoPaths(void) {
3837
    char str[256];
3838
    LOG_TRACE("()");
3839
 
3840
    if (!gOppo_paths_shown) {
3841
        if (gOppo_path_actor != NULL) {
3842
            gOppo_path_actor->render_style = BR_RSTYLE_NONE;
3843
        }
21 pmbaty 3844
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -1, "Not displaying any paths");
1 pmbaty 3845
    } else {
3846
        RebuildOppoPathModel();
3847
        sprintf(str, "Total %d nodes, %d sections",
3848
            gProgram_state.AI_vehicles.number_of_path_nodes,
3849
            gProgram_state.AI_vehicles.number_of_path_sections);
21 pmbaty 3850
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -1, str);
1 pmbaty 3851
    }
3852
    if (ConsistencyCheck()) {
3853
        WriteOutOppoPaths();
3854
    }
3855
}
3856
 
3857
#include <errno.h>
3858
#include <string.h>
3859
 
3860
// IDA: void __cdecl WriteOutOppoPaths()
3861
void WriteOutOppoPaths(void) {
3862
    char the_path[256];
3863
    char str[13];
3864
    FILE* f;
3865
    int i;
3866
    LOG_TRACE("()");
3867
 
3868
    if (!gMade_path_filename) {
3869
        for (i = 0; 1; i++) {
3870
#ifdef DETHRACE_FIX_BUGS
3871
            sprintf(str, "OPATH%03d.TXT", i);
3872
#else
3873
            sprintf(str, "OPATH%0.3d.TXT", i);
3874
#endif
3875
            PathCat(the_path, gApplication_path, str);
3876
#ifdef DETHRACE_FIX_BUGS
3877
            // OldDRfopen refuses to open unknown .TXT files
3878
            f = fopen(the_path, "r");
3879
#else
3880
            f = DRfopen(the_path, "r+");
3881
#endif
3882
            if (f == NULL) {
3883
                break;
3884
            }
3885
            fclose(f);
3886
        }
3887
        strcpy(gOppo_path_filename, the_path);
3888
        gMade_path_filename = 1;
3889
    }
3890
#ifdef DETHRACE_FIX_BUGS
3891
    f = fopen(gOppo_path_filename, "w");
3892
#else
3893
    f = DRfopen(gOppo_path_filename, "wt");
3894
#endif
20 pmbaty 3895
    if (f == NULL) { printf("f is NULL, errno=%d, msg=\"%s\"\n", errno, strerror(errno));}
1 pmbaty 3896
    fprintf(f, "%s\n", "START OF OPPONENT PATHS");
3897
    fprintf(f, "\n%-3d                             // Number of path nodes\n",
3898
        gProgram_state.AI_vehicles.number_of_path_nodes);
3899
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
3900
        fprintf(f, "%9.3f,%9.3f,%9.3f   // Node #%d\n",
3901
            gProgram_state.AI_vehicles.path_nodes[i].p.v[0],
3902
            gProgram_state.AI_vehicles.path_nodes[i].p.v[1],
3903
            gProgram_state.AI_vehicles.path_nodes[i].p.v[2],
3904
            i);
3905
    }
3906
    fprintf(f, "\n%-3d                                           // Number of path sections\n",
3907
        gProgram_state.AI_vehicles.number_of_path_sections);
3908
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
3909
        fprintf(f, "%4d,%4d,%4d,%4d,%4d,%4d,%7.1f,%5d   // Section #%d\n",
3910
            gProgram_state.AI_vehicles.path_sections[i].node_indices[0],
3911
            gProgram_state.AI_vehicles.path_sections[i].node_indices[1],
3912
            gProgram_state.AI_vehicles.path_sections[i].min_speed[0],
3913
            gProgram_state.AI_vehicles.path_sections[i].max_speed[0],
3914
            gProgram_state.AI_vehicles.path_sections[i].min_speed[1],
3915
            gProgram_state.AI_vehicles.path_sections[i].max_speed[1],
3916
            gProgram_state.AI_vehicles.path_sections[i].width,
3917
            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,
3918
            i);
3919
    }
3920
    fprintf(f, "\n%-2d                                                            // Number of cop start points\n",
3921
        gProgram_state.AI_vehicles.number_of_cops);
3922
    for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
3923
        fprintf(f, "%9.3f,%9.3f,%9.3f,%9.3f,%9.3f,%9.3f   // Cop start point #%d\n",
3924
            gProgram_state.AI_vehicles.cop_start_points[i].v[0],
3925
            gProgram_state.AI_vehicles.cop_start_points[i].v[1],
3926
            gProgram_state.AI_vehicles.cop_start_points[i].v[2],
3927
            0.f, 0.f, 0.f, i);
3928
    }
3929
    fprintf(f, "END OF OPPONENT PATHS");
3930
    fclose(f);
3931
}
3932
 
3933
// IDA: int __cdecl NewNodeOKHere()
3934
int NewNodeOKHere(void) {
3935
    br_vector3 last_node_to_this;
3936
    LOG_TRACE("()");
3937
 
3938
    if (gAlready_elasticating) {
3939
        BrVector3Sub(&last_node_to_this,
3940
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p,
3941
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0]].p);
3942
        return BrVector3Length(&last_node_to_this) != 0.f;
3943
    }
3944
    return 1;
3945
}
3946
 
3947
// IDA: void __cdecl ShowHideOppoPaths()
3948
void ShowHideOppoPaths(void) {
3949
    LOG_TRACE("()");
3950
 
3951
    if (!gAlready_elasticating) {
3952
        gOppo_paths_shown = !gOppo_paths_shown;
3953
        ShowOppoPaths();
3954
    }
3955
}
3956
 
3957
// IDA: void __cdecl DropElasticateyNode()
3958
void DropElasticateyNode(void) {
3959
    char str[256];
3960
    tS16 old_node;
3961
    tS16 new_node;
3962
    tS16 section_no_index;
3963
    br_scalar distance;
3964
    int all_the_same_type;
3965
    int one_wayness;
3966
    //tPath_section_type_enum section_type; // Pierre-Marie Baty -- unused variable
3967
    tPath_section_type_enum original_type;
3968
    LOG_TRACE("()");
3969
 
3970
    all_the_same_type = 1;
3971
    if (!NewNodeOKHere()) {
3972
        return;
3973
    }
3974
    if (gAlready_elasticating) {
3975
        old_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
3976
        BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[old_node].p,
3977
            &gProgram_state.current_car.car_master_actor->t.t.translate.t);
3978
        original_type = gProgram_state.AI_vehicles.path_sections[gMobile_section].type;
3979
        one_wayness = gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way;
3980
        new_node = ReallocExtraPathNodes(1);
3981
        gMobile_section = ReallocExtraPathSections(1);
3982
    } else {
3983
        if (!gOppo_paths_shown) {
21 pmbaty 3984
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "You must show paths before adding to them (F5)");
1 pmbaty 3985
            return;
3986
        }
3987
        if (gProgram_state.AI_vehicles.number_of_path_nodes == 0) {
21 pmbaty 3988
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not implemented yet. Go away.");
1 pmbaty 3989
            return;
3990
        }
3991
        old_node = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
3992
        if (distance > 10.f) {
21 pmbaty 3993
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
1 pmbaty 3994
            return;
3995
        }
3996
        original_type = 0;
3997
        if (gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections != 0) {
3998
            for (section_no_index = 1; section_no_index < gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections; section_no_index++) {
20 pmbaty 3999
                if (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[section_no_index]].type !=
4000
                        gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].type) {
1 pmbaty 4001
                    all_the_same_type = 0;
4002
                }
4003
            }
4004
            if (all_the_same_type) {
20 pmbaty 4005
                original_type = gProgram_state.AI_vehicles.path_sections [gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].type;
1 pmbaty 4006
            }
4007
        }
4008
        gAlready_elasticating = 1;
4009
        new_node = ReallocExtraPathNodes(1);
4010
        gMobile_section = ReallocExtraPathSections(1);
4011
        one_wayness = 0;
4012
    }
4013
    gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0] = old_node;
4014
    gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = new_node;
4015
    gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0] = 0;
4016
    gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1] = 0;
4017
    gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0] = 255;
4018
    gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1] = 255;
4019
    gProgram_state.AI_vehicles.path_sections[gMobile_section].type = original_type;
4020
    gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way = one_wayness;
4021
    if (gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections == 0) {
4022
        gProgram_state.AI_vehicles.path_sections[gMobile_section].width = 1.f;
4023
    } else {
20 pmbaty 4024
        gProgram_state.AI_vehicles.path_sections[gMobile_section].width =
4025
            gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].width;
1 pmbaty 4026
    }
4027
    gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections = 0;
4028
    gProgram_state.AI_vehicles.path_nodes[new_node].sections[gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections] = gMobile_section;
4029
    gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections += 1;
4030
    gProgram_state.AI_vehicles.path_nodes[old_node].sections[gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections] = gMobile_section;
4031
    gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections += 1;
4032
    ShowOppoPaths();
4033
    sprintf(str, "New section #%d, new node #%d", gMobile_section, new_node);
21 pmbaty 4034
    NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4035
}
4036
 
4037
// IDA: void __cdecl InsertAndElasticate()
4038
void InsertAndElasticate(void) {
4039
    tS16 inserted_node;
4040
    tS16 elasticatey_node;
4041
    tS16 section_no;
4042
    tS16 new_section;
4043
    br_vector3 direction_v;
4044
    br_vector3 intersect;
4045
    br_vector3 wank;
4046
    br_scalar distance;
4047
    int not_perp;
4048
    int one_wayness;
4049
    char str[256];
4050
    tPath_section_type_enum section_type;
4051
    LOG_TRACE("()");
4052
 
4053
    not_perp = 0;
4054
    if (NewNodeOKHere()) {
4055
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4056
        BrVector3Sub(&wank,
4057
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p,
4058
            &intersect);
4059
        if (BrVector3Length(&wank) == 0.f) {
4060
            not_perp = 1;
4061
        }
4062
        BrVector3Sub(&wank,
4063
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p,
4064
            &intersect);
4065
        if (BrVector3Length(&wank) == 0.f) {
4066
            not_perp = 1;
4067
        }
4068
        if (not_perp || distance > 10.f) {
21 pmbaty 4069
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Get nearer to the section");
1 pmbaty 4070
        } else {
4071
            new_section = ReallocExtraPathSections(1);
4072
            if (gAlready_elasticating) {
4073
                inserted_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
4074
                section_type = gProgram_state.AI_vehicles.path_sections[gMobile_section].type;
4075
                one_wayness = gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way;
4076
                elasticatey_node = ReallocExtraPathNodes(1);
4077
                gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections = 0;
4078
                gProgram_state.AI_vehicles.path_sections[new_section].width = gProgram_state.AI_vehicles.path_sections[gMobile_section].width;
4079
            } else {
4080
                inserted_node = ReallocExtraPathNodes(2);
4081
                gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections = 0;
4082
                elasticatey_node = inserted_node + 1;
4083
                gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections = 0;
4084
                gProgram_state.AI_vehicles.path_sections[new_section].width = gProgram_state.AI_vehicles.path_sections[section_no].width;
4085
                section_type = gProgram_state.AI_vehicles.path_sections[section_no].type;
4086
                one_wayness = gProgram_state.AI_vehicles.path_sections[section_no].one_way;
4087
            }
4088
            InsertThisNodeInThisSectionHere(inserted_node, section_no, &gSelf->t.t.translate.t);
4089
            gMobile_section = new_section;
4090
            gProgram_state.AI_vehicles.path_sections[new_section].node_indices[0] = inserted_node;
4091
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = elasticatey_node;
4092
            gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0] = 0;
4093
            gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1] = 0;
4094
            gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0] = 255;
4095
            gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1] = 255;
4096
            gProgram_state.AI_vehicles.path_sections[gMobile_section].type = section_type;
4097
            gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way = one_wayness;
4098
            gProgram_state.AI_vehicles.path_nodes[inserted_node].sections[gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections] = gMobile_section;
4099
            gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections += 1;
4100
            gProgram_state.AI_vehicles.path_nodes[elasticatey_node].sections[gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections] = gMobile_section;
4101
            gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections += 1;
4102
            gAlready_elasticating = 1;
4103
            ShowOppoPaths();
4104
            sprintf(str, "New section %d, new node #%d inserted into section #%d",
4105
                gMobile_section, inserted_node, section_no);
21 pmbaty 4106
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4107
        }
4108
    }
4109
}
4110
 
4111
// IDA: void __cdecl InsertAndDontElasticate()
4112
void InsertAndDontElasticate(void) {
4113
    tS16 inserted_node;
4114
    tS16 section_no;
4115
    br_vector3 direction_v;
4116
    br_vector3 intersect;
4117
    br_vector3 wank;
4118
    br_scalar distance;
4119
    int not_perp;
4120
    char str[256];
4121
    LOG_TRACE("()");
4122
 
4123
    not_perp = 0;
4124
    if (NewNodeOKHere()) {
4125
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4126
        BrVector3Sub(&wank, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p, &intersect);
4127
        if (BrVector3Length(&wank) == 0.f) {
4128
            not_perp = 1;
4129
        }
4130
        BrVector3Sub(&wank, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p, &intersect);
4131
        if (BrVector3Length(&wank) == 0.f) {
4132
            not_perp = 1;
4133
        }
4134
        if (not_perp || distance > 10.f) {
21 pmbaty 4135
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Get nearer to the section");
1 pmbaty 4136
        } else {
4137
            if (gAlready_elasticating) {
4138
                gAlready_elasticating = 0;
4139
                inserted_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
4140
            } else {
4141
                inserted_node = ReallocExtraPathNodes(1);
4142
                gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections = 0;
4143
            }
4144
            InsertThisNodeInThisSectionHere(inserted_node, section_no, &gSelf->t.t.translate.t);
4145
            ShowOppoPaths();
4146
            sprintf(str, "New node #%d inserted into section #%d", inserted_node, section_no);
21 pmbaty 4147
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4148
        }
4149
    }
4150
}
4151
 
4152
// IDA: void __cdecl DropDeadEndNode()
4153
void DropDeadEndNode(void) {
4154
    char str[256];
4155
    LOG_TRACE("()");
4156
 
4157
    if (NewNodeOKHere() && gAlready_elasticating) {
4158
        gAlready_elasticating = 0;
4159
        BrVector3Copy(
4160
            &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p,
4161
            &gSelf->t.t.translate.t);
4162
        ShowOppoPaths();
4163
        sprintf(str, "New section #%d, finish node #%d",
4164
            gMobile_section,
4165
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
21 pmbaty 4166
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 4000, -1, str);
1 pmbaty 4167
    }
4168
}
4169
 
4170
// IDA: void __cdecl DropNodeOnNodeAndStopElasticating()
4171
void DropNodeOnNodeAndStopElasticating(void) {
4172
    int node_no;
4173
    char str[256];
4174
    br_scalar distance;
4175
    LOG_TRACE("()");
4176
 
4177
    if (gAlready_elasticating) {
4178
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4179
        if (gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0] == node_no || distance > 10.f) {
21 pmbaty 4180
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
1 pmbaty 4181
        } else if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections >= COUNT_OF(gProgram_state.AI_vehicles.path_nodes[node_no].sections)) {
4182
            sprintf(str, "Sorry, node #%d already has %d sections attached", node_no, (int)COUNT_OF(gProgram_state.AI_vehicles.path_nodes[node_no].sections));
21 pmbaty 4183
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4184
        } else {
4185
            gAlready_elasticating = 0;
4186
            gProgram_state.AI_vehicles.number_of_path_nodes -= 1;
4187
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = node_no;
4188
            gProgram_state.AI_vehicles.path_nodes[node_no].sections[gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections] = gMobile_section;
4189
            gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections += 1;
4190
            ShowOppoPaths();
4191
            sprintf(str, "New section #%d, attached to existing node #%d",
4192
                gMobile_section,
4193
                gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
21 pmbaty 4194
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 4000, -1, str);
1 pmbaty 4195
        }
4196
    }
4197
}
4198
 
4199
// IDA: void __cdecl WidenOppoPathSection()
4200
void WidenOppoPathSection(void) {
4201
    LOG_TRACE("()");
4202
 
20 pmbaty 4203
 
4204
   if (gOppo_paths_shown) {
4205
       RecalcNearestPathSectionWidth(.05f);
4206
   }
1 pmbaty 4207
}
4208
 
4209
// IDA: void __cdecl NarrowOppoPathSection()
4210
void NarrowOppoPathSection(void) {
4211
    LOG_TRACE("()");
4212
 
4213
    if (gOppo_paths_shown) {
4214
        RecalcNearestPathSectionWidth(-.05f);
4215
    }
4216
}
4217
 
4218
// IDA: void __cdecl IncreaseSectionMinSpeed()
4219
void IncreaseSectionMinSpeed(void) {
4220
    LOG_TRACE("()");
4221
 
4222
    if (gOppo_paths_shown) {
4223
        RecalcNearestPathSectionSpeed(0, 1);
4224
    }
4225
}
4226
 
4227
// IDA: void __cdecl DecreaseSectionMinSpeed()
4228
void DecreaseSectionMinSpeed(void) {
4229
    LOG_TRACE("()");
4230
 
4231
    if (gOppo_paths_shown) {
4232
        RecalcNearestPathSectionSpeed(0, -1);
4233
    }
4234
}
4235
 
4236
// IDA: void __cdecl IncreaseSectionMaxSpeed()
4237
void IncreaseSectionMaxSpeed(void) {
4238
    LOG_TRACE("()");
4239
 
20 pmbaty 4240
   if (gOppo_paths_shown) {
4241
       RecalcNearestPathSectionSpeed(1, 1);
4242
   }
1 pmbaty 4243
}
4244
 
4245
// IDA: void __cdecl DecreaseSectionMaxSpeed()
4246
void DecreaseSectionMaxSpeed(void) {
4247
    LOG_TRACE("()");
4248
 
4249
    if (gOppo_paths_shown) {
4250
        RecalcNearestPathSectionSpeed(1, -1);
4251
    }
4252
}
4253
 
4254
// IDA: void __cdecl PullOppoPoint()
4255
void PullOppoPoint(void) {
4256
    tS16 node_no;
4257
    br_scalar distance;
4258
    LOG_TRACE("()");
4259
 
4260
    if (gOppo_paths_shown) {
4261
        if (gAlready_elasticating) {
21 pmbaty 4262
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're elasticating");
1 pmbaty 4263
        } else {
4264
            node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4265
            if (distance > 10.f) {
21 pmbaty 4266
                NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any paths close enough");
1 pmbaty 4267
            } else {
4268
                BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[node_no].p, &gSelf->t.t.translate.t);
4269
                ShowOppoPaths();
21 pmbaty 4270
                NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Bing!");
1 pmbaty 4271
            }
4272
        }
4273
    }
4274
}
4275
 
4276
// IDA: void __cdecl ShowNodeInfo()
4277
void ShowNodeInfo(void) {
4278
    tS16 node_no;
4279
    char str[256];
4280
    br_scalar distance;
4281
    LOG_TRACE("()");
4282
 
4283
    if (!gOppo_paths_shown) {
21 pmbaty 4284
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4285
    } else if (gAlready_elasticating) {
4286
        sprintf(str, "Next point will be #%d",
4287
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
21 pmbaty 4288
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4289
    } else {
4290
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4291
        if (distance > 10.f) {
21 pmbaty 4292
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
1 pmbaty 4293
        } else {
4294
            sprintf(str, "Nearest node #%d has %d attached sections",
4295
                node_no,
4296
                gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections);
21 pmbaty 4297
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4298
        }
4299
    }
4300
}
4301
 
4302
// IDA: void __cdecl ShowSectionInfo1()
4303
void ShowSectionInfo1(void) {
4304
    tS16 section_no;
4305
    char str[256];
4306
    br_scalar distance;
4307
    br_vector3 direction_v;
4308
    br_vector3 intersect;
4309
    LOG_TRACE("()");
4310
 
4311
    if (!gOppo_paths_shown) {
21 pmbaty 4312
      NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4313
    } else if (gAlready_elasticating) {
4314
        sprintf(str, "This section will be #%d attached to nodes #%d and #%d",
4315
            gMobile_section,
4316
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0],
4317
            gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
21 pmbaty 4318
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4319
    } else {
4320
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4321
        if (distance > 10.f) {
21 pmbaty 4322
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
1 pmbaty 4323
        } else {
4324
            sprintf(str, "Nearest section #%d, start node #%d, finish node #%d",
4325
                section_no,
4326
                gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0],
4327
                gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
21 pmbaty 4328
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4329
        }
4330
    }
4331
}
4332
 
4333
// IDA: void __cdecl ShowSectionInfo2()
4334
void ShowSectionInfo2(void) {
4335
    tS16 section_no;
4336
    char str[256];
4337
    br_scalar distance;
4338
    br_vector3 direction_v;
4339
    br_vector3 intersect;
4340
    LOG_TRACE("()");
4341
 
4342
    if (!gOppo_paths_shown) {
21 pmbaty 4343
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4344
    } else if (gAlready_elasticating) {
4345
        sprintf(str, "Towards start - min %d max %d, finish - min %d, max %d mph",
4346
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0]),
4347
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0]),
4348
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1]),
4349
            (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1]));
21 pmbaty 4350
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4351
    } else {
4352
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4353
        if (distance > 10.f) {
21 pmbaty 4354
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
1 pmbaty 4355
        } else {
4356
            sprintf(str, "Towards start - min %d max %d, finish - min %d, max %d mph",
4357
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0]),
4358
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0]),
4359
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1]),
4360
                (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1]));
21 pmbaty 4361
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4362
        }
4363
    }
4364
}
4365
 
4366
// IDA: void __cdecl DeleteOppoPathSection()
4367
void DeleteOppoPathSection(void) {
4368
    br_scalar distance;
4369
    br_vector3 intersect;
4370
    br_vector3 direction_v;
4371
    tS16 section_no;
4372
    LOG_TRACE("()");
4373
 
4374
    if (gOppo_paths_shown == 0) {
21 pmbaty 4375
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4376
    } else if (gAlready_elasticating) {
21 pmbaty 4377
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4378
    } else {
4379
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4380
        if (distance > 10.f) {
21 pmbaty 4381
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
1 pmbaty 4382
        } else {
4383
            DeleteSection(section_no);
4384
            DeleteOrphanNodes();
4385
            ShowOppoPaths();
21 pmbaty 4386
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Pop!");
1 pmbaty 4387
        }
4388
    }
4389
}
4390
 
4391
// IDA: void __cdecl DeleteOppoPathNodeAndSections()
4392
void DeleteOppoPathNodeAndSections(void) {
4393
    br_scalar distance;
4394
    tS16 node_no;
4395
    LOG_TRACE("()");
4396
 
4397
    if (!gOppo_paths_shown) {
21 pmbaty 4398
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
20 pmbaty 4399
    }
4400
    else if (gAlready_elasticating) {
21 pmbaty 4401
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4402
    } else {
4403
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4404
        if (distance > 10.f) {
21 pmbaty 4405
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
1 pmbaty 4406
        } else {
4407
            DeleteNode(node_no, 1);
4408
            DeleteOrphanNodes();
4409
            ShowOppoPaths();
21 pmbaty 4410
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Blam!");
1 pmbaty 4411
        }
4412
    }
4413
}
4414
 
4415
// IDA: void __cdecl DeleteOppoPathNodeAndJoin()
4416
void DeleteOppoPathNodeAndJoin(void) {
4417
    br_scalar distance;
4418
    tS16 node_no;
4419
    LOG_TRACE("()");
4420
 
4421
    if (!gOppo_paths_shown) {
21 pmbaty 4422
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4423
    } else if (gAlready_elasticating) {
21 pmbaty 4424
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4425
    } else {
4426
        node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
4427
        if (distance > 10.f) {
21 pmbaty 4428
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
1 pmbaty 4429
        } else if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections != 2) {
21 pmbaty 4430
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Node must have exactly 2 sections attached");
1 pmbaty 4431
        } 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 4432
                    && gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[1]].node_indices[1] == node_no) ||
4433
                (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[1]].node_indices[0] == node_no
4434
                    && gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[0]].node_indices[1] == node_no)) {
1 pmbaty 4435
            ConsistencyCheck();
20 pmbaty 4436
            DeleteNode(node_no,0);
1 pmbaty 4437
            ConsistencyCheck();
4438
            DeleteOrphanNodes();
4439
            ConsistencyCheck();
4440
            ShowOppoPaths();
21 pmbaty 4441
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Blam!");
20 pmbaty 4442
        }
4443
        else {
21 pmbaty 4444
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Sections must point in same direction");
1 pmbaty 4445
        }
4446
    }
4447
}
4448
 
4449
// IDA: void __cdecl ReverseSectionDirection()
4450
void ReverseSectionDirection(void) {
4451
    tS16 temp;
4452
    tU8 speed_temp;
4453
    br_scalar distance;
4454
    br_vector3 intersect;
4455
    br_vector3 direction_v;
4456
    tS16 section_no;
4457
    LOG_TRACE("()");
4458
 
4459
    if (!gOppo_paths_shown) {
21 pmbaty 4460
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4461
    } else if (gAlready_elasticating) {
21 pmbaty 4462
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4463
    } else {
20 pmbaty 4464
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t,  &direction_v, &intersect, &distance);
1 pmbaty 4465
        if (distance > 10.f) {
21 pmbaty 4466
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
1 pmbaty 4467
        } else {
4468
            temp = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0];
20 pmbaty 4469
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] =
4470
                gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1];
1 pmbaty 4471
            gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] = temp;
4472
 
4473
            speed_temp = gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0];
20 pmbaty 4474
            gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0] =
4475
                gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1];
1 pmbaty 4476
            gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1] = speed_temp;
4477
 
4478
            speed_temp = gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0];
20 pmbaty 4479
            gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0] =
4480
                gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1];
1 pmbaty 4481
            gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1] = speed_temp;
4482
 
4483
            ShowOppoPaths();
4484
        }
4485
    }
4486
}
4487
 
4488
// IDA: void __cdecl CycleSectionType()
4489
void CycleSectionType(void) {
4490
    br_scalar distance;
4491
    br_vector3 intersect;
4492
    br_vector3 direction_v;
4493
    tS16 section_no;
4494
    char str[256];
4495
    LOG_TRACE("()");
4496
 
4497
    if (!gOppo_paths_shown) {
21 pmbaty 4498
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4499
    } else if (gAlready_elasticating) {
21 pmbaty 4500
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4501
    } else {
4502
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4503
        if (distance > 10.f) {
21 pmbaty 4504
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
1 pmbaty 4505
        } else {
20 pmbaty 4506
            gProgram_state.AI_vehicles.path_sections[section_no].type =
4507
                (gProgram_state.AI_vehicles.path_sections[section_no].type + 1) % 3;
4508
            sprintf(str, "%s section",  gPath_section_type_names[gProgram_state.AI_vehicles.path_sections[section_no].type]);
1 pmbaty 4509
            ShowOppoPaths();
21 pmbaty 4510
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4511
        }
4512
    }
4513
}
4514
 
4515
// IDA: void __cdecl ToggleOneWayNess()
4516
void ToggleOneWayNess(void) {
4517
    br_scalar distance;
4518
    br_vector3 intersect;
4519
    br_vector3 direction_v;
4520
    tS16 section_no;
4521
    LOG_TRACE("()");
4522
 
4523
    if (!gOppo_paths_shown) {
21 pmbaty 4524
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4525
    } else if (gAlready_elasticating) {
21 pmbaty 4526
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4527
    } else {
4528
        section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
4529
        if (distance > 10.f) {
21 pmbaty 4530
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
1 pmbaty 4531
        } else {
4532
            if (gProgram_state.AI_vehicles.path_sections[section_no].one_way) {
4533
                gProgram_state.AI_vehicles.path_sections[section_no].one_way = 0;
20 pmbaty 4534
            }
4535
            else {
1 pmbaty 4536
                gProgram_state.AI_vehicles.path_sections[section_no].one_way = 1;
4537
            }
4538
            ShowOppoPaths();
4539
            if (gProgram_state.AI_vehicles.path_sections[section_no].one_way) {
21 pmbaty 4540
                NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "ONE-WAY");
1 pmbaty 4541
            } else {
21 pmbaty 4542
                NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "TWO-WAY");
1 pmbaty 4543
            }
4544
        }
4545
    }
4546
}
4547
 
4548
// IDA: void __cdecl CopStartPointInfo()
4549
void CopStartPointInfo(void) {
4550
    char str[256];
4551
    int i;
4552
    int closest;
4553
    br_scalar closest_distance;
4554
    br_scalar distance;
4555
    br_vector3 car_to_point;
4556
    LOG_TRACE("()");
4557
 
4558
    closest = -1;
4559
    closest_distance = FLT_MAX;
4560
    if (!gOppo_paths_shown) {
21 pmbaty 4561
      NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4562
    } else {
4563
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
4564
            BrVector3Sub(&car_to_point, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
4565
            distance = BrVector3LengthSquared(&car_to_point);
4566
            if (distance < closest_distance) {
4567
                closest = i;
4568
                closest_distance = distance;
4569
            }
4570
        }
4571
        if (closest < 0 || closest_distance > 10.f) {
21 pmbaty 4572
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "No cop start points close enough");
1 pmbaty 4573
        } else {
4574
            sprintf(str, "Nearest cop start point #%d", closest);
21 pmbaty 4575
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4576
        }
4577
    }
4578
}
4579
 
4580
// IDA: void __cdecl DropCopStartPoint()
4581
void DropCopStartPoint(void) {
4582
    char str[256];
4583
    LOG_TRACE("()");
4584
 
4585
    if (!gOppo_paths_shown) {
21 pmbaty 4586
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4587
    } else if (gAlready_elasticating) {
21 pmbaty 4588
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4589
    } else {
4590
        if (gProgram_state.AI_vehicles.number_of_cops < COUNT_OF(gProgram_state.AI_vehicles.cop_start_points)) {
4591
            BrVector3Copy(&gProgram_state.AI_vehicles.cop_start_points[gProgram_state.AI_vehicles.number_of_cops], &gSelf->t.t.translate.t);
4592
            gProgram_state.AI_vehicles.number_of_cops += 1;
4593
            ShowOppoPaths();
4594
            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));
21 pmbaty 4595
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4596
        } else {
4597
            sprintf(str, "Sorry, no more than %d cop start points", (int)COUNT_OF(gProgram_state.AI_vehicles.cop_start_points));
21 pmbaty 4598
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4599
        }
4600
    }
4601
}
4602
 
4603
// IDA: void __cdecl DeleteCopStartPoint()
4604
void DeleteCopStartPoint(void) {
4605
    char str[256];
4606
    int i;
4607
    int closest;
4608
    br_scalar closest_distance;
4609
    br_scalar distance;
4610
    br_vector3 car_to_point;
4611
    LOG_TRACE("()");
4612
 
4613
    closest = -1;
4614
    closest_distance = FLT_MAX;
4615
    if (!gOppo_paths_shown) {
21 pmbaty 4616
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
1 pmbaty 4617
    } else if (gAlready_elasticating) {
21 pmbaty 4618
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
1 pmbaty 4619
    } else {
4620
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
4621
            BrVector3Sub(&car_to_point, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
4622
            distance = BrVector3Length(&car_to_point);
4623
            if (distance < closest_distance) {
4624
                closest = i;
4625
                closest_distance = distance;
4626
            }
4627
        }
4628
        if (closest < 0 || closest_distance > 10.f) {
21 pmbaty 4629
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "No cop start points close enough");
1 pmbaty 4630
        } else {
4631
            for (i = closest; i < gProgram_state.AI_vehicles.number_of_cops - 1; i++) {
4632
                BrVector3Copy(&gProgram_state.AI_vehicles.cop_start_points[i],
4633
                    &gProgram_state.AI_vehicles.cop_start_points[i + 1]);
4634
            }
4635
            gProgram_state.AI_vehicles.number_of_cops -= 1;
4636
            ShowOppoPaths();
4637
            sprintf(str, "Deleted cop start point #%d", closest);
21 pmbaty 4638
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
1 pmbaty 4639
        }
4640
    }
4641
}
4642
 
4643
// IDA: void __cdecl CycleCopStartPointType()
4644
void CycleCopStartPointType(void) {
4645
    LOG_TRACE("()");
20 pmbaty 4646
 
1 pmbaty 4647
}