#include "driver2.h" #include "felony.h" #include "cop_ai.h" #include "civ_ai.h" #include "players.h" #include "cars.h" #include "mission.h" #include "convert.h" #include "gamesnd.h" #include "dr2roads.h" #include "overlay.h" #include "cutrecorder.h" short initialOccurrenceDelay[12] = { 24, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0 }; short initialReccurrenceDelay[12] = { 128, 0, 128, 64, 64, 32, 32, 0, 128, 256 }; int CopsCanSeePlayer = 0; short pedestrianFelony = 0; FELONY_VALUE initialFelonyValue[12] = { { 659, 0 }, { 659, 75 }, { 659, 0 }, { 659, 0 }, { 659, 659 }, { 1318, 659 }, { 659, 659 }, { 659, 25 }, { 659, 0 }, { 659, 0 }, { 659, 0 }, { 4096, 0 } }; FELONY_DATA felonyData; int FelonyIncreaseTime = 0; int FelonyDecreaseTime = 0; int FelonyIncreaseTimer = 0; int FelonyDecreaseTimer = 0; // [D] [T] void InitFelonyDelayArray(FELONY_DELAY *pFelonyDelay, short *pMaximum, int count) { FELONY_DELAY *pCurrent; pCurrent = pFelonyDelay + count; while (pFelonyDelay < pCurrent) { pFelonyDelay->current = 0; pFelonyDelay->maximum = *pMaximum++; pFelonyDelay++; } } // [D] [T] void InitFelonyData(FELONY_DATA *pFelonyData) { InitFelonyDelayArray(pFelonyData->occurrenceDelay, initialOccurrenceDelay, numberOf(initialOccurrenceDelay)); InitFelonyDelayArray(pFelonyData->reoccurrenceDelay, initialReccurrenceDelay, numberOf(initialOccurrenceDelay)); memcpy((u_char*)&pFelonyData->value, (u_char*)&initialFelonyValue, sizeof(initialFelonyValue)); } // [D] [T] int GetCarHeading(int direction) { return (direction + 0x200U & 0xfff) >> 10; } // [D] [T] char GetCarDirectionOfTravel(CAR_DATA *cp) { int direction; direction = GetCarHeading(cp->hd.direction);; if (cp->hd.wheel_speed < 0) direction = direction + 2 & 3; return direction; } // [D] [T] void NoteFelony(FELONY_DATA *pFelonyData, char type, short scale) { int felonyTooLowForRoadblocks; char rnd; // $s1 int dir; // $a3 short *felony; int phrase; int additionalFelonyPoints; if (player[0].playerCarId < 0) felony = &pedestrianFelony; else felony = &car_data[player[0].playerCarId].felonyRating; felonyTooLowForRoadblocks = *felony; if (pFelonyData->occurrenceDelay[type].current < pFelonyData->occurrenceDelay[type].maximum) return; #ifdef FELONY_DEBUG switch (type) { case 1: SetPlayerMessage(0, "Scaring pedestrians", 0, 1); break; case 2: SetPlayerMessage(0, "Speeding", 0, 1); break; case 3: SetPlayerMessage(0, "Red light crossing", 0, 1); break; case 4: SetPlayerMessage(0, "Collision with another vehicle", 0, 1); break; case 5: SetPlayerMessage(0, "Collision with cop", 0, 1); break; case 6: SetPlayerMessage(0, "Collision with building", 0, 1); break; case 7: SetPlayerMessage(0, "Damaging propery", 0, 1); break; case 8: SetPlayerMessage(0, "Wrong way", 0, 1); break; case 9: SetPlayerMessage(0, "Driving without lights", 0, 1); break; case 10: SetPlayerMessage(0, "Reckless driving", 0, 1); break; case 11: SetPlayerMessage(0, "Stealing cop car", 0, 1); break; } #endif if (CopsCanSeePlayer == 0 && (type != 11)) return; if (pFelonyData->reoccurrenceDelay[type].current != 0) return; pFelonyData->reoccurrenceDelay[type].current = pFelonyData->reoccurrenceDelay[type].maximum; if (*felony <= FELONY_PURSUIT_MIN_VALUE) additionalFelonyPoints = pFelonyData->value[type].placid; else additionalFelonyPoints = pFelonyData->value[type].angry * pFelonyData->pursuitFelonyScale >> 0xc; *felony += (additionalFelonyPoints * scale >> 12); if (*felony > FELONY_MAX_VALUE) *felony = FELONY_MAX_VALUE; // KILL PEDESTRIAN FELONY HERE if (player[0].playerType == 2) *felony = 0; if (first_offence == 0 && numActiveCops) { // say something.. rnd = Random2(1); dir = GetCarDirectionOfTravel(&car_data[player[0].playerCarId]); switch (type) { case 1: case 5: case 6: case 7: case 11: break; case 3: if ((rnd & 3) != 0) break; CopSay(5, 0); break; case 4: if ((rnd % 3) & 0xff != 0) break; CopSay((rnd & 1) + 7, 0); break; default: if ((rnd % 17) & 0xFF == 0) { if (MaxPlayerDamage[0] * 3 / 4 < car_data[player[0].playerCarId].totalDamage) phrase = rnd % 4; else phrase = rnd % 3; if (last_cop_phrase != phrase && 0 < TimeSinceLastSpeech) { if (phrase < 3) CopSay(phrase + 15, dir); else CopSay(6, 0); last_cop_phrase = phrase; } } break; } } if (felonyTooLowForRoadblocks <= FELONY_ROADBLOCK_MIN_VALUE) { if (*felony > FELONY_ROADBLOCK_MIN_VALUE) { roadblockCount = roadblockDelayDiff[gCopDifficultyLevel] + (Random2(0) & 0xff); } } } // [D] [T] void AdjustFelony(FELONY_DATA *pFelonyData) { FELONY_DELAY *pFelonyDelay; short *felony; if (player[0].playerCarId < 0) felony = &pedestrianFelony; else felony = &car_data[player[0].playerCarId].felonyRating; if (*felony != 0 && *felony <= FELONY_PURSUIT_MIN_VALUE) { if (FelonyDecreaseTimer++ == FelonyDecreaseTime) { (*felony)--; FelonyDecreaseTimer = 0; } } else if (CopsCanSeePlayer) { if (*felony > FELONY_PURSUIT_MIN_VALUE && FelonyIncreaseTimer++ == FelonyIncreaseTime) { (*felony)++; if (*felony > FELONY_MAX_VALUE) *felony = FELONY_MAX_VALUE; FelonyIncreaseTimer = 0; } } pFelonyDelay = pFelonyData->reoccurrenceDelay; while (pFelonyDelay <= &pFelonyData->reoccurrenceDelay[11]) { if (pFelonyDelay->current != 0) pFelonyDelay->current--; pFelonyDelay++; } } short playerLastRoad = 0; // [D] [T] void CheckPlayerMiscFelonies(void) { int i; bool goingWrongWay; int surfInd; int maxSpeed; int limit; int exitId; int _exitId; VECTOR *carPos; DRIVER2_ROAD_INFO roadInfo; DRIVER2_JUNCTION *jn; CAR_DATA* cp; // Do not register felony if player does not have a car if (player[0].playerType == 2 || player[0].playerCarId < 0 || FelonyBar.active == 0) return; cp = &car_data[player[0].playerCarId]; carPos = (VECTOR *)cp->hd.where.t; surfInd = GetSurfaceIndex(carPos); // check junctions if (IS_JUNCTION_SURFACE(surfInd)) { jn = GET_JUNCTION(surfInd); if( (IS_CURVED_SURFACE(playerLastRoad) || IS_STRAIGHT_SURFACE(playerLastRoad)) && (jn->flags & 0x1)) { exitId = 0; i = 0; while (i < 4) { if (jn->ExitIdx[i] == playerLastRoad) { exitId = i; break; } i++; } // Run a red light! if (junctionLightsPhase[exitId & 1] == 1) NoteFelony(&felonyData, 3, 4096); } } playerLastRoad = surfInd; goingWrongWay = false; if(GetSurfaceRoadInfo(&roadInfo, surfInd)) { int lane; int crd; lane = GetLaneByPositionOnRoad(&roadInfo, carPos); if(roadInfo.straight) crd = (roadInfo.straight->angle - cp->hd.direction) + 1024U >> 0xb & 1; else crd = NotTravellingAlongCurve(carPos->vx, carPos->vz, cp->hd.direction, roadInfo.curve); // check if on correct lane if (ROAD_IS_AI_LANE(&roadInfo, lane)) { if (ROAD_LANE_DIR(&roadInfo, lane) == 0) { if (crd == 1) goingWrongWay = true; } else { if (crd == 0) goingWrongWay = true; } } #if 0 printInfo("ROAD %d lane: %d / %d, (%d). AI drive: %d, flg: %d%d%d, dir: %d, spd: %d (wrong way: %d)\n", roadInfo.surfId, lane + 1, ROAD_WIDTH_IN_LANES(&roadInfo), // lane count. * 2 for both sides as roads are symmetric IS_NARROW_ROAD(&roadInfo), ROAD_IS_AI_LANE(&roadInfo, lane), // lane AI driveable flag (roadInfo.NumLanes & 0x20) > 0, // flag 0 - first lane? (roadInfo.NumLanes & 0x40) > 0, // flag 1 - leftmost park (roadInfo.NumLanes & 0x80) > 0, // flag 2 - rightmost park ROAD_LANE_DIRECTION_BIT(&roadInfo, lane), // direction bit ROAD_SPEED_LIMIT(&roadInfo), // speed limit id goingWrongWay); #endif // get road speed limit maxSpeed = speedLimits[ROAD_SPEED_LIMIT(&roadInfo)]; } else { maxSpeed = speedLimits[2]; } // wrong way if (goingWrongWay) felonyData.occurrenceDelay[8].current++; else felonyData.occurrenceDelay[8].current = 0; NoteFelony(&felonyData, 8, 4096); // if lights are off (broken) if (gTimeOfDay == 3 && cp->ap.damage[0] > 1000 && cp->ap.damage[1] > 1000) NoteFelony(&felonyData, 9, 4096); // reckless driving. // for that checking if rear wheels are sliding if (cp->wheelspin == 0) { if (ABS(cp->hd.rear_vel) > 11100) felonyData.occurrenceDelay[10].current++; else felonyData.occurrenceDelay[10].current = 0; } else { felonyData.occurrenceDelay[10].current++; } NoteFelony(&felonyData, 10, 4096); // check the speed limit if (speedLimits[2] == maxSpeed) limit = (maxSpeed * 19) >> 4; else limit = (maxSpeed * 3) >> 1; if (FIXEDH(cp->hd.wheel_speed) > limit) NoteFelony(&felonyData, 2, 4096); } // [D] [T] void InitFelonySystem(void) { FelonyIncreaseTime = 31; FelonyDecreaseTime = 31; FelonyIncreaseTimer = 0; FelonyDecreaseTimer = 0; InitFelonyData(&felonyData); } // enable when debugging whacky StoreCarPosition // #define LEADAI_EASY // [D] [T] void CarHitByPlayer(CAR_DATA *victim, int howHard) { char type; _CutRec_CheckInvalidatePing(victim->id, howHard); #ifdef LEADAI_EASY if (victim->controlType == CONTROL_TYPE_LEAD_AI) victim->totalDamage = 0xffff; #endif if (howHard > 0 && victim->controlType != CONTROL_TYPE_PURSUER_AI) { if ((victim->controlFlags & 1) == 0) { if (howHard < 32) { NoteFelony(&felonyData, 4, (howHard << 0x17) >> 0x10); return; } type = 4; } else { if (howHard < 16) { NoteFelony(&felonyData, 5, (howHard << 0x18) >> 0x10); return; } type = 5; } NoteFelony(&felonyData, type, 4096); } }